ASP.NET WebAPI2第004天_請求內容回傳格式_客製化方法名稱_FromBody,FromUri用法

 
雖然預設跑出來是json
但其實都還是建議要透過Accept
設定好client side預期接收的格式


Accept indicates what kind of response from the server the client can accept. 
可理解為發送端(client)希望能接收到的回應格式

Content-type always is about the content of the current request or response.
可理解為發送端(client or server)目前發出請求傳遞的資料格式



比方XML







發送的請求

1
2
3
GET /api/products HTTP/1.1
Host: localhost:59877
Accept: application/xml



請求回來的Response

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
GET http://localhost:59877/api/products: {
  "Network": {
    "addresses": {
      "local": {
        "address": "::1",
        "family": "IPv6",
        "port": 52394
      },
      "remote": {
        "address": "::1",
        "family": "IPv6",
        "port": 59877
      }
    }
  },
  "Request Headers": {
    "accept": "application/xml",
    "postman-token": "3590169d-af21-44c8-b536-42fae0088fac",
    "host": "localhost:59877",
    "connection": "keep-alive"
  },
  "Response Headers": {
    "cache-control": "no-cache",
    "pragma": "no-cache",
    "content-type": "application/xml; charset=utf-8",
    "expires": "-1",
    "server": "Microsoft-IIS/10.0",
    "x-aspnet-version": "4.0.30319",
    "x-sourcefiles": "=?UTF-8?B?RDpcV2ViUHJvamVjdHNcV2ViQXBpXE15V2ViQVBJMl9UZXN0MFxNeVdlYkFQSTJfVGVzdDBcYXBpXHByb2R1Y3Rz?=",
    "x-powered-by": "ASP.NET",
    "date": "Mon, 13 Dec 2021 08:09:15 GMT",
    "content-length": "392"
  },
  "Response Body": "<ArrayOfProduct xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://schemas.datacontract.org/2004/07/MyWebAPI2_Test0.Models\"><Product><Id>0</Id><Price>20</Price><ProductName>Apple</ProductName></Product><Product><Id>1</Id><Price>40</Price><ProductName>Banana</ProductName></Product><Product><Id>2</Id><Price>60</Price><ProductName>Orange</ProductName></Product></ArrayOfProduct>"
}



或Json





發送的請求

1
2
3
GET /api/products HTTP/1.1
Host: localhost:59877
Accept: application/json





請求回來的Response

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
GET http://localhost:59877/api/products: {
  "Network": {
    "addresses": {
      "local": {
        "address": "::1",
        "family": "IPv6",
        "port": 53622
      },
      "remote": {
        "address": "::1",
        "family": "IPv6",
        "port": 59877
      }
    }
  },
  "Request Headers": {
    "accept": "application/json",
    "postman-token": "194ecb74-86fb-4cc3-a51b-7bc921f43086",
    "host": "localhost:59877",
    "connection": "keep-alive"
  },
  "Response Headers": {
    "cache-control": "no-cache",
    "pragma": "no-cache",
    "content-type": "application/json; charset=utf-8",
    "expires": "-1",
    "server": "Microsoft-IIS/10.0",
    "x-aspnet-version": "4.0.30319",
    "x-sourcefiles": "=?UTF-8?B?RDpcV2ViUHJvamVjdHNcV2ViQXBpXE15V2ViQVBJMl9UZXN0MFxNeVdlYkFQSTJfVGVzdDBcYXBpXHByb2R1Y3Rz?=",
    "x-powered-by": "ASP.NET",
    "date": "Mon, 13 Dec 2021 08:27:25 GMT",
    "content-length": "135"
  },
  "Response Body": "[{\"Id\":0,\"ProductName\":\"Apple\",\"Price\":20.0},{\"Id\":1,\"ProductName\":\"Banana\",\"Price\":40.0},{\"Id\":2,\"ProductName\":\"Orange\",\"Price\":60.0}]"
}


客製化方法名稱

在預設產生的專案範本中其實可看到被規範要
命名固定的名稱Get,Post,Put,Delete
或者GetXXX,PostXXX,PutXXX,DeleteXXX (有固定關鍵字前綴)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class ValuesController : ApiController
{
    // GET api/values
    public IEnumerable<string> Get()
    {
        return new string[] { "value1", "value2" };
    }

    // GET api/values/5
    public string Get(int id)
    {
        return "value";
    }

    // POST api/values
    public void Post([FromBody] string value)
    {
    }

    // PUT api/values/5
    public void Put(int id, [FromBody] string value)
    {
    }

    // DELETE api/values/5
    public void Delete(int id)
    {
    }
}

以Get為例
若我Products名稱改為Get也就是預設的


或者GetProducts
都是可以直接透過Url 路由: api/Products去訪問的




但當我一改為LoadProducts時候就會出錯





為了能夠讓方法客製化彈性我們只需增加[HttpGet]相關的Http Verbs修飾即可


那這裡可以再實驗Delete 跟 Post的狀況
首先一樣命名一個是屬於固定關鍵字前綴的

PostProduct



DeleteProduct



都屬於200系列

而若我們故意改成非PostXXX跟DeleteXXX 則效果
Post我這裡測試過若用AddXXX預設也可以成功識別為Post添加一筆

