Angular传表单+文件至.NET6 Web API遇反序列化错误求助
问题:Angular表单数据+文件上传至.NET 6 Web API失败
Angular 端实现代码
HTML组件代码
<form [formGroup]="form" (ngSubmit)="submit()" enctype="multipart/form-data"> <table> <tr> <td>Name</td> <td><input type="text" formControlName="name"></td> </tr> <tr> <td>Price</td> <td><input type="text" formControlName="price"></td> </tr> <tr> <td>Quantity</td> <td><input type="text" formControlName="quantity"></td> </tr> <tr> <td>Description</td> <td><textarea formControlName="description" cols="30" rows="10"></textarea></td> </tr> <tr> <td>Status</td> <td><input type="checkbox" formControlName="status"></td> </tr> <tr> <td>Photo</td> <td> <input type="file" Name="photo" (change)="fileControl($event)"> </td> </tr> <tr> <td>Category</td> <td> <select formControlName="categoryId"> <option value="1"> cate 1 </option> <option value="2"> cate 2 </option> <option value="3"> cate 3 </option> </select> </td> </tr> <tr> <td> </td> <td><input type="submit" value="Save"></td> </tr> </table> </form>
TS组件代码
export class CreateApiComponent implements OnInit { form: FormGroup; file: any; constructor( private productApiService: ProductApiService, private formBuilder: FormBuilder, private datePipe : DatePipe ){} ngOnInit() { this.form = this.formBuilder.group({ name: '', price: 0, quantity: 0, status: true, description: '', photo: '', categoryId: 1 }) } fileControl(e:any) { this.file = e.target.files[0]; } submit() { let product: ProductApi = this.form.value; product.created = this.datePipe.transform(new Date(), 'dd/MM/yyyy'); let formData = new FormData(); formData.append('data', this.file); this.productApiService.createWithFile(product, formData).then( res => { this.result = do something ; }, err => { console.log(err); } ) } }
服务层createWithFile方法
async createWithFile(product: Product, file: FormData) { return await lastValueFrom(this.httpClient.post(this.baseUrl+'create-with-file', {Product: product, Data: file})); }
ProductApi类
export class ProductApi { id: number; name: string; price: number; quantity: number; status: boolean; description: string; created: string; photo: string; categoryId: number; categoryName: string; }
.NET 6 端实现代码
Product实体类
public partial class Product { public int Id { get; set; } public string? Name { get; set; } public int? Quantity { get; set; } public string? Description { get; set; } public double? Price { get; set; } public bool Status { get; set; } public string? Photo { get; set; } public DateTime Created { get; set; } public int CategoryId { get; set; } }
CreatedUpload接收模型
public class CreatedUpload { public Product Product { get; set; } public IFormFile Data { get; set; } }
控制器方法
[HttpPost("create-with-file")] [Consumes("application/json")] [Produces("application/json")] public IActionResult CreateWithFile([FromBody] CreatedUpload createdUpload) { try { // 此处添加调试代码查看createdUpload数据 return Ok(); } catch { return BadRequest(); } }
遇到的错误
System.NotSupportedException: Deserialization of interface types is not supported. Type 'Microsoft.AspNetCore.Http.IFormFile'. Path: $.Data | LineNumber: 0 | BytePositionInLine: 144.
尝试过修改控制器参数为[FromBody] Product product, [FromBody] IFormFile data或[FromForm] Product product, [FromForm] IFormFile data,但参数均返回null,无法解决。
解决方案
核心原因
文件上传必须使用multipart/form-data格式,不能将文件对象嵌套在JSON中发送(JSON无法序列化文件流);同时[FromBody]只能接收一次请求体,无法同时绑定多个参数。
分步修复
1. 调整Angular端FormData构建逻辑
将表单数据和文件合并到同一个FormData对象中,而非嵌套JSON:
submit() { let product: ProductApi = this.form.value; // 改用.NET易解析的日期格式 product.created = this.datePipe.transform(new Date(), 'yyyy-MM-dd'); let formData = new FormData(); // 将Product的所有字段添加到FormData,用"Product.字段名"作为键实现嵌套绑定 Object.keys(product).forEach(key => { formData.append(`Product.${key}`, product[key]); }); // 添加文件 if (this.file) { formData.append('Data', this.file); } this.productApiService.createWithFile(formData).then( res => { // 处理成功逻辑 }, err => { console.log(err); } ) }
2. 修改Angular服务层方法
直接传递FormData对象,无需额外嵌套:
async createWithFile(formData: FormData) { // HttpClient会自动设置multipart/form-data请求头 return await lastValueFrom(this.httpClient.post(this.baseUrl+'create-with-file', formData)); }
3. 调整.NET端控制器配置
移除[Consumes("application/json")],并使用[FromForm]绑定请求数据:
[HttpPost("create-with-file")] [Produces("application/json")] public IActionResult CreateWithFile([FromForm] CreatedUpload createdUpload) { try { // 此时createdUpload.Product和createdUpload.Data均可正常获取 return Ok(); } catch { return BadRequest(); } }
可选优化:简化接收模型
如果不想使用嵌套的Product对象,可以将Product的字段直接平铺到CreatedUpload中,减少绑定复杂度:
public class CreatedUpload { public string? Name { get; set; } public int? Quantity { get; set; } public string? Description { get; set; } public double? Price { get; set; } public bool Status { get; set; } public string? Photo { get; set; } public DateTime Created { get; set; } public int CategoryId { get; set; } public IFormFile Data { get; set; } }
对应Angular端只需直接append字段名即可:
Object.keys(product).forEach(key => { formData.append(key, product[key]); });
内容的提问来源于stack exchange,提问作者FalAn
相关产品推荐
相关产品推荐

