Get error: "Deserialization of interface types is not supported" while sending request (with field and file) to .NET 6 Web API

huangapple go评论91阅读模式
英文:

Get error: "Deserialization of interface types is not supported" while sending request (with field and file) to .NET 6 Web API

问题

我正在练习在Angular中将值传递给表单,然后将其与文件一起发送到.NET 6 Web API。

以下是我所做的事情:

我的HTML组件:

  1. <form [formGroup]="form" (ngSubmit)="submit()" enctype="multipart/form-data">
  2. <table>
  3. <tr>
  4. <td>Name</td>
  5. <td><input type="text" formControlName="name"></td>
  6. </tr>
  7. <tr>
  8. <td>Price</td>
  9. <td><input type="text" formControlName="price"></td>
  10. </tr>
  11. <tr>
  12. <td>Quantity</td>
  13. <td><input type="text" formControlName="quantity"></td>
  14. </tr>
  15. <tr>
  16. <td>Description</td>
  17. <td><textarea formControlName="description" cols="30" rows="10"></textarea></td>
  18. </tr>
  19. <tr>
  20. <td>Status</td>
  21. <td><input type="checkbox" formControlName="status"></td>
  22. </tr>
  23. <tr>
  24. <td>Photo</td>
  25. <td>
  26. <input type="file" Name="photo" (change)="fileControl($event)">
  27. </td>
  28. </tr>
  29. <tr>
  30. <td>Category</td>
  31. <td>
  32. <select formControlName="categoryId">
  33. <option value="1"> cate 1 </option>
  34. <option value="2"> cate 2 </option>
  35. <option value="3"> cate 3 </option>
  36. </select>
  37. </td>
  38. </tr>
  39. <tr>
  40. <td>&nbsp;</td>
  41. <td><input type="submit" value="Save"></td>
  42. </tr>
  43. </table>
  44. </form>

这是我的ts组件:

  1. export class CreateApiComponent implements OnInit {
  2. form: FormGroup;
  3. file: any;
  4. constructor(
  5. private productApiService: ProductApiService,
  6. private formBuilder: FormBuilder,
  7. private datePipe : DatePipe
  8. ){}
  9. ngOnInit() {
  10. this.form = this.formBuilder.group({
  11. name: '',
  12. price: 0,
  13. quantity: 0,
  14. status: true,
  15. description: '',
  16. photo: '',
  17. categoryId: 1
  18. })
  19. }
  20. fileControl(e:any) {
  21. this.file = e.target.files[0];
  22. }
  23. submit() {
  24. let product: ProductApi = this.form.value;
  25. product.created = this.datePipe.transform(new Date(), 'dd/MM/yyyy');
  26. let formData = new FormData();
  27. formData.append('data', this.file);
  28. this.productApiService.createWithFile(product, formData).then(
  29. res => {
  30. this.result = do something ;
  31. },
  32. err => {
  33. console.log(err);
  34. }
  35. )
  36. }
  37. }

这是我的createWithFile函数,baseUrl只是一个包含"localhost:port/path"的字符串:

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

这是我的productApi类:

  1. export class ProductApi {
  2. id: number;
  3. name: string;
  4. price: number;
  5. quantity: number;
  6. status: boolean;
  7. description: string;
  8. created: string;
  9. photo: string;
  10. categoryId: number;
  11. categoryName: string;
  12. }

现在到我的ASP.NET,我正在使用Entity Framework:

这是我的Product类,Created属性有一个JsonConverter来处理它,所以不用担心:

  1. public partial class Product
  2. {
  3. public int Id { get; set; }
  4. public string? Name { get; set; }
  5. public int? Quantity { get; set; }
  6. public string? Description { get; set; }
  7. public double? Price { get; set; }
  8. public bool Status { get; set; }
  9. public string? Photo { get; set; }
  10. public DateTime Created { get; set; }
  11. public int CategoryId { get; set; }
  12. }

我还创建了一个模型来接收来自POST请求的参数:

  1. public class CreatedUpload
  2. {
  3. public Product Product { get; set; }
  4. public IFormFile Data { get; set; }
  5. }

这是我的控制器:

  1. [HttpPost("create-with-file")]
  2. [Consumes("application/json")]
  3. [Produces("application/json")]
  4. public IActionResult CreateWithFile([FromBody] CreatedUpload createdUpload)
  5. {
  6. try
  7. {
  8. *在这个位置,我放了一个调试以检查createdUpload中的数据*
  9. return Ok();
  10. }
  11. catch
  12. {
  13. return BadRequest();
  14. }
  15. }

这是我期望的,我希望createdUpload从POST请求中捕获{Product: product, Data: file},但我得到了这个错误:

System.NotSupportedException: 不支持接口类型的反序列化。类型 'Microsoft.AspNetCore.Http.IFormFile'。路径:$.Data | LineNumber: 0 | BytePositionInLine: 144。

