[C#] .Net6 關於 Web API 的二三事 – form 直接 POST 到 Web API

2022-10-03


最近都把專案升級成 .Net  6 ,沒啥大問題,只是最近想找個機會好好研究一下 Web API,想說遇到或是測試到就筆記一下,所以也不是啥教學文

如果有啥更好得做法,可以在跟我說,這邊沒有啥難度的技術,可能對很多高手來說就是喝水一樣的事情。


這次案例設計很簡單就是一個 <form> 我直接 POST 到 Web API (案例中為 TestController) 上面

介紹一下 <form> 的結構

&lt;form action=&quot;/api/test/sample2&quot; method=&quot;post&quot;&gt; &lt;div style=&quot;margin-bottom:20px&quot;&gt; &lt;legend&gt;用戶帳號&lt;/legend&gt;&lt;br /&gt; &lt;input class=&quot;input&quot; type=&quot;text&quot; placeholder=&quot;donma&quot; name=&quot;userId&quot;&gt; &lt;/div&gt; &lt;fieldset&gt; &lt;legend&gt;選擇你的興趣:&lt;/legend&gt; &lt;div&gt; &lt;input type=&quot;checkbox&quot; name=&quot;hobby&quot; checked value=&quot;釣魚&quot;&gt; &lt;label&gt;釣魚&lt;/label&gt; &lt;/div&gt; &lt;div&gt; &lt;input type=&quot;checkbox&quot; name=&quot;hobby&quot; checked value=&quot;睡覺&quot;&gt; &lt;label&gt;睡覺&lt;/label&gt; &lt;/div&gt; &lt;div&gt; &lt;input type=&quot;checkbox&quot; name=&quot;hobby&quot; value=&quot;看電影&quot;&gt; &lt;label&gt;看電影&lt;/label&gt; &lt;/div&gt; &lt;div&gt; &lt;input type=&quot;checkbox&quot; name=&quot;hobby&quot; value=&quot;上網&quot;&gt; &lt;label&gt;上網&lt;/label&gt; &lt;/div&gt; &lt;/fieldset&gt; &lt;div&gt; &lt;legend for=&quot;gender&quot;&gt;性別&lt;/legend&gt;&lt;br&gt; &lt;select name=&quot;gender&quot;&gt; &lt;option value=&quot;0&quot;&gt;Female&lt;/option&gt; &lt;option value=&quot;1&quot;&gt;Male&lt;/option&gt; &lt;/select&gt; &lt;/div&gt; &lt;div&gt; &lt;legend for=&quot;&quot;&gt;生日&lt;/legend&gt;&lt;br&gt; &lt;input class=&quot;input&quot; type=&quot;date&quot; placeholder=&quot;2022-10-11&quot; value=&quot;2022-10-11&quot; name=&quot;userBirth&quot;&gt; &lt;/div&gt; &lt;div&gt; &lt;legend for=&quot;&quot;&gt;薪水&lt;/legend&gt;&lt;br&gt; &lt;input class=&quot;input&quot; type=&quot;number&quot; value=&quot;199.99&quot; name=&quot;userSalary&quot;&gt; &lt;/div&gt; &lt;hr /&gt; &lt;div&gt; &lt;input type=&quot;submit&quot; value=&quot;送出&quot;&gt; &lt;/div&gt; &lt;/form&gt;

出來長相大概長這樣,如果你看 code 無法想像的話


接下來就是 首先在 .Net 6 中,你得先在 Program.cs 加入 app.MapControllers();  這一步我常常忘記



Web API Code Sample2 :

using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using System.Dynamic; namespace TestWebAPI1.API { [Route("api/test")] [ApiController] public class TestController : ControllerBase { [HttpGet] public string Get() { return "Test Service"; } [HttpPost] [Route("sample2")] public IActionResult RegistUserActionResult([FromForm] string userId, [FromForm] string gender, [FromForm] string[] hobby, [FromForm] DateTimeOffset userBirth, [FromForm] decimal? userSalary) { dynamic res = new ExpandoObject(); res.UserId = userId; res.Gender = gender; res.Hobby = hobby; res.Birth = userBirth; res.Salary = userSalary; return Ok(res); } } }

這案例很簡單回傳就是 IActionResult ,其實回傳他預設就會是 ontent-type: application/json; charset=utf-8


回應結果

{"UserId":"許當麻","Gender":"0","Hobby":["釣魚","睡覺"],"Birth":"2022-10-11T00:00:00+08:00","Salary":199.99}


看一下回應的 headers


這裡面值得一提的就是,input 如果 type  是 number ,WebAPI 這邊是可以用 decimal 承接,如果是 input:checkbox 同一個 name ,則可以使用 string[]  接 (如文中的 hobby)

這其實非常方便,當然一般狀況通常都是在前端準備好物件(一個 自訂好結構的 json ) 在一次往後送,不過這是測試,我也非常偷懶的直接用 dynamic 接了就拋


再來測試 XML 輸出,因為輸出 dynamic 比較麻煩所以我建立了一個物件 User.cs

public class User { public string Id { get; set; } public string Name { get; set; } public string[]? Hobby { get; set; } public string Gender { get; set; } public decimal? Salary { get; set; } public DateTimeOffset Birth { get; set; } }


Web API Code Sample3 :


using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using System.Dynamic; namespace TestWebAPI1.API { [Route("api/test")] [ApiController] public class TestController : ControllerBase { [HttpGet] public string Get() { return "Test Service"; } [HttpPost] [Route("sample3")] //輸出為 XML [Produces("application/xml")] public IActionResult RegistUserJson([FromForm] string userId, [FromForm] string gender, [FromForm] string[] hobby, [FromForm] DateTimeOffset userBirth, [FromForm] decimal? userSalary) { var u = new Models.User(); u.Name = userId; u.Id = userId; u.Gender = gender; u.Birth = userBirth; u.Hobby = hobby; u.Salary = userSalary; return Ok(u); } } }


注意,需要再 Program.cs 中 var app = builder.Build();  之前加入 

builder.Services.AddControllers().AddXmlSerializerFormatters().AddXmlDataContractSerializerFormatters();


當然 form 那邊的 action 也要改成  action="/api/test/sample3"


回應結果:

&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt; &lt;User xmlns:xsd=&quot;http://www.w3.org/2001/XMLSchema&quot; xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;&gt; &lt;script /&gt; &lt;Id&gt;許當麻&lt;/Id&gt; &lt;Name&gt;許當麻&lt;/Name&gt; &lt;Hobby&gt; &lt;string&gt;釣魚&lt;/string&gt; &lt;string&gt;睡覺&lt;/string&gt; &lt;/Hobby&gt; &lt;Gender&gt;0&lt;/Gender&gt; &lt;Salary&gt;199.99&lt;/Salary&gt; &lt;Birth&gt;2022-10-11T00:00:00+08:00&lt;/Birth&gt; &lt;/User&gt;


回應的 headers




注意: 如果你的 Web API 部分有些是允許 null 或是空值傳入 ,但是你傳入到 Web API 會出現 

{

   "type":"https://tools.ietf.org/html/rfc7231#section-6.5.1",

   "title":"One or more validation errors occurred.",

   "status":400,

   "traceId":"00-c483917b91ff3410d2305af939c2ac3e-60280f3f85d80a58-00",

   "errors":{

      "userId":[

         "The userId field is required."

      ]

   }


需要再 Program.cs 中 修改這一段

//for not check required builder.Services.AddControllers(options => options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true);


需要再 AddControllers 中加入  options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true 


大概筆記到這裡,不算教學,算是給自己的筆記


當麻許的碎念筆記 2014 | Donma Hsu Design.