用ASP.NET Core 2.0 建立规范的 REST API -- 预备知识
什么是REST
REST 是 Representational State Transfer 的縮寫. 它是一種架構(gòu)的風(fēng)格, 這種風(fēng)格基于一套預(yù)定義的規(guī)則, 這些規(guī)則描述了網(wǎng)絡(luò)資源是如何定義和尋址的.
一個(gè)實(shí)現(xiàn)了REST這些規(guī)則的服務(wù)就叫做RESTful的服務(wù).
最早是由Roy Fielding提出的.
RPC 風(fēng)格
/getUsers/getUser?id=1/createUser/deleteUser?id=4/updateUser?name=dave?
上面這些節(jié)點(diǎn)是針對(duì)User的CRUD操作.?
這種樣式風(fēng)格的web服務(wù)更傾向于叫做RPC風(fēng)格的服務(wù).
在RPC的世界里, 節(jié)點(diǎn)僅僅就是可以在遠(yuǎn)程被觸發(fā)的函數(shù), 而在REST的世界里, 節(jié)點(diǎn)就是實(shí)體, 也叫做資源.
REST的原則/約束
REST有6大原則/約束, 每一個(gè)原則都是對(duì)API有正面或負(fù)面影響的設(shè)計(jì)決定.
RESTful API 最關(guān)心的有這幾方面:?性能, 可擴(kuò)展性, 簡(jiǎn)潔性, 互操作性, 通訊可見性, 組件便攜性和可靠性.
這些方面被封裝在REST的6個(gè)原則里, 它們是:?
1.?客服端-服務(wù)端約束: 客戶端和服務(wù)端是分離的, 它們可以獨(dú)自的進(jìn)化.
2.?無(wú)狀態(tài): 客戶端和服務(wù)段的通信必須是無(wú)狀態(tài)的, 狀態(tài)應(yīng)包含在請(qǐng)求里的. 也就是說(shuō)請(qǐng)求里要包含服務(wù)端需要的所有的信息, 以便服務(wù)端可以理解請(qǐng)求并可以創(chuàng)造上下文.
3.?分層系統(tǒng): 就像其它的軟件架構(gòu)一樣, REST也需要分層結(jié)構(gòu), 但是不允許某層直接訪問(wèn)不相鄰的層.?
4.?統(tǒng)一接口: 這里分為4點(diǎn), 他們是: 資源標(biāo)識(shí)符(URI), 資源的操作(也就是方法Method, HTTP動(dòng)詞), 自描述的響應(yīng)(可以認(rèn)為是媒體類型Media-Type), 以及狀態(tài)管理(超媒體作為應(yīng)用狀態(tài)的引擎 HATEOAS, Hypermedia as the Engine of Application State).
5.?緩存: 緩存約束派生于無(wú)狀態(tài)約束, 它要求從服務(wù)端返回的響應(yīng)必須明確表明是可緩存的還是不可緩存的.
6.?按需編碼: 這允許客戶端可以從服務(wù)端訪問(wèn)特定的資源而無(wú)須知曉如何處理它們. 服務(wù)端可以擴(kuò)展或自定義客戶端的功能.
只有滿足了這6個(gè)原則的系統(tǒng)才可以真正稱得上是RESTful的, 其實(shí)大部分系統(tǒng)的RESTful API并不是RESTful的, 但這樣并不代表這些API就不好, 利弊需要開發(fā)人員去衡量.
Richardson 成熟度模型
Richardson 成熟度模型代表著你的API是否足夠成熟, 分為4個(gè)級(jí)別, 0代表最差, 3代表最好.
0級(jí), 天花沼澤:
這里HTTP協(xié)議只是被用來(lái)進(jìn)行遠(yuǎn)程交互, 協(xié)議的其余部分都用錯(cuò)了, 都是RPC風(fēng)格的實(shí)現(xiàn)(例如SOAP, 尤其是使用WCF的時(shí)候).
例如:
POST (查詢數(shù)據(jù)信息) http://host/myapiPOST (創(chuàng)建數(shù)據(jù)) http://host/myapi?
1級(jí), 資源:
這級(jí)里, 每個(gè)資源都映射到一個(gè)URI上了, 但是HTTP方法并沒(méi)有正確的使用, 結(jié)果的復(fù)雜度不算太高.
例如這兩個(gè)查詢:
POST http://host/api/authorsPOST http://host/api/authors/{id}?
2級(jí), 動(dòng)詞:
正確使用了HTTP動(dòng)詞, 狀態(tài)碼也正確的使用了, 同時(shí)也去掉了不必要的變種.
例如:
GET http://host/api/authors200 Ok (authors) POST (author representation) http://host/api/authors201 Created (author)3級(jí), 超媒體:
API支持超媒體作為應(yīng)用狀態(tài)的引擎?HATEOAS, Hypermedia as the Engine of Application State, 引入了可發(fā)現(xiàn)性.
例如:
GET http://host/api/authors200 Ok (返回了authors 和 驅(qū)動(dòng)應(yīng)用程序的超鏈接)?
介紹ASP.NET Core
略.
但是, 你需要知道以下概念: .NET Core, .NET Standard.
還需要會(huì)使用下列工具: .NET Core CLI, Visual Studio 2017/Visual Studio Code/Visual Studio for Mac
ASP.NET Core 支持創(chuàng)建Web API, 但并不是直接支持RESTful的 Web API.
?
ASP.NET Core的基本知識(shí)
這部分還是需要簡(jiǎn)單的介紹下, 如果已經(jīng)會(huì)了, 請(qǐng)略過(guò)本文其余部分.
創(chuàng)建ASP.NET Core項(xiàng)目
打開VS2017, 選擇ASP.NET Core Web Application項(xiàng)目模板, 寫好名字, OK.
?
選擇空模板, OK:
?
項(xiàng)目建立好了, 結(jié)果如下:
然后我們看一下項(xiàng)目文件, 右鍵編輯MyRestful.Api:
這里, SDK屬性表示了我們使用的是哪個(gè)SDK, 而目標(biāo)框架是.NET Core 2.0.
(提示: 如果需要指向多個(gè)目標(biāo)框架的話可以使用TargetFrameworks元素, 注意多了個(gè)s)
?
看一下Program.cs:
Main方法是程序的入口. 而Web的宿主是通過(guò)BuildWebHost函數(shù)來(lái)實(shí)例化的, 它調(diào)用了WebHost.CreateDefaultBuilder方法, 很明顯這是一個(gè)建造者模式, 它最終會(huì)構(gòu)建出一個(gè)web宿主.
調(diào)用WebHost.CreateDefaultBuilder會(huì)返回一個(gè)IWebHostBuilder, 它允許我們進(jìn)行一些配置動(dòng)作.
程序啟動(dòng)
UseStartup方法會(huì)注冊(cè)一個(gè)類, 這個(gè)類負(fù)責(zé)配置整個(gè)程序的啟動(dòng)過(guò)程. 這里默認(rèn)用的是Startup類.
Startup類有兩個(gè)方法 ConfigureServices (這個(gè)可以沒(méi)有) 和 Configure (這個(gè)必須有):
在Configure方法里, 配置應(yīng)該遵循Add/Use的風(fēng)格樣式, 首先定義需要什么, 然后定義如何使用它.
而在ConfigureServices方法里, 所有程序級(jí)的依賴項(xiàng)都可以在這里注冊(cè)到默認(rèn)的IoC容器里, 把它們添加到IServiceCollection即可.
Configure方法才是真正負(fù)責(zé)配置HTTP請(qǐng)求管道的方法, 并且運(yùn)行時(shí)也需要它.
IApplicationBuilder的擴(kuò)展方法Run會(huì)傳遞一個(gè)RequestDelegate, 其內(nèi)部功能就是回寫Hello World.
?
ASP.NET Core還允許我們按約定為指定環(huán)境建立單獨(dú)的啟動(dòng)配置. 啟動(dòng)類可以通過(guò)這個(gè)函數(shù)定義UseStartup(startupAssemblyName: xxx); 運(yùn)行時(shí)會(huì)在這個(gè)指定的組件查找叫做Startup, Startup[環(huán)境名]的類, 其中[環(huán)境名]就是ASPNETCORE_ENVIRONMENT這個(gè)環(huán)境變量的值. 如果能找到指定環(huán)境的類, 那么它將覆蓋默認(rèn)的啟動(dòng)類.?
例如 環(huán)境變量值如果是Developmen的話, 那么運(yùn)行時(shí)就會(huì)嘗試尋找Startup和StartupDevelopment類, 該約定在啟動(dòng)類里面的方法名上也有效, 環(huán)境特定的啟動(dòng)類里的兩個(gè)方法分別是 Configure[環(huán)境名]和Configure[環(huán)境名]Services.
?
除了之前講的Run方法外, IApplicationBuilder還有一個(gè)Use擴(kuò)展方法.
Use擴(kuò)展方法接受RequestDelegate作為參數(shù)來(lái)提供HttpContext, 同時(shí)接受也為下一層準(zhǔn)備的RequestDelegate參數(shù).
需要注意的是, Run方法和Use方法定義的順序非常重要, 運(yùn)行時(shí)將會(huì)精確的按照創(chuàng)建的順序來(lái)執(zhí)行.
?
服務(wù)器
ASP.NET Core 服務(wù)器的作用是響應(yīng)客戶端發(fā)過(guò)來(lái)的請(qǐng)求, 這些請(qǐng)求會(huì)作為HttpContext傳遞進(jìn)來(lái). ASP.NET Core 內(nèi)置兩種服務(wù)器:
Kestrel, 它是跨平臺(tái)的服務(wù)器, 基于Libuv.
HTTP.sys, 它是僅限Windows系統(tǒng)的服務(wù)器, 基于HTTP.sys內(nèi)核驅(qū)動(dòng).
下面就是從客戶端發(fā)請(qǐng)求到應(yīng)用程序的流圖:
其中Kestrel可以作為一個(gè)獨(dú)立進(jìn)程自行托管, 也可以在IIS里. 但是還是建議使用IIS或Nginx等作為反向代理服務(wù)器. 在構(gòu)建API或微服務(wù)時(shí), 這些服務(wù)器可以作為網(wǎng)關(guān)使用, 因?yàn)樗鼈儠?huì)限制對(duì)外暴露的東西也可以更好的與現(xiàn)有系統(tǒng)集成, 所以它們會(huì)提供額外的防御層,?
使用反向代理服務(wù)器(IIS)之后的流圖如下:
讓web宿主工作于IIS之后需要使用IWebHostBuilder的UseIISIntegration這個(gè)擴(kuò)展方法.
除了內(nèi)置的兩種服務(wù)器, 您還可以使用自定義的服務(wù)器, 使用IWebHostBuilder的UserServer擴(kuò)展方法, 它接受一個(gè)實(shí)現(xiàn)了IServer接口的實(shí)例, 您的自定義服務(wù)器需要實(shí)現(xiàn)該接口. 這里就不講了.
?
中間件
在應(yīng)用程序請(qǐng)求管道內(nèi)裝配的組件就是中間件, 它們負(fù)責(zé)處理通過(guò)管道的請(qǐng)求和響應(yīng).
在HTTP請(qǐng)求管道的上下文里, 中間件可以叫做請(qǐng)求委托, 它們是由Run, Map 和 Use 擴(kuò)展方法共同組建而成的.
每個(gè)中間件可以在它被調(diào)用之前和之后執(zhí)行可選的邏輯, 同時(shí)也可以決定該請(qǐng)求是否可以被送到管道的下一個(gè)中間件那里.
請(qǐng)求在中間件里的流圖如下:
看一下這個(gè)例子:
如果我在瀏覽器地址輸入?http://localhost:5000/return, 那么結(jié)果就是Returned!
如果輸入 http://localhost:5000/end, 那么是The End.
如果輸入?http://localhost:5000/xxx?value=1234, 結(jié)果是 the number is 1234
如果輸入?http://localhost:5000/xxx?value=abcde, 結(jié)果是?Hello, the value is abcde!
?
注意:?應(yīng)用程序管道里的請(qǐng)求委托(中間件)定義的順序是非常重要的, 請(qǐng)求的時(shí)候按定義的順序執(zhí)行, 而響應(yīng)的順序正好相反.
?
中間件最好不要像上面一樣寫在Startup類里, 每個(gè)中間件應(yīng)該放在單獨(dú)的類里.?
我把上例中檢查是否為數(shù)字的中間件寫在一個(gè)單獨(dú)的類里:
這種中間件沒(méi)有實(shí)現(xiàn)特定的接口或者繼承特定類, 它更像是Duck Typing (你走起路來(lái)像個(gè)鴨子, 叫起來(lái)像個(gè)鴨子, 那么你就是個(gè)鴨子).
然后在Startup的Configure方法里調(diào)用app.UseMiddleware<NumberMiddleware>()即可:
?
路由
在ASP.NET Core里,使用路由中間件RouterMiddleware來(lái)處理路由.
想要使用路由, 同樣也是遵循 Add/Use 這個(gè)模式.?
首先在ConfigureServices方法里添加(Add):
然后在Configure方法里使用(Use):
UseRouter這個(gè)擴(kuò)展方法可以接受IRouter或者Action<IRouterBuilder>作為參數(shù).
例如:
當(dāng)發(fā)送?http://localhost:5000/ GET請(qǐng)求的時(shí)候, 返回 Default route.
當(dāng) GET http://localhost:5000/user/dave的時(shí)候, 返回 Hi dave
當(dāng) POST?http://localhost:5000/user/dave的時(shí)候, 返回 Hi, posted name is dave
其中{name}, 是名為name的參數(shù).
如果寫成"user/{name}/{age:number}", 那么age這個(gè)參數(shù)的必須可以被解析為數(shù)值型.
而"user/{name}/{gender?}", 這里的gender參數(shù)可以沒(méi)有.
?
Controller
HTTP請(qǐng)求通過(guò)管道最終到達(dá)Action并返回的流圖如下:
默認(rèn)情況下Controller放在ASP.NET Core項(xiàng)目的Controllers目錄下。
在ASP.NET Core項(xiàng)目里可以通過(guò)多種方式來(lái)創(chuàng)建Controller,當(dāng)然最建議的方式還是通過(guò)繼承AspNetCore.Mvc.Controller這個(gè)抽象類來(lái)建立Controller。
例如:
上例中類名可以不是以Controller結(jié)尾。
?
還有其它的方式創(chuàng)建Controller,按約定類名以Controller結(jié)尾的POCO類也會(huì)被認(rèn)為是Controller,例如:
?
針對(duì)POCO類, 即使名稱不是以Controller結(jié)尾,仍然可以把它作為Controller,這就需要在類上面添加?[Controller]?這個(gè)屬性:
?
如果某個(gè)類的名字以Controller結(jié)尾, 但是你不想把它當(dāng)作Controller,那么就應(yīng)該為該類標(biāo)注?[NonController]?這個(gè)屬性:
?
實(shí)際上, 看源碼就可以知道 Controller 繼承于 ControllerBase:
?
?而ControllerBase上面標(biāo)注著?[Controller]?屬性。
?
Action
在Controller里面,可以使用public修飾符來(lái)定義Action,通常會(huì)帶有參數(shù),可以返回任何類型,但是大多數(shù)情況下應(yīng)該返回IActionResult。Action的方法名要么是以HTTP的動(dòng)詞開頭,要么是使用HTTP動(dòng)詞屬性標(biāo)簽,包括:[HttpGet], [HttpPut], [HttpPost], [HttpDelete], [HttpHead], [HttpOptions], [HttpPatch].
例如:
其中某個(gè)方法名如果恰好是以HTTP的動(dòng)詞開頭,那么可以通過(guò)標(biāo)注?[NonAction]?屬性來(lái)表示這個(gè)方法不是Action。
通過(guò)繼承Controller基類的方法來(lái)創(chuàng)建Controller還是有很多好處的,因?yàn)樗峁┝撕芏鄮椭椒?#xff0c;例如:Ok, NotFound, BadRequest等,它們分別對(duì)應(yīng)HTTP的狀態(tài)碼 200, 404, 400;此外還有Redirect,LocalRedirect,RedirectToRoute,Json,File,Content等方法。
?
為MVC定義路由有兩種方式:使用IRouteBuilder或者使用基于屬性標(biāo)簽的路由。針對(duì)Rest,最好還是使用基于屬性標(biāo)簽的方式。
路由屬性標(biāo)簽可以標(biāo)注在Controller或者Action方法上,例如:
Controller類上標(biāo)注的路由“api/[controller]”,其中[controller] 就代表該類的名字去掉結(jié)尾Controller的部分,也就是“api/person”。
在Controller上使用[Route]屬性就定義了該Controller下所有Action的路由基地址,每個(gè)Action可以包含一個(gè)或者多個(gè)相對(duì)的路由模板(地址),這些路由模板可以在[Http...]中定義。但是如果使用?~?這個(gè)符號(hào)的話,該Action的地址將會(huì)是絕對(duì)路由地址,也就是覆蓋了Controller定義的基路由。
?
實(shí)體綁定
傳入的請(qǐng)求會(huì)映射到Action方法的參數(shù),可以實(shí)原始數(shù)據(jù)類型也可以是復(fù)雜的類型例如Dto(data transfer object)或ViewModel。這個(gè)把Http請(qǐng)求綁定到參數(shù)的過(guò)程叫做實(shí)體綁定。
例如:
?
其中id參數(shù)是定義在路由里的,而name參數(shù)在路由里沒(méi)有,但是仍然可以從查詢參數(shù)中把name參數(shù)映射出來(lái)。
注意路由參數(shù)和查詢參數(shù)的區(qū)別,下面這個(gè)URL里val1和val2是查詢參數(shù),它們是在url的后邊使用?和&分隔:
/product?val1=2&val2=10?
而針對(duì)上面的Action,下面這個(gè)URL的路由參數(shù)id就是123:
/api/first/123?
?
針對(duì)下面這個(gè)POST Action:
我們可以通過(guò)幾種方式為其傳遞類型為Person的參數(shù)。
可以使用查詢參數(shù):/api/people?id=1&name=Dave
如果POST Json數(shù)據(jù):
那么在Action里面得到的參數(shù)person的屬性值都是null。這是因?yàn)檫@樣的原始數(shù)據(jù)是包含在請(qǐng)求的Body里面,為了解決這個(gè)問(wèn)題,你需要告訴Action從哪里獲取參數(shù),針對(duì)這個(gè)例子就應(yīng)該使用?[FromBody]?屬性標(biāo)簽:
如果提交的是表單數(shù)據(jù),那么就應(yīng)該使用[FromForm]:
其它的出處還有?[FromHeader], [FromRoute], [FromServices]等。
再看一個(gè)FromHeader的例子:
?
如果使用復(fù)雜類型Person來(lái)獲取person參數(shù)好像不行,只能使用原始類型的吧?
?
實(shí)體驗(yàn)證
ASP.NET Core內(nèi)置的實(shí)體驗(yàn)證是通過(guò)驗(yàn)證屬性標(biāo)簽來(lái)實(shí)現(xiàn)的,大多數(shù)情況下這樣會(huì)很方便。
例如:
其中Display不是驗(yàn)證標(biāo)簽,但是通過(guò)它可以自定義屬性的顯式名稱,在其它錯(cuò)誤信息里可以使用{0}來(lái)引用該名稱。
?
判斷實(shí)體參數(shù)是否符合要求,可以檢查ModelState.IsValid屬性,這個(gè)屬性也是由ControllerBase提供的,例如:
發(fā)送一個(gè)請(qǐng)求:
這是個(gè)不合理的參數(shù),返回的是400 BadRequest,帶著驗(yàn)證結(jié)果:
?
盡管大多數(shù)情況西,驗(yàn)證屬性標(biāo)簽都滿足要求,但是有時(shí)候還是需要進(jìn)行一些靈活的驗(yàn)證,你可以使用像FluentValidation這樣的第三方庫(kù),也可以使用內(nèi)置的方式來(lái)實(shí)現(xiàn)自定義驗(yàn)證。
ASP.NET Core內(nèi)置支持兩種方式來(lái)進(jìn)行自定義驗(yàn)證:通過(guò)繼承ValidationAttribute來(lái)創(chuàng)建自定義驗(yàn)證屬性標(biāo)簽,或者讓實(shí)體實(shí)現(xiàn)IValidatebleObject接口。
使用自定義驗(yàn)證屬性標(biāo)簽:
把該標(biāo)簽放到name屬性上
使用剛才的請(qǐng)求,其結(jié)果是:
?
另一種方式,在Person類實(shí)現(xiàn)IValidatableObject接口
但是我使用這種方法并不好用,不知道我哪里用錯(cuò)了!
?
過(guò)濾器
和中間件一樣,ASP.NET Core MVC的過(guò)濾器也可以在請(qǐng)求管道的特定階段的之前或之后執(zhí)行某些代碼。過(guò)濾器還可以有子管道,子管道里面包含著其它過(guò)濾器。
過(guò)濾器和中間件的區(qū)別:中間件是應(yīng)用程序級(jí)別的,它可以處理每個(gè)發(fā)送過(guò)來(lái)的請(qǐng)求;而過(guò)濾器是針對(duì)MVC的,它只會(huì)處理發(fā)往MVC的請(qǐng)求。
ASP.NET Core MVC的過(guò)濾器分為5類:
授權(quán)過(guò)濾器,它是第一個(gè)運(yùn)行的,它的作用就是判斷HTTP Context中的用戶是否擁有當(dāng)前請(qǐng)求的權(quán)限,如果用戶沒(méi)有權(quán)限,那么它就會(huì)“短路”管道。
資源過(guò)濾器,在授權(quán)過(guò)濾器后運(yùn)行,在管道其它動(dòng)作之前,和管道動(dòng)作都結(jié)束后運(yùn)行。它可以實(shí)現(xiàn)緩存或由于性能原因執(zhí)行短路操作。它在實(shí)體綁定之前運(yùn)行,所以它也可以對(duì)影響實(shí)體綁定。
Action過(guò)濾器,它在Action方法調(diào)用之前和之后立即執(zhí)行,它可以操作傳進(jìn)Action的參數(shù)和返回的結(jié)果。
異常過(guò)濾器,針對(duì)在寫入響應(yīng)Body之前發(fā)生的未處理的異常,它可以應(yīng)用全局的策略,
結(jié)果過(guò)濾器,它可以在每個(gè)Action結(jié)果執(zhí)行之前和之后運(yùn)行代碼,但也只是在Action方法無(wú)錯(cuò)誤的成功完成后才可以執(zhí)行。
下圖標(biāo)明了這些過(guò)濾器在管道中是如何交互的:
過(guò)濾器可以作為屬性標(biāo)簽使用,或者也可以在Startup類里面進(jìn)行全局注冊(cè)。
例子:
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Filters;
namespace MyRestful.Api.Filters
{
? ? public class DefaultNameFilter: IActionFilter, IAsyncActionFilter
? ? {
? ? ? ? public void OnActionExecuting(ActionExecutingContext context)
? ? ? ? {
? ? ? ? ? ? context.ActionDescriptor.RouteValues["name"] = "Anonymous";
? ? ? ? }
? ? ? ? public void OnActionExecuted(ActionExecutedContext context)
? ? ? ? {
? ? ? ? ? ? context.HttpContext.Response.Headers["X-Name"] = context.ActionDescriptor.RouteValues["name"];
? ? ? ? }
? ? ? ? public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
? ? ? ? {
? ? ? ? ? ? OnActionExecuting(context);
? ? ? ? ? ? var result = await next();
? ? ? ? ? ? OnActionExecuted(result);
? ? ? ? }
? ? }
}
全局注冊(cè),在Startup里:
public void ConfigureServices(IServiceCollection services)
? ? ? ? {
? ? ? ? ? ? services.AddMvc(options =>
? ? ? ? ? ? {
? ? ? ? ? ? ? ? options.Filters.Add<DefaultNameFilter>();
? ? ? ? ? ? });
? ? ? ? }
或者自定義一個(gè)屬性標(biāo)簽,內(nèi)部的代碼是一樣的:
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Filters;
namespace MyRestful.Api.Filters
{
? ? public class DefaultUserNameFilterAttribute: Attribute, IActionFilter, IAsyncActionFilter
? ? {
? ? ? ? public void OnActionExecuting(ActionExecutingContext context)
? ? ? ? {
? ? ? ? ? ? context.ActionDescriptor.RouteValues["name"] = "Anonymous";
? ? ? ? }
? ? ? ? public void OnActionExecuted(ActionExecutedContext context)
? ? ? ? {
? ? ? ? ? ? context.HttpContext.Response.Headers["X-Name"] = context.ActionDescriptor.RouteValues["name"];
? ? ? ? }
? ? ? ? public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
? ? ? ? {
? ? ? ? ? ? OnActionExecuting(context);
? ? ? ? ? ? var result = await next();
? ? ? ? ? ? OnActionExecuted(result);
? ? ? ? }
? ? }
}
然后把該標(biāo)簽用在Action方法上即可:
[DefaultUserNameFilter]
? ? ? ? [HttpGet("first/{id}")]
? ? ? ? public IActionResult FindFirstPerson(int id, string name)
? ? ? ? {
? ? ? ? ? ? return null;
? ? ? ? }
?
格式化響應(yīng)結(jié)果
Action的結(jié)果最好使用IActionResult, 但也可以使用其他類型,例如IEnumerable<T>等。強(qiáng)制結(jié)果輸出為特定的類型可以通過(guò)調(diào)用特定的方法來(lái)實(shí)現(xiàn),例如JsonResponse就是輸出JSON,ContentResponse就是輸出文本。另外也可以使用[Produces(xxx)] 這個(gè)過(guò)濾器,它可以應(yīng)用于全局,controller或者Action。
在REST服務(wù)里,有個(gè)詞叫內(nèi)容協(xié)商,它表示客戶端通過(guò)Accept Header里的media-type來(lái)指定所需的結(jié)果格式。
ASP.NET Core MVC 默認(rèn)實(shí)現(xiàn)并使用JSON格式化,但也支持其它格式,這需要在startup里面注冊(cè)。
客戶端瀏覽器可能在請(qǐng)求的Accept Headers里提供了多種的格式,但是ASP.NET Core MVC 默認(rèn)是忽略瀏覽器的Accept Header的,并使用標(biāo)準(zhǔn)的輸出格式。但是修改MvcOptions的RespectBrowserAcceptHeader值為true,可以改變這個(gè)行為:
ASP.NET Core還提供了 XML 格式,可以在MvcOptions里面添加:
?
今天先寫到這,還沒(méi)有切入正題。
原文地址: https://www.cnblogs.com/cgzl/p/9010978.html
.NET社區(qū)新聞,深度好文,歡迎訪問(wèn)公眾號(hào)文章匯總 http://www.csharpkit.com
總結(jié)
以上是生活随笔為你收集整理的用ASP.NET Core 2.0 建立规范的 REST API -- 预备知识的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 从Xamarin.Essentials谈
- 下一篇: 使用ML.NET预测纽约出租车费