這幾天剛 好有機會把WebAPI ModelBinding的方式整理一下,MVC也是差不多的用法,但還是會有一些差異。
今天會用到[FromRoute], [FromQuery], [FromBody]這三種ModelBinding方式
快速建立有CRUD模板的Controller
- 新增Controller
- 選擇模板
- 修改啟動執行檔(launchsettings.json)
把launchUrl修改成我們新的啟動路由。避免網頁啟動時一直是預設啟動的weatherforecast。
- 修改Controller上面的路由範本
[Route("api/[controller]")] => [Route("api/[controller]/[action]")]
加上[action],不然只能執行預設的沒有參數的[HttpGet] Action。
建立測試資料來源
因為一切講求快速,資料來源就使用一個靜態的List來代替
public static List<Info> MyFriendList = new List<Info>
{
new Info(){ Id = 999, Mobile = "111", Name = "JoJo"},
};
public class Info
{
public int? Id { get; set; }
public string Name { get; set; }
public string Mobile { get; set; }
}
接著利用原本就有的Get, Post, Put, Delete這四個Action進行快速魔改!
Get [Read]
- 無參數的Get
[HttpGet]
public IEnumerable<Info> Get()
{
return MyFriendList.ToList();
}
- 透過[FromRoute]從路由傳遞參數,取得指定資料
如果直接把路由參數加在[HttpGet]後面的話,就不用在參數前面加上[FromRoute]。另外記得路由參數要長得跟參數一模一樣(包含大小寫)。
[HttpGet("{id}")]
public string Get_FromRoute(int id)
{
var data = MyFriendList.Where(c => c.Id == id).FirstOrDefault();
if (data != null)
{
return $"Id:{data.Id}, Name:{data.Name}, Mobile:{data.Mobile}.";
}
return "no data";
}
Post [Create]
- 透過 [FromBody傳送參數]
[HttpPost]
public string Post([FromBody] string value)
{
var rnd = new Random();
MyFriendList.Add(new Info
{ Id = rnd.Next(1, 9999), Name = value, Mobile = "0912345678" });
return "Post Sucess.";
}
如果參數來源為[FromBody],參數預設會以json/application傳遞,如果參數不為Dto Class的話,直接傳字串即可。
Post不能直接透過瀏覽器直接傳送Request Body,所以一定要透過API測試工具。雖然大家都用Postman,但我偏愛Insomnia,另外要提醒大家,如果用launchsettings.json的啟動方式是用https且是用NTLM驗證身分的話,可能會有問題喔,可以參考:Insomnia呼叫由Kestrel啟動的Web API時,會一直回應401 Unauthorized錯誤。這篇測試雖然是用https測試,但沒有身分驗證,所以沒有問題。
- 另外加碼用Route傳遞參數來呼叫Post的API(誰說Post只能用Request Body來傳參數!!)
[HttpPost("{name}/{mobile}")]
public string Post_FromRoute(string name, string mobile)
{
var rnd = new Random();
MyFriendList.Add(new Info
{ Id = rnd.Next(1, 9999), Name = name, Mobile = mobile });
return "Post Sucess.";
}
- 再加碼用[FromQuery]傳遞參數來呼叫Post的API(誰說Post只能用Request Body來傳參數!!)
[HttpPost]
public string Post_FromQuery(string? name, string? mobile)
{
var rnd = new Random();
var data = new Info { Id = rnd.Next(1, 9999) };
if (!string.IsNullOrEmpty(name))
{
data.Name = name;
}
if (!string.IsNullOrEmpty(mobile))
{
data.Mobile = mobile;
}
MyFriendList.Add(data);
return "Post Sucess.";
}
[FromRoute]跟[FromQuery]都是透過網址來栓參數,但這兩者的區別有看出在哪裡嗎!
- [FromRouter]
- https://localhost:44359/MyCRUD/Create_FromRoute/hao_test/999
- 參數一定要按照路由格式填寫。
- [FromQuery]
- https://localhost:44359/MyCRUD/Create_FromQuery?name=gg&mobile=
- 參數不一定要填寫,彈性較大。
Put [Update]
- 透過Route + [FromBody]傳送參數
[HttpPut("{id}")]
public string Put(int id, [FromBody] string value)
{
var data = MyFriendList.Where(c => c.Id == id).FirstOrDefault();
if (data != null)
{
data.Name = value;
return "Put Success.";
}
else { return "Put Fail."; }
}
Put 一樣不能直接透過瀏覽器直接呼叫,一定要透過API測試工具。
此API參數透過Route(如果直接把路由參數加在[HttpGet]後面的話,就不用在參數前面加上[FromRoute]) + [FromBody]傳輸。
- 全部參數透過[FromBody]傳遞
[HttpPut]
public string Put_FromBody([FromBody] RequestDto para)
{
var data = MyFriendList.Where(c => c.Id == para.Id).FirstOrDefault();
if (data != null)
{
data.Name = para.Name;
return "Put Success.";
}
else { return "Put Fail."; }
}
[FromBody]標籤只能綁定一個參數,但如果把所有的參數用一個DTO Class包裝起來的話,就可以一起使用[FromBody]來傳遞參數了。且.Net Core會自己進行ModelBinding
Put [Delete]
- 透過Route傳送參數
[HttpDelete("{id}")]
public string Delete(int id)
{
var data = MyFriendList.Where(c => c.Id == id).FirstOrDefault();
if (data != null)
{
MyFriendList.Remove(data);
return "Delete Success.";
}
else { return "Delete Fail."; }
}
改用[HttpGet]來呼叫Delete的API
安捏也是可以的喔
[HttpGet("{id}")]
public string Delete_FromRoute([FromRoute]int id)
{
var data = MyFriendList.Where(c => c.Id == id).FirstOrDefault();
if (data != null)
{
MyFriendList.Remove(data);
return "Delete Success.";
}
else { return "Delete Fail."; }
}
但最好是別這麼做,使用HttpDelete是為了讓參數不直接透過網址url傳送,避免用網址即可呼叫API誤刪資料(因為[HttpGet]會把網址存在紀錄裡)。但就算使用[HttpDelete]也還是要加上身分驗證,不然知道id的人照樣能用API測試工具來刪除資料。
以上