You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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>&nbsp;</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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 01:01:51