ASP.NET Web Api第一條請求和路由

ASP.NET應(yīng)用程序的生命周期

  • ASP.NET程序的生命周期開始于用戶通過瀏覽器向Web服務(wù)器發(fā)送一個請求吗浩,ASP.NET是一個ISAPI Web 服務(wù)器的一個擴展,當服務(wù)器接收到一個請求,服務(wù)器會檢查被請求文件的擴展名邑时,通過這個擴展名決定該使用哪個ISAPI來處理該請求咐蝇,然后將該請求發(fā)送到相關(guān)的ISAPI擴展。
  • 當ASP.NET程序收到第一個請求時鸭栖,ApplicationManager會創(chuàng)建一個應(yīng)用域歌馍,應(yīng)用域?qū)⒉煌腁SP.NET和其全局變量隔離開來,在應(yīng)用域內(nèi)晕鹊,一個HostingEnvironment的實例被創(chuàng)建松却,該對象提供了應(yīng)用的一些信息,例如文件夾的名字等溅话。ASP.NET也會變異頂級的項目晓锻,包括位于App_Code文件夾下的代碼。
  • ASP.NET會創(chuàng)建并初始化核心的對象:HttpContext公荧、HttpRequest带射、HttpResponse,HttpContext包含了當前應(yīng)用請求和響應(yīng)的對象循狰。
  • 當所有的核心對象都已經(jīng)初始化窟社,ASP.NET通過創(chuàng)建一個HttpApplication對象開始,如果應(yīng)用包含一個Global.asax文件绪钥,ASP.NET會創(chuàng)建一個Global.asax類灿里,該類是派生自HttpApplication,并使用該派生類來呈現(xiàn)應(yīng)用程腹。(第一次請求時匣吊,ASP.NET會創(chuàng)建HttpApplication的實例,為了性能考慮,該實例有可能會被復(fù)用)同時配置模塊也會被創(chuàng)建色鸳。
  • HttpApplication類會執(zhí)行內(nèi)部的方法社痛,例如驗證等函數(shù)。

Global.asax文件

應(yīng)用的生命周期中命雀,應(yīng)用會產(chǎn)生一些事件蒜哀,開發(fā)者可以復(fù)寫這些特殊的事件和方法,為了處理應(yīng)用的事件吏砂,可以創(chuàng)建一個Global.asax的文件撵儿,位于應(yīng)用的根目錄。

如果創(chuàng)建了Global.asax狐血,ASP.NET會將其編譯成繼承自HttpApplication的類淀歇,然后用它來代表應(yīng)用。

每個HttpApplication同時只處理一個請求匈织,好處就是不需要對非static得變量上鎖浪默。

ASP.NET自動綁定應(yīng)用事件到Global.asax文件,通常事件名為Application_event缀匕,例如Application_BeginRequest.

注意Application_Start和Application_End方法比較特殊浴鸿,這兩個方法并不代表HttpApplication事件,ASP.NET在整個應(yīng)用域生命周期中只調(diào)用一次

Application_event:Application_Error可以在應(yīng)用的任何階段調(diào)用弦追,Application_EndRequest是唯一的事件每個請求都會調(diào)用

init:HttpApplication實例的所有模塊被創(chuàng)建時調(diào)用

Dispose:應(yīng)用實例被銷毀之前會被調(diào)用,用來釋放資源

Application_End:應(yīng)用域被卸載前調(diào)用


Action Results

Web api的控制器的返回值可以是如下類型:

  • void
  • HttpResponseMessage
  • IHttpActionResult
  • 其它類型

根據(jù)返回值類型的不同花竞,Web api使用不同的機制來返回HTTP響應(yīng)信息劲件。

返回值類型 Web api創(chuàng)建響應(yīng)方式
void 返回204(No content)
HttpResponseMessage 直接轉(zhuǎn)換成http響應(yīng)的消息
IHttpActionResult 調(diào)用 ExecuteAsync 創(chuàng)建一個 HttpResponseMessage, 然后轉(zhuǎn)換成一個Http 響應(yīng)消息
其它類型 直接將對象序列化,并將序列化的結(jié)果寫入body中约急,返回200(OK)

路由

ASP.NET 的 Web API零远,一個controller 被用來處理 HTTP 請求,Controller里的public方法被叫做action厌蔽,當Web API接收到一個請求時牵辣,會將請求路由到對應(yīng)的action。路由表位于App_Start\WebApiConfig.cs奴饮。

默認情況下纬向,路由表定義的模板為api/{controller}/{id},其中{}是占位符戴卜,當一個請求到達后逾条,會按照路由表指定的模板進行匹配,如果沒有任何一項能夠匹配成功投剥,那么就會報404錯誤师脂。如果存在匹配的模板,Web api將會抽取出{controller}的內(nèi)容,在其后加上Controller吃警,匹配對應(yīng)的controller糕篇,然后會根據(jù)http-method,選擇對應(yīng)的action酌心,例如如果method為Get拌消,會查找對應(yīng)的GetXXX方法,Post會查找對應(yīng)的PostXXX方法谒府,然后將{i}作為參數(shù)傳入方法拼坎。

