asp.net 将Angular 数据(带有字段和文件)传递到API时出现“不支持接口类型的反序列化”错误

4zcjmb1e  于 2023-02-17  发布在  .NET
关注(0)|答案(1)|浏览(90)

我正在练习用Angular将值传递给表单,然后将其放在asp.net的post url中下面是我所做的:我的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函数,baseUrl只是一个包含"localhost:port/path"的字符串:

async createWithFile(product: Product, file: FormData){
    return await lastValueFrom(this.httpClient.post(this.baseUrl+'create-with-file', {Product: product, Data: file}));
}

这是我的产品Api类:

export class ProductApi{
    id: number;
    name: string;
    price: number;
    quantity: number;
    status: boolean;
    description: string;
    created: string;
    photo: string;
    categoryId: number;
    categoryName: string;
}

现在到我的Asp.net,我使用实体框架:这是我的产品类,Created字段得到了一个JsonConverter来处理它,所以不用担心:

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; }
}

我还创建了一个模型来捕获post请求中的参数:

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
        {
            *at this spot i put a debug to check the data in createdUpload*
            return Ok();
        }
        catch
        {
            return BadRequest();
        }
    }

这是我所期望的,我期望createdUpload捕获{Product:产品,数据:文件}从后请求,但我得到这个错误:

System.NotSupportedException: Deserialization of interface types is not supported. Type 'Microsoft.AspNetCore.Http.IFormFile'. Path: $.Data | LineNumber: 0 | BytePositionInLine: 144.

我也像这样修改了CreateWithFile的参数,但是它们都返回null(但没有引发错误):

[FromBody] Product product, [FromBody] IFormFile data
[FromForm] Product product, [FromForm] IFormFile data

我在Stackoverflow看了很多问题,但是没有一个答案能解决我的问题,我走到了死胡同,请帮帮我:(

i7uq4tfw

i7uq4tfw1#

您应该将带有product对象和file的请求主体作为FormData发送到API。
1.迭代product对象的键属性,将键值对添加到formData中,注意发送product对象需要前缀product。

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);

  // Add key-value pair into formData
  let key: keyof typeof product;
  for (k in product) {
    formData.append(`product.${k}`, product[k]);
  }

  this.productApiService.createWithFile(formData).then(
    res => {
      // Success
    },
    err => {
      console.log(err);                
    }
  )
}

1.用formData参数修改createWithFile方法签名,发布formData,Angular HttpClient将发布content-type为:multipart/form-data.

async createWithFile(formData: FormData){
    return await lastValueFrom(this.httpClient.post(this.baseUrl+'create-with-file', formData));
}

1.在Web API中,修改CreateWithFile操作以接收具有FromForm属性的createdUpload对象。

public IActionResult CreateWithFile([FromForm] CreatedUpload createdUpload)

相关问题