但RemoveXXX這個就會突發出現405 Method 無法識別
mapping不到HttpDelete


當把HttpDelete 屬性修飾補上後即可work 回傳的是200系列




將Put改成ModifyProduct不加HttpVerbs屬性修飾時候
也有類似狀況


當補加回[HttpPut]修飾後就能正常Work了












也因此建議
若要客製化對應HttpVerbs 的Method 名稱話
養成好習慣
第一種.統一都加上對應HttpVerbs的屬性修飾
[HttpGet]
[HttpPost]
[HttpPut]
[HttpDelete]

第一種方式的程式碼

WebApiConfig 採用預設路由

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;

namespace MyWebAPI2_Test0
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API 設定和服務

            // Web API 路由
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }
    }
}


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
using MyWebAPI2_Test0.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;

namespace MyWebAPI2_Test0.Controllers
{
    public class ProductsController : ApiController
    {
        static List<Product> products = new List<Product>()
        {
            new Product(){Id = 0,ProductName = "Apple",Price = 20},
            new Product(){Id = 1,ProductName = "Banana",Price = 40},
            new Product(){Id = 2,ProductName = "Orange",Price = 60}
        };

        // GET: api/Products
        [HttpGet]
        public IHttpActionResult LoadProducts()
        {
            return Ok(products);
        }

        // GET: api/Products/5
        [HttpGet]
        public Product GetSpecificProduct(int id)
        {
            return products[id];
        }

        // POST: api/Products
        [HttpPost]
        public HttpResponseMessage AddProduct([FromBody] Product product)
        {
            products.Add(product);
            return new HttpResponseMessage(HttpStatusCode.Created);
        }

        // PUT: api/Products/5
        [HttpPut]
        public void ModifyProduct([FromUri] int id, [FromBody] Product product)
        {
            products[id] = product;
        }

        // DELETE: api/Products/5
        [HttpDelete]
        public void RemoveProduct(int id)
        {
            products.RemoveAt(id);
        }

    }
}


第二種.或是採用預設的固定HttpVerbs名稱Get,Post,Put,Delete
在上方加上對應含意[ActionName("自訂")]也可以
[ActionName("SelectAll")]
[ActionName("SelectByID")]
[ActionName("Add")]
[ActionName("Modify")]
[ActionName("Remove")]

第二種方式的程式碼

在WebApiConfig 調整路由
將原本的
routeTemplate: "api/{controller}/{id}"
改為
routeTemplate: "api/{controller}/{action}/{id}"


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;

namespace MyWebAPI2_Test0
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API 設定和服務

            // Web API 路由
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{action}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }
    }
}


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
using MyWebAPI2_Test0.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;

namespace MyWebAPI2_Test0.Controllers
{
    public class ProductsController : ApiController
    {
        static List<Product> products = new List<Product>()
        {
            new Product(){Id = 0,ProductName = "Apple",Price = 20},
            new Product(){Id = 1,ProductName = "Banana",Price = 40},
            new Product(){Id = 2,ProductName = "Orange",Price = 60}
        };

        // GET: api/products/SelectAll
        [ActionName("SelectAll")]
        public IHttpActionResult Get()
        {
            return Ok(products);
        }

        // GET: api/products/SelectByID/1
        [ActionName("SelectByID")]
        public Product Get(int id)
        {
            return products[id];
        }

        // POST: api/products/Add
        [ActionName("Add")]
        public HttpResponseMessage Post([FromBody] Product product)
        {
            products.Add(product);
            return new HttpResponseMessage(HttpStatusCode.Created);
        }

        // PUT: api/products/Modify/2
        [ActionName("Modify")]
        public void Put([FromUri] int id, [FromBody] Product product)
        {
            products[id] = product;
        }

        // DELETE: api/products/Remove/2
        [ActionName("Remove")]
        public void Delete(int id)
        {
            products.RemoveAt(id);
        }

    }
}


一般第一種.可能較常見


FromBody,FromUri用法

以Put方法為例就有分為從Url傳參跟從FormBody傳參兩種形式
這時要區分就建議加上這兩種修飾




假設今天你想將資料全透過URL GET參數傳送
比方編輯更新
這時將Product DTO用[FromUri]標記修飾
原先的URL請求從
http://localhost:59877/api/products/1

改為
http://localhost:59877/api/products?id=1&ProductName=Cherry&Price=50

















Ref:
Difference between the Accept and Content-Type HTTP headers

Custom Method Names in Web API

How to Post a Custom Named Method in Web Api ?





留言

  1. 您好:
    參考您的說明,目前GET與POST皆可正常執行(中斷點有進去),但put卻出現錯誤,中斷點也沒進去,請問這要如何除錯? 謝謝!

    此錯誤表示伺服器上不存在此檔案或目錄。請建立檔案或目錄,然後再次嘗試要求。
    IIS70Error=404

    回覆刪除

張貼留言

這個網誌中的熱門文章

經得起原始碼資安弱點掃描的程式設計習慣培養(五)_Missing HSTS Header

經得起原始碼資安弱點掃描的程式設計習慣培養(三)_7.Cross Site Scripting(XSS)_Stored XSS_Reflected XSS All Clients

(2021年度)駕訓學科筆試準備題庫歸納分析_法規是非題