快速建立CRUD WebAPI,以及ModelBinding的應用

這幾天剛 好有機會把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測試工具來刪除資料。

 

以上