Skip to content

Commit

Permalink
Merge branch 'release/v2.0.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
Morasiu committed Nov 17, 2024
2 parents 02f05c4 + 94d8daa commit 0b75bd2
Show file tree
Hide file tree
Showing 34 changed files with 523 additions and 191 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [2.0.0] - 2024-11-17
### Changed
- Upgrade to .Net 8.0
- Upgrade to Swashbuckle 7.0.0
- Upgrade to Microsoft.AspNetCore.Mvc.NewtonsoftJson 7.0.0
- Fixed for breaking changes in Swashbuckle 7.0.0 (Issue #24)


## [1.10.1] - 2024-11-15
### Fixed
- Issue #22 - NullReferenceException when binding empty JSON body. Thanks @cloviscoli!
Expand Down
145 changes: 75 additions & 70 deletions src/Demo/Controllers/ProductController.cs
Original file line number Diff line number Diff line change
@@ -1,82 +1,87 @@
using System;
using System.Linq;
using Demo.Models.Products;
using Demo.Models.Products;
using Demo.Models.Wrapper;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using Swashbuckle.AspNetCore.JsonMultipartFormDataSupport.Models;

namespace Demo.Controllers {
[Produces("application/json")]
[ApiController]
[Route("[controller]")]
public class ProductController : ControllerBase {
[HttpPost]
public IActionResult Post([FromForm] MultipartFormData<Product> data) {
var json = data.Json ?? throw new NullReferenceException(nameof(data));
var image = data.File;
return Ok(new { json, image?.FileName });
}
namespace Demo.Controllers;

[HttpPost("required")]
public IActionResult Post([FromForm] MultipartRequiredFormData<Product> data) {
var json = data.Json ?? throw new NullReferenceException(nameof(data));
var image = data.File;
return Ok(new { json, image?.FileName });
}
[Produces("application/json")]
[ApiController]
[Route("[controller]")]
public class ProductController : ControllerBase {
[HttpPost]
public IActionResult Post([FromForm] MultipartFormData<Product> data) {
var json = data.Json ?? throw new NullReferenceException(nameof(data));
var image = data.File;
return Ok(new { json, image?.FileName });
}

[HttpPost("wrapper/required")]
public IActionResult Post([FromForm] RequiredProductWrapper wrapper) {
var wrapperProduct = wrapper.Product ?? throw new NullReferenceException(nameof(wrapper.Product));
var images = wrapper.Files;
return Ok(new { wrapperProduct, images = images.Select(a => a.FileName) });
}
[HttpPost("required")]
public IActionResult Post([FromForm] MultipartRequiredFormData<Product> data) {
var json = data.Json ?? throw new NullReferenceException(nameof(data));
var image = data.File;
return Ok(new { json, image?.FileName });
}

[HttpPost("required/nested")]
public IActionResult Post([FromForm] MultipartRequiredFormData<NestedProduct> data) {
var json = data.Json ?? throw new NullReferenceException(nameof(data));
var image = data.File;
return Ok(new { json, image?.FileName });
}

[HttpPost("wrapper")]
public IActionResult PostWrapper([FromForm] ProductWrapper wrapper) {
var wrapperProduct = wrapper.Product ?? throw new NullReferenceException(nameof(wrapper.Product));
var images = wrapper.Files;
return Ok(new { wrapperProduct, images = images.Select(a => a.FileName) });
}
[HttpPost("wrapper/required")]
public IActionResult Post([FromForm] RequiredProductWrapper wrapper) {
var wrapperProduct = wrapper.Product ?? throw new NullReferenceException(nameof(wrapper.Product));
var images = wrapper.Files;
return Ok(new { wrapperProduct, images = images.Select(a => a.FileName) });
}

[HttpPost("wrapper/simple")]
public IActionResult PostWrapper([FromForm] SimpleProductWrapper wrapper) {
var productName = wrapper.ProductName;
var productId = wrapper.ProductId ?? throw new NullReferenceException(nameof(wrapper.ProductId));
var images = wrapper.Files;
return Ok(new { productName, productId, images = images.Select(a => a.FileName) });
}
[HttpPost("wrapper")]
public IActionResult PostWrapper([FromForm] ProductWrapper wrapper) {
var wrapperProduct = wrapper.Product ?? throw new NullReferenceException(nameof(wrapper.Product));
var images = wrapper.Files;
return Ok(new { wrapperProduct, images = images.Select(a => a.FileName) });
}

[HttpPost("wrapper/complex")]
public IActionResult PostWrapper([FromForm] ComplexProductWrapper wrapper) {
var productName = wrapper.ProductName;
var productId = wrapper.ProductId ?? throw new NullReferenceException(nameof(wrapper.ProductId));
var product = wrapper.Product;
var images = wrapper.Files;
return Ok(new {
productName,
productId,
product = JsonConvert.SerializeObject(product),
images = images.Select(a => a.FileName)
});
}
[HttpPost("wrapper/simple")]
public IActionResult PostWrapper([FromForm] SimpleProductWrapper wrapper) {
var productName = wrapper.ProductName;
var productId = wrapper.ProductId ?? throw new NullReferenceException(nameof(wrapper.ProductId));
var images = wrapper.Files;
return Ok(new { productName, productId, images = images.Select(a => a.FileName) });
}

[HttpPost("wrapper/complex-data")]
public IActionResult PostDataWrapper([FromForm] ComplexProductWithDataWrapper wrapper)
{
var productName = wrapper.ProductName;
var productId = wrapper.ProductId ?? throw new NullReferenceException(nameof(wrapper.ProductId));
var product = wrapper.Product;
var images = wrapper.Files;
var data = wrapper.ProductData;
return Ok(new
{
productName,
productId,
product = JsonConvert.SerializeObject(product),
images = images.Select(a => a.FileName),
data = JsonConvert.SerializeObject(data)
});
}
}
[HttpPost("wrapper/complex")]
public IActionResult PostWrapper([FromForm] ComplexProductWrapper wrapper) {
var productName = wrapper.ProductName;
var productId = wrapper.ProductId ?? throw new NullReferenceException(nameof(wrapper.ProductId));
var product = wrapper.Product;
var images = wrapper.Files;
return Ok(new {
productName,
productId,
product = JsonConvert.SerializeObject(product),
images = images.Select(a => a.FileName)
});
}

[HttpPost("wrapper/complex-data")]
public IActionResult PostDataWrapper([FromForm] ComplexProductWithDataWrapper wrapper)
{
var productName = wrapper.ProductName;
var productId = wrapper.ProductId ?? throw new NullReferenceException(nameof(wrapper.ProductId));
var product = wrapper.Product;
var images = wrapper.Files;
var data = wrapper.ProductData;
return Ok(new
{
productName,
productId,
product = JsonConvert.SerializeObject(product),
images = images.Select(a => a.FileName),
data = JsonConvert.SerializeObject(data)
});
}
}
28 changes: 14 additions & 14 deletions src/Demo/Demo.csproj
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Swashbuckle.AspNetCore.JsonMultipartFormDataSupport\Swashbuckle.AspNetCore.JsonMultipartFormDataSupport.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="FluentValidation.AspNetCore" Version="11.0.2" />
<PackageReference Include="MicroElements.Swashbuckle.FluentValidation" Version="5.6.0" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.3.0" />
</ItemGroup>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FluentValidation.AspNetCore" Version="11.3.0" />
<PackageReference Include="MicroElements.Swashbuckle.FluentValidation" Version="6.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="7.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="7.0.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Swashbuckle.AspNetCore.JsonMultipartFormDataSupport\Swashbuckle.AspNetCore.JsonMultipartFormDataSupport.csproj" />
</ItemGroup>
</Project>
15 changes: 15 additions & 0 deletions src/Demo/Models/Products/NestedProduct.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace Demo.Models.Products;

public sealed class NestedProduct
{
public Guid Id { get; set; }
public string? Name { get; set; }
public ProductType Type { get; set; }
public required NestedProductData Data { get; set; }
}

public sealed class NestedProductData
{
public double Price { get; set; }
public DateTime StartDate { get; set; }
}
22 changes: 10 additions & 12 deletions src/Demo/Models/Products/Product.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
using System;
namespace Demo.Models.Products;

namespace Demo.Models.Products {
public class Product {
public Guid Id { get; set; }
public string? Name { get; set; }
public ProductType Type { get; set; }
}

public enum ProductType {
Phone,
Laptop
}
public class Product {
public Guid Id { get; set; }
public string? Name { get; set; }
public ProductType Type { get; set; }
}

public enum ProductType {
Phone,
Laptop
}
13 changes: 5 additions & 8 deletions src/Demo/Models/Products/ProductData.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
using System;
namespace Demo.Models.Products;

namespace Demo.Models.Products
public class ProductData
{
public class ProductData
{
public double Price { get; set; }
public DateTime StartDate { get; set; }
}
}
public double Price { get; set; }
public DateTime StartDate { get; set; }
}
23 changes: 11 additions & 12 deletions src/Demo/Models/Products/ProductExamples.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
using System;
using Swashbuckle.AspNetCore.Filters;
using Swashbuckle.AspNetCore.Filters;

namespace Demo.Models.Products {
public class ProductExamples : IExamplesProvider<Product> {
public Product GetExamples() {
var product = new Product {
Id = Guid.NewGuid(),
Name = "Example",
Type = ProductType.Phone
};
return product;
}
namespace Demo.Models.Products;

public class ProductExamples : IExamplesProvider<Product> {
public Product GetExamples() {
var product = new Product {
Id = Guid.NewGuid(),
Name = "Example",
Type = ProductType.Phone
};
return product;
}
}
34 changes: 16 additions & 18 deletions src/Demo/Models/Wrapper/ComplexProductWithDataWrapper.cs
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
using Demo.Models.Products;
using Microsoft.AspNetCore.Http;
using System.ComponentModel.DataAnnotations;
using Demo.Models.Products;
using Swashbuckle.AspNetCore.JsonMultipartFormDataSupport.Attributes;
using System.ComponentModel.DataAnnotations;

namespace Demo.Models.Wrapper
namespace Demo.Models.Wrapper;

public class ComplexProductWithDataWrapper
{
public class ComplexProductWithDataWrapper
{
[FromJson]
[Required]
public Product Product { get; set; } = null!;
[FromJson]
[Required]
public Product Product { get; set; } = null!;

[FromJson]
public ProductData? ProductData { get; set; }
[FromJson]
public ProductData? ProductData { get; set; }

// [FromJson] <-- not required
[Required]
public int? ProductId { get; set; }
// [FromJson] <-- not required
[Required]
public int? ProductId { get; set; }

public string? ProductName { get; set; }
public IFormFileCollection Files { get; set; } = new FormFileCollection();
}
}
public string? ProductName { get; set; }
public IFormFileCollection Files { get; set; } = new FormFileCollection();
}
1 change: 0 additions & 1 deletion src/Demo/Models/Wrapper/ComplexProductWrapper.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System.ComponentModel.DataAnnotations;
using Demo.Models.Products;
using Microsoft.AspNetCore.Http;
using Swashbuckle.AspNetCore.JsonMultipartFormDataSupport.Attributes;

namespace Demo.Models.Wrapper;
Expand Down
13 changes: 6 additions & 7 deletions src/Demo/Models/Wrapper/ProductWrapper.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
using Demo.Models.Products;
using Microsoft.AspNetCore.Http;
using Swashbuckle.AspNetCore.JsonMultipartFormDataSupport.Attributes;

namespace Demo.Models.Wrapper {
public class ProductWrapper {
[FromJson] // <-- This attribute is required for binding.
public Product? Product { get; set; }
namespace Demo.Models.Wrapper;

public IFormFileCollection Files { get; set; } = new FormFileCollection();
}
public class ProductWrapper {
[FromJson] // <-- This attribute is required for binding.
public Product? Product { get; set; }

public IFormFileCollection Files { get; set; } = new FormFileCollection();
}
17 changes: 8 additions & 9 deletions src/Demo/Models/Wrapper/RequiredProductWrapper.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
using System.ComponentModel.DataAnnotations;
using Demo.Models.Products;
using Microsoft.AspNetCore.Http;
using Swashbuckle.AspNetCore.JsonMultipartFormDataSupport.Attributes;

namespace Demo.Models.Wrapper {
public class RequiredProductWrapper {
[Required]
[FromJson] // <-- This attribute is required for binding.
public Product Product { get; set; } = null!;
namespace Demo.Models.Wrapper;

public class RequiredProductWrapper {
[Required]
[FromJson] // <-- This attribute is required for binding.
public Product Product { get; set; } = null!;

[Required]
public IFormFileCollection Files { get; set; } = new FormFileCollection();
}
[Required]
public IFormFileCollection Files { get; set; } = new FormFileCollection();
}
Loading

0 comments on commit 0b75bd2

Please sign in to comment.