如果不希望采用上述action的定位方式,可以通過annotation指定

  • [HttpGet]
  • [HttpPost]
  • [HttpPut]
  • [HttpDelete]
  • [HttpHead]
  • [HttpOptions]
  • [HttpPatch]

或者:

  • [AcceptVerbs("GET", "POST")]等

也可以在定義路由模板時指定Action占位符:api/{controller}/{action}/{id}完疫,[ActionName("XXX")]形式的Annotation泰鸡,可以指定該方法可用來處理的action例如:

public class ProdctsController: ApiController {
    [HttpGet]
    [ActionName("Thumbnail")]
    public HttpResponseMessage GetThumbnailImage(int id);
}

路由為api/Products/Thumbnail/1時,會將請求定位到GetThumbnailImage壳鹤。
如果不希望一個公有的方法作為action盛龄,可以通過[NonAction]指定。

路由字典

當匹配到一個URI時芳誓,WebApi會創(chuàng)建一個字典余舶,用來保存占位符中的每個值,key是占位符的名字锹淌,value是值匿值,字典存儲于IHttpRouteData對象中。如果一個占位符有默認值赂摆,且默認值為RouteParameter.Optional挟憔,當URI沒有該占位符對應(yīng)的值時,這個值不會保存在字典中烟号。如果默認值定義的key-value不在路由模板中绊谭,那么該key-value會被存儲在字典中。

選擇一個Controller

控制器的選擇是通過IHttpControllerSelector.SelectController這個方法汪拥。該方法接收一個HttpRequestMessage實例达传,返回一個HttpControllerDescriptor對象,默認的實現(xiàn)是由DefaultHttpControllerSelector類實現(xiàn):

  1. 在路由字典中查找鍵controller對應(yīng)的值
  2. 將值取出迫筑,并且在該字符串的末尾追加字符串"Controller"
  3. 通過第二步拼接出來的字符串尋找對應(yīng)的控制器

如果找不到對應(yīng)的控制器宪赶,將會返回一個錯誤給客戶端

在第三步中,DefaultHttpControllerSelector使用IHttpControllerTypeResolver接口來獲取所有web控制器列表铣焊,默認情況下逊朽,IhttpControllerTypeResolver只返回公有的非抽象的且結(jié)尾為Controller的并且繼承自IHttpController的類。

選擇Action

控制器選擇完畢后曲伊,框架會調(diào)用IHttpActionSelector.SelectAction方法叽讳,這個方法需要HttpControllerContext作為參數(shù)追他,返回HttpActionDescriptor

默認通過ApiControllerActionSelector類實現(xiàn)岛蚤,當進行action選擇時邑狸,會考慮以下幾個因素:

  • 請求的http-method
  • 在路由模板中是否存在{action}
  • action的參數(shù)

控制器的哪些方法可以被當做action:

  1. 必須是public訪問權(quán)限
  2. 沒有特殊的修飾(構(gòu)造函數(shù)、events涤妒、overload等修飾符)
  3. 方法不能繼承自ApiController

框架指選擇以下的方法作為有效的Action:

  1. 由Annotation([AcceptVerbs]单雾、[HttpGet]、[HttpPost]等)標記的方法她紫。
  2. 由Get硅堆、Post、Put等開頭的方法贿讹。
  3. 不滿足1渐逃、2時,該方法只支持POST請求民褂。

參數(shù)綁定規(guī)則:

  • 簡單類型從URI獲取
  • 復(fù)雜類型從request body獲取

選擇Action流程:

  1. 創(chuàng)建一個滿足于該http-method的action列表
  2. 如果路由字典中有鍵action茄菊,從列表移除哪些不滿足該值得action
  3. 匹配參數(shù)
    • 從URI中獲取參數(shù)列表
    • 對每一個action,將action中的參數(shù)與參數(shù)列表匹配
    • 選擇參數(shù)互相匹配的action
    • 如果有多個action滿足赊堪,選擇參數(shù)匹配最多的
  4. 忽略有[NonAction]標記的屬性

如果沒有找到對應(yīng)的參數(shù)面殖,并且該參數(shù)沒有被標記optional,將報錯哭廉。

屬性Route

Web API引入的Route屬性(Annotation)脊僚,語法格式如下:

[Route("customers/{customerId}/orders")]
public IEnumerable<Order> GetOrdersByCustomer(int customerId) { ... }

如果想啟用Route屬性,需要在WebApiConfig.cs文件中添加如下代碼:

config.MapHttpAttributeRoutes();

Route屬性可以與路由模板一同使用遵绰。

Route屬性可以與http-method結(jié)合使用:

public class OrdersController: ApiController {
    [Route("api/v2/{say}")]
    [HttpGet]//或者[AcceptVerbs("Get")]
    public HttpResponseMessage say(int say) { ... }
}

只有通過get方式才能夠訪問吃挑。Route屬性的參數(shù)也可以指定多個。

Route屬性可以指定前綴:

[RoutePrefix("api/books")]
public class BooksController: ApiController {
    [Route("")] //api/books
    public HttpResponseMessage index() {....}

    [Route("{id:int}")]//api/books/id
    public HttpResponseMessage indexs(int id) { ... }

    [Route("~/v1/book")]// ~取消前綴街立,/v1/book
    public HttpResponseMessage v1Books() {....}
}

Route類型限制

[Route("users/{id:int}")]
public User GetUserById(int id) { ... }

Route 可以自定義限制類型:

自定義路由參數(shù)限制類型可以繼承自IHttpRouteConstraint,并且覆寫Match方法埠通。然后在WebApiConfig.cs中的Register方法中注冊該限制:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        var constraintResolver = new DefaultInlineConstraintResolver();
        constraintResolver.ConstraintMap.Add("nonzero", typeof(NonZeroConstraint));

        config.MapHttpAttributeRoutes(constraintResolver);
    }
}

在指定的Route中使用該規(guī)則:

[Route("{id:nonzero}")]
...

當需要Route指定默認值時有兩種方式:

public class BooksController : ApiController
{
    [Route("api/books/locale/{lcid:int?}")]
    public IEnumerable<Book> GetBooksByLocale(int lcid = 1033) { ... }
}
public class BooksController : ApiController
{
    [Route("api/books/locale/{lcid:int=1033}")]
    public IEnumerable<Book> GetBooksByLocale(int lcid) { ... }
}

兩種方式結(jié)果一樣赎离,但是流程和效率不一樣。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末端辱,一起剝皮案震驚了整個濱河市梁剔,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌舞蔽,老刑警劉巖荣病,帶你破解...
    沈念sama閱讀 216,997評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異渗柿,居然都是意外死亡个盆,警方通過查閱死者的電腦和手機脖岛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來颊亮,“玉大人柴梆,你說我怎么就攤上這事≈栈螅” “怎么了绍在?”我有些...
    開封第一講書人閱讀 163,359評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長雹有。 經(jīng)常有香客問我偿渡,道長,這世上最難降的妖魔是什么霸奕? 我笑而不...
    開封第一講書人閱讀 58,309評論 1 292
  • 正文 為了忘掉前任溜宽,我火速辦了婚禮,結(jié)果婚禮上铅祸,老公的妹妹穿的比我還像新娘次询。我一直安慰自己磕昼,他們只是感情好,可當我...
    茶點故事閱讀 67,346評論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著宿百,像睡著了一般。 火紅的嫁衣襯著肌膚如雪器瘪。 梳的紋絲不亂的頭發(fā)上跃巡,一...
    開封第一講書人閱讀 51,258評論 1 300
  • 那天,我揣著相機與錄音什猖,去河邊找鬼票彪。 笑死,一個胖子當著我的面吹牛不狮,可吹牛的內(nèi)容都是我干的降铸。 我是一名探鬼主播,決...
    沈念sama閱讀 40,122評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼摇零,長吁一口氣:“原來是場噩夢啊……” “哼推掸!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起驻仅,我...
    開封第一講書人閱讀 38,970評論 0 275
  • 序言:老撾萬榮一對情侶失蹤谅畅,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后噪服,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體毡泻,經(jīng)...
    沈念sama閱讀 45,403評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,596評論 3 334
  • 正文 我和宋清朗相戀三年粘优,在試婚紗的時候發(fā)現(xiàn)自己被綠了仇味。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片呻顽。...
    茶點故事閱讀 39,769評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖邪铲,靈堂內(nèi)的尸體忽然破棺而出芬位,到底是詐尸還是另有隱情,我是刑警寧澤带到,帶...
    沈念sama閱讀 35,464評論 5 344
  • 正文 年R本政府宣布昧碉,位于F島的核電站,受9級特大地震影響揽惹,放射性物質(zhì)發(fā)生泄漏被饿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,075評論 3 327
  • 文/蒙蒙 一搪搏、第九天 我趴在偏房一處隱蔽的房頂上張望狭握。 院中可真熱鬧,春花似錦疯溺、人聲如沸论颅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽恃疯。三九已至,卻和暖如春墨闲,著一層夾襖步出監(jiān)牢的瞬間今妄,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評論 1 269
  • 我被黑心中介騙來泰國打工鸳碧, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留盾鳞,地道東北人。 一個月前我還...
    沈念sama閱讀 47,831評論 2 370
  • 正文 我出身青樓瞻离,卻偏偏與公主長得像腾仅,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子套利,可洞房花燭夜當晚...
    茶點故事閱讀 44,678評論 2 354

推薦閱讀更多精彩內(nèi)容