我还更改了CreateWithFile的参数,但它们都返回null(但不会引发错误):

  1. [FromBody] Product product, [FromBody] IFormFile data
  2. [FromForm] Product product, [FromForm] IFormFile data

我已经阅读了StackOverflow上的许多问题,但没有答案能够解决我的问题,我陷入了困境,请帮助我 Get error: "Deserialization of interface types is not supported" while sending request (with field and file) to .NET 6 Web API

英文:

I am practicing passing value to a form in Angular, and then sending it with a file to .NET 6 Web API.

Here is what I do:

My HTML component:

  1. &lt;form [formGroup]=&quot;form&quot; (ngSubmit)=&quot;submit()&quot; enctype=&quot;multipart/form-data&quot;&gt;
  2. &lt;table&gt;
  3. &lt;tr&gt;
  4. &lt;td&gt;Name&lt;/td&gt;
  5. &lt;td&gt;&lt;input type=&quot;text&quot; formControlName=&quot;name&quot;&gt;&lt;/td&gt;
  6. &lt;/tr&gt;
  7. &lt;tr&gt;
  8. &lt;td&gt;Price&lt;/td&gt;
  9. &lt;td&gt;&lt;input type=&quot;text&quot; formControlName=&quot;price&quot;&gt;&lt;/td&gt;
  10. &lt;/tr&gt;
  11. &lt;tr&gt;
  12. &lt;td&gt;Quantity&lt;/td&gt;
  13. &lt;td&gt;&lt;input type=&quot;text&quot; formControlName=&quot;quantity&quot;&gt;&lt;/td&gt;
  14. &lt;/tr&gt;
  15. &lt;tr&gt;
  16. &lt;td&gt;Description&lt;/td&gt;
  17. &lt;td&gt;&lt;textarea formControlName=&quot;description&quot; cols=&quot;30&quot; rows=&quot;10&quot;&gt;&lt;/textarea&gt;&lt;/td&gt;
  18. &lt;/tr&gt;
  19. &lt;tr&gt;
  20. &lt;td&gt;Status&lt;/td&gt;
  21. &lt;td&gt;&lt;input type=&quot;checkbox&quot; formControlName=&quot;status&quot;&gt;&lt;/td&gt;
  22. &lt;/tr&gt;
  23. &lt;tr&gt;
  24. &lt;td&gt;Photo&lt;/td&gt;
  25. &lt;td&gt;
  26. &lt;input type=&quot;file&quot; Name=&quot;photo&quot; (change)=&quot;fileControl($event)&quot;&gt;
  27. &lt;/td&gt;
  28. &lt;/tr&gt;
  29. &lt;tr&gt;
  30. &lt;td&gt;Category&lt;/td&gt;
  31. &lt;td&gt;
  32. &lt;select formControlName=&quot;categoryId&quot;&gt;
  33. &lt;option value=&quot;1&quot;&gt; cate 1 &lt;/option&gt;
  34. &lt;option value=&quot;2&quot;&gt; cate 2 &lt;/option&gt;
  35. &lt;option value=&quot;3&quot;&gt; cate 3 &lt;/option&gt;
  36. &lt;/select&gt;
  37. &lt;/td&gt;
  38. &lt;/tr&gt;
  39. &lt;tr&gt;
  40. &lt;td&gt;&amp;nbsp;&lt;/td&gt;
  41. &lt;td&gt;&lt;input type=&quot;submit&quot; value=&quot;Save&quot;&gt;&lt;/td&gt;
  42. &lt;/tr&gt;
  43. &lt;/table&gt;
  44. &lt;/form&gt;

And here is my ts component:

  1. export class CreateApiComponent implements OnInit {
  2. form: FormGroup;
  3. file: any;
  4. constructor(
  5. private productApiService: ProductApiService,
  6. private formBuilder: FormBuilder,
  7. private datePipe : DatePipe
  8. ){}
  9. ngOnInit() {
  10. this.form = this.formBuilder.group({
  11. name: &#39;&#39;,
  12. price: 0,
  13. quantity: 0,
  14. status: true,
  15. description: &#39;&#39;,
  16. photo: &#39;&#39;,
  17. categoryId: 1
  18. })
  19. }
  20. fileControl(e:any) {
  21. this.file = e.target.files[0];
  22. }
  23. submit() {
  24. let product: ProductApi = this.form.value;
  25. product.created = this.datePipe.transform(new Date(), &#39;dd/MM/yyyy&#39;);
  26. let formData = new FormData();
  27. formData.append(&#39;data&#39;, this.file);
  28. this.productApiService.createWithFile(product, formData).then(
  29. res =&gt; {
  30. this.result = do something ;
  31. },
  32. err =&gt; {
  33. console.log(err);
  34. }
  35. )
  36. }
  37. }

And this is my createWithFile function, baseUrl is just a string containing "localhost:port/path":

  1. async createWithFile(product: Product, file: FormData) {
  2. return await lastValueFrom(this.httpClient.post(this.baseUrl+&#39;create-with-file&#39;, {Product: product, Data: file}));
  3. }

And this is my productApi class:

  1. export class ProductApi {
  2. id: number;
  3. name: string;
  4. price: number;
  5. quantity: number;
  6. status: boolean;
  7. description: string;
  8. created: string;
  9. photo: string;
  10. categoryId: number;
  11. categoryName: string;
  12. }

Now to my ASP.NET, I'm using Entity Framework:
This is my Product class, the Created property got a JsonConverter to take care of it so no worry:

  1. public partial class Product
  2. {
  3. public int Id { get; set; }
  4. public string? Name { get; set; }
  5. public int? Quantity { get; set; }
  6. public string? Description { get; set; }
  7. public double? Price { get; set; }
  8. public bool Status { get; set; }
  9. public string? Photo { get; set; }
  10. public DateTime Created { get; set; }
  11. public int CategoryId { get; set; }
  12. }

I also create a model to catch the param from post request:

  1. public class CreatedUpload
  2. {
  3. public Product Product { get; set; }
  4. public IFormFile Data { get; set; }
  5. }

This is my controller:

  1. [HttpPost(&quot;create-with-file&quot;)]
  2. [Consumes(&quot;application/json&quot;)]
  3. [Produces(&quot;application/json&quot;)]
  4. public IActionResult CreateWithFile([FromBody] CreatedUpload createdUpload)
  5. {
  6. try
  7. {
  8. *at this spot i put a debug to check the data in createdUpload*
  9. return Ok();
  10. }
  11. catch
  12. {
  13. return BadRequest();
  14. }
  15. }

Here is what I expect, I expect the createdUpload to catch {Product: product, Data: file} from the POST request, but instead I got this error:

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

I also change the param of my CreateWithFile like this, but all of them return null (but not raise error):

  1. [FromBody] Product product, [FromBody] IFormFile data
  2. [FromForm] Product product, [FromForm] IFormFile data

I have read many questions in StackOverflow, but no answer can solve my problem, and I reach a dead end, please help me Get error: "Deserialization of interface types is not supported" while sending request (with field and file) to .NET 6 Web API

答案1

得分: 2

  1. 使用 FormData 将请求主体发送包含 product 对象和 file 的内容。
  1. submit() {
  2. let product: ProductApi = this.form.value;
  3. product.created = this.datePipe.transform(new Date(), 'dd/MM/yyyy');
  4. let formData = new FormData();
  5. formData.append('data', this.file);
  6. // 将键值对添加到 formData
  7. let key: keyof typeof product;
  8. for (k in product) {
  9. formData.append(`product.${k}`, product[k]);
  10. }
  11. this.productApiService.createWithFile(formData).then(
  12. res => {
  13. // 成功
  14. },
  15. err => {
  16. console.log(err);
  17. }
  18. )
  19. }
  1. 使用 formData 参数修改 createWithFile 方法签名。 使用 Angular HttpClient 发送请求,内容类型为 multipart/form-data
  1. async createWithFile(formData: FormData){
  2. return await lastValueFrom(this.httpClient.post(this.baseUrl+'create-with-file', formData));
  3. }
  1. 在 Web API 中,使用 FromForm 特性修改 CreateWithFile 操作,以接收 createdUpload 对象。
  1. public IActionResult CreateWithFile([FromForm] CreatedUpload createdUpload)
英文:

You should send the request body with product object and file as FormData to API.

  1. Iterate the key properties of product object and add the key-value pair into formData. Note that it is required to have the prefix "product" in order to send the product object.
  1. submit() {
  2. let product: ProductApi = this.form.value;
  3. product.created = this.datePipe.transform(new Date(), &#39;dd/MM/yyyy&#39;);
  4. let formData = new FormData();
  5. formData.append(&#39;data&#39;, this.file);
  6. // Add key-value pair into formData
  7. let key: keyof typeof product;
  8. for (k in product) {
  9. formData.append(`product.${k}`, product[k]);
  10. }
  11. this.productApiService.createWithFile(formData).then(
  12. res =&gt; {
  13. // Success
  14. },
  15. err =&gt; {
  16. console.log(err);
  17. }
  18. )
  19. }
  1. Modify the createWithFile method signature with formData parameter. Post the formData. Angular HttpClient will post the request with content-type: multipart/form-data.
  1. async createWithFile(formData: FormData){
  2. return await lastValueFrom(this.httpClient.post(this.baseUrl+&#39;create-with-file&#39;, formData));
  3. }
  1. In Web API, modify the CreateWithFile action to receive the createdUpload object with the FromForm attribute.
  1. public IActionResult CreateWithFile([FromForm] CreatedUpload createdUpload)

huangapple
  • 本文由 发表于 2023年2月16日 18:13:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/75470746.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定