国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發(fā)設計 > 正文

WebApi 異常處理解決方案

2019-11-08 03:22:31
字體:
來源:轉載
供稿:網友

一、使用異常篩選器捕獲所有異常

我們知道,一般情況下,WebApi作為服務使用,每次客戶端發(fā)送http請求到我們的WebApi服務里面,服務端得到結果輸出response到客戶端。這個過程中,一旦服務端發(fā)生異常,會統(tǒng)一向客戶端返回500的錯誤。

        [HttpGet]        public string GetAllChargingData([FromUri]TB_CHARGING obj)        {            throw new NotImplementedException("方法不被支持");        }

我們來看看http請求

而有些時候,我們客戶端需要得到更加精確的錯誤碼來判斷異常類型,怎么辦呢?

記得在介紹AOP的時候,我們介紹過MVC里面的IExceptionFilter接口,這個接口用于定義異常篩選器所需的方法,在WebApi里面,也有這么一個異常篩選器,下面我們通過一個實例來看看具體如何實現(xiàn)。

首先在App_Start里面新建一個類WebApiExceptionFilterAttribute.cs,繼承ExceptionFilterAttribute,重寫OnException方法

復制代碼
    public class WebApiExceptionFilterAttribute : ExceptionFilterAttribute     {        //重寫基類的異常處理方法        public override void OnException(HttpActionExecutedContext actionExecutedContext)        {            //1.異常日志記錄(正式項目里面一般是用log4net記錄異常日志)            Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "——" +                actionExecutedContext.Exception.GetType().ToString() + ":" + actionExecutedContext.Exception.Message + "——堆棧信息:" +                actionExecutedContext.Exception.StackTrace);            //2.返回調用方具體的異常信息            if (actionExecutedContext.Exception is NotImplementedException)            {                actionExecutedContext.Response = new HttPResponseMessage(HttpStatusCode.NotImplemented);            }            else if (actionExecutedContext.Exception is TimeoutException)            {                actionExecutedContext.Response = new HttpResponseMessage(HttpStatusCode.RequestTimeout);            }            //.....這里可以根據項目需要返回到客戶端特定的狀態(tài)碼。如果找不到相應的異常,統(tǒng)一返回服務端錯誤500            else            {                actionExecutedContext.Response = new HttpResponseMessage(HttpStatusCode.InternalServerError);            }            base.OnException(actionExecutedContext);        }    }復制代碼

 代碼解析:通過判斷異常的具體類型,向客戶端返回不同的http狀態(tài)碼,示例里面寫了兩個,可以根據項目的實際情況加一些特定的我們想要捕獲的異常,然后將對應的狀態(tài)碼寫入http請求的response里面,對于一些我們無法判斷類型的異常,統(tǒng)一返回服務端錯誤500。關于http的狀態(tài)碼,framework里面定義了一些常見的類型,我們大概看看:

復制代碼
#region 程序集 System.dll, v4.0.0.0// C:/Program Files (x86)/Reference Assemblies/Microsoft/Framework/.NETFramework/v4.5/System.dll#endregionusing System;namespace System.Net{    // 摘要:     //     包含為 HTTP 定義的狀態(tài)代碼的值。    public enum HttpStatusCode    {        // 摘要:         //     等效于 HTTP 狀態(tài) 100。 System.Net.HttpStatusCode.Continue 指示客戶端可能繼續(xù)其請求。        Continue = 100,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 101。 System.Net.HttpStatusCode.SwitchingProtocols 指示正在更改協(xié)議版本或協(xié)議。        SwitchingProtocols = 101,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 200。 System.Net.HttpStatusCode.OK 指示請求成功,且請求的信息包含在響應中。 這是最常接收的狀態(tài)代碼。        OK = 200,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 201。 System.Net.HttpStatusCode.Created 指示請求導致在響應被發(fā)送前創(chuàng)建新資源。        Created = 201,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 202。 System.Net.HttpStatusCode.Accepted 指示請求已被接受做進一步處理。        Accepted = 202,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 203。 System.Net.HttpStatusCode.NonAuthoritativeInformation 指示返回的元信息來自緩存副本而不是原始服務器,因此可能不正確。        NonAuthoritativeInformation = 203,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 204。 System.Net.HttpStatusCode.NoContent 指示已成功處理請求并且響應已被設定為無內容。        NoContent = 204,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 205。 System.Net.HttpStatusCode.ResetContent 指示客戶端應重置(或重新加載)當前資源。        ResetContent = 205,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 206。 System.Net.HttpStatusCode.PartialContent 指示響應是包括字節(jié)范圍的 GET        //     請求所請求的部分響應。        PartialContent = 206,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 300。 System.Net.HttpStatusCode.MultipleChoices 指示請求的信息有多種表示形式。        //     默認操作是將此狀態(tài)視為重定向,并遵循與此響應關聯(lián)的 Location 標頭的內容。        MultipleChoices = 300,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 300。 System.Net.HttpStatusCode.Ambiguous 指示請求的信息有多種表示形式。 默認操作是將此狀態(tài)視為重定向,并遵循與此響應關聯(lián)的        //     Location 標頭的內容。        Ambiguous = 300,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 301。 System.Net.HttpStatusCode.MovedPermanently 指示請求的信息已移到 Location        //     頭中指定的 URI 處。 接收到此狀態(tài)時的默認操作為遵循與響應關聯(lián)的 Location 頭。        MovedPermanently = 301,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 301。 System.Net.HttpStatusCode.Moved 指示請求的信息已移到 Location 頭中指定的        //     URI 處。 接收到此狀態(tài)時的默認操作為遵循與響應關聯(lián)的 Location 頭。 原始請求方法為 POST 時,重定向的請求將使用 GET 方法。        Moved = 301,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 302。 System.Net.HttpStatusCode.Found 指示請求的信息位于 Location 頭中指定的        //     URI 處。 接收到此狀態(tài)時的默認操作為遵循與響應關聯(lián)的 Location 頭。 原始請求方法為 POST 時,重定向的請求將使用 GET 方法。        Found = 302,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 302。 System.Net.HttpStatusCode.Redirect 指示請求的信息位于 Location 頭中指定的        //     URI 處。 接收到此狀態(tài)時的默認操作為遵循與響應關聯(lián)的 Location 頭。 原始請求方法為 POST 時,重定向的請求將使用 GET 方法。        Redirect = 302,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 303。 作為 POST 的結果,System.Net.HttpStatusCode.SeeOther 將客戶端自動重定向到        //     Location 頭中指定的 URI。 用 GET 生成對 Location 標頭所指定的資源的請求。        SeeOther = 303,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 303。 作為 POST 的結果,System.Net.HttpStatusCode.RedirectMethod 將客戶端自動重定向到        //     Location 頭中指定的 URI。 用 GET 生成對 Location 標頭所指定的資源的請求。        RedirectMethod = 303,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 304。 System.Net.HttpStatusCode.NotModified 指示客戶端的緩存副本是最新的。 未傳輸此資源的內容。        NotModified = 304,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 305。 System.Net.HttpStatusCode.UseProxy 指示請求應使用位于 Location 頭中指定的        //     URI 的代理服務器。        UseProxy = 305,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 306。 System.Net.HttpStatusCode.Unused 是未完全指定的 HTTP/1.1 規(guī)范的建議擴展。        Unused = 306,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 307。 System.Net.HttpStatusCode.RedirectKeepVerb 指示請求信息位于 Location        //     頭中指定的 URI 處。 接收到此狀態(tài)時的默認操作為遵循與響應關聯(lián)的 Location 頭。 原始請求方法為 POST 時,重定向的請求還將使用        //     POST 方法。        RedirectKeepVerb = 307,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 307。 System.Net.HttpStatusCode.TemporaryRedirect 指示請求信息位于 Location        //     頭中指定的 URI 處。 接收到此狀態(tài)時的默認操作為遵循與響應關聯(lián)的 Location 頭。 原始請求方法為 POST 時,重定向的請求還將使用        //     POST 方法。        TemporaryRedirect = 307,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 400。 System.Net.HttpStatusCode.BadRequest 指示服務器未能識別請求。 如果沒有其他適用的錯誤,或者不知道準確的錯誤或錯誤沒有自己的錯誤代碼,則發(fā)送        //     System.Net.HttpStatusCode.BadRequest。        BadRequest = 400,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 401。 System.Net.HttpStatusCode.Unauthorized 指示請求的資源要求身份驗證。 WWW-Authenticate        //     頭包含如何執(zhí)行身份驗證的詳細信息。        Unauthorized = 401,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 402。 保留 System.Net.HttpStatusCode.PaymentRequired 以供將來使用。        PaymentRequired = 402,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 403。 System.Net.HttpStatusCode.Forbidden 指示服務器拒絕滿足請求。        Forbidden = 403,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 404。 System.Net.HttpStatusCode.NotFound 指示請求的資源不在服務器上。        NotFound = 404,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 405。 System.Net.HttpStatusCode.MethodNotAllowed 指示請求的資源上不允許請求方法(POST        //     或 GET)。        MethodNotAllowed = 405,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 406。 System.Net.HttpStatusCode.NotAcceptable 指示客戶端已用 Accept 頭指示將不接受資源的任何可用表示形式。        NotAcceptable = 406,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 407。 System.Net.HttpStatusCode.ProxyAuthenticationRequired 指示請求的代理要求身份驗證。        //     Proxy-authenticate 頭包含如何執(zhí)行身份驗證的詳細信息。        ProxyAuthenticationRequired = 407,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 408。 System.Net.HttpStatusCode.RequestTimeout 指示客戶端沒有在服務器期望請求的時間內發(fā)送請求。        RequestTimeout = 408,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 409。 System.Net.HttpStatusCode.Conflict 指示由于服務器上的沖突而未能執(zhí)行請求。        Conflict = 409,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 410。 System.Net.HttpStatusCode.Gone 指示請求的資源不再可用。        Gone = 410,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 411。 System.Net.HttpStatusCode.LengthRequired 指示缺少必需的 Content-length        //     頭。        LengthRequired = 411,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 412。 System.Net.HttpStatusCode.PreconditionFailed 指示為此請求設置的條件失敗,且無法執(zhí)行此請求。        //     條件是用條件請求標頭(如 If-Match、If-None-Match 或 If-Unmodified-Since)設置的。        PreconditionFailed = 412,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 413。 System.Net.HttpStatusCode.RequestEntityTooLarge 指示請求太大,服務器無法處理。        RequestEntityTooLarge = 413,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 414。 System.Net.HttpStatusCode.RequestUriTooLong 指示 URI 太長。        RequestUriTooLong = 414,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 415。 System.Net.HttpStatusCode.UnsupportedMediaType 指示請求是不支持的類型。        UnsupportedMediaType = 415,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 416。 System.Net.HttpStatusCode.RequestedRangeNotSatisfiable 指示無法返回從資源請求的數(shù)據范圍,因為范圍的開頭在資源的開頭之前,或因為范圍的結尾在資源的結尾之后。        RequestedRangeNotSatisfiable = 416,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 417。 System.Net.HttpStatusCode.ExpectationFailed 指示服務器未能符合 Expect        //     頭中給定的預期值。        ExpectationFailed = 417,        //        UpgradeRequired = 426,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 500。 System.Net.HttpStatusCode.InternalServerError 指示服務器上發(fā)生了一般錯誤。        InternalServerError = 500,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 501。 System.Net.HttpStatusCode.NotImplemented 指示服務器不支持請求的函數(shù)。        NotImplemented = 501,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 502。 System.Net.HttpStatusCode.BadGateway 指示中間代理服務器從另一代理或原始服務器接收到錯誤響應。        BadGateway = 502,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 503。 System.Net.HttpStatusCode.ServiceUnavailable 指示服務器暫時不可用,通常是由于過多加載或維護。        ServiceUnavailable = 503,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 504。 System.Net.HttpStatusCode.GatewayTimeout 指示中間代理服務器在等待來自另一個代理或原始服務器的響應時已超時。        GatewayTimeout = 504,        //        // 摘要:         //     等效于 HTTP 狀態(tài) 505。 System.Net.HttpStatusCode.HttpVersionNotSupported 指示服務器不支持請求的        //     HTTP 版本。        HttpVersionNotSupported = 505,    }}復制代碼

定義好了異常處理方法,剩下的就是如何使用了。可以根據實際情況,在不同級別使用統(tǒng)一的異常處理機制。

1、接口級別

        [WebApiExceptionFilter]        [HttpGet]        public string GetAllChargingData([FromUri]TB_CHARGING obj)        {            throw new NotImplementedException("方法不被支持");        }

執(zhí)行到異常后,會先進到OnException方法:

執(zhí)行完成之后瀏覽器查看:

如果需要,甚至可以向Status Code里面寫入自定義的描述信息,并且還可以向我們的Response的Content里面寫入我們想要的信息。我們稍微改下OnException方法:

復制代碼
       if (actionExecutedContext.Exception is NotImplementedException)            {                var oResponse = new HttpResponseMessage(HttpStatusCode.NotImplemented);                oResponse.Content = new StringContent("方法不被支持");                oResponse.ReasonPhrase = "This Func is Not Supported";                actionExecutedContext.Response = oResponse;            }復制代碼

看看ReasonPhrase描述信息

看看Response的描述信息

2、控制器級別

如果想要某一個或者多個控制器里面的所有接口都使用異常過濾,直接在控制器上面標注特性即可。

某一個控制器上面啟用異常過濾復制代碼
  [WebApiExceptionFilter]    public class ChargingController : BaseApiController    {        #region Get        [HttpGet]        public string GetAllChargingData([FromUri]TB_CHARGING obj)        {            throw new NotImplementedException("方法不被支持");        }    }復制代碼多個控制器上面同時啟用異常過濾
    [WebApiExceptionFilter]    public class BaseApiController : ApiController    {    }復制代碼
    public class ChargingController : BaseApiController    {        #region Get        [HttpGet]        public string GetAllChargingData([FromUri]TB_CHARGING obj)        {            throw new NotImplementedException("方法不被支持");        }    }復制代碼

這樣,所有繼承BaseApiController的子類都會啟用異常過濾。

3、全局配置

如果需要對整個應用程序都啟用異常過濾,則需要做如下兩步:

  1、在Global.asax全局配置里面添加 GlobalConfiguration.Configuration.Filters.Add(new WebApiExceptionFilterAttribute()); 這一句,如下:

復制代碼
void application_Start(object sender, EventArgs e)        {            // 在應用程序啟動時運行的代碼            AreaRegistration.RegisterAllAreas();            GlobalConfiguration.Configure(WebApiConfig.Register);            RouteConfig.RegisterRoutes(RouteTable.Routes);            GlobalConfiguration.Configuration.Filters.Add(new WebApiExceptionFilterAttribute());        }復制代碼

  2、在WebApiConfig.cs文件的Register方法里面添加  config.Filters.Add(new WebApiExceptionFilterAttribute()); 這一句,如下:

復制代碼
        public static void Register(HttpConfiguration config)        {            //跨域配置            config.EnableCors(new EnableCorsAttribute("*", "*", "*"));            // Web API 路由            config.MapHttpAttributeRoutes();            RouteTable.Routes.MapHttpRoute(                name: "DefaultApi",                routeTemplate: "api/{controller}/{action}/{id}",                defaults: new { id = RouteParameter.Optional }            ).RouteHandler = new sessionControllerRouteHandler();            config.Filters.Add(new WebApiExceptionFilterAttribute());        }復制代碼回到頂部

二、HttpResponseException自定義異常信息

上面說的是全局的異常捕獲以及處理方式,在某些情況下,我們希望以異常的方式向客戶端發(fā)送相關信息,可能就需要用到我們的HttpResponseException。比如:

復制代碼
        [HttpGet]        public TB_CHARGING GetById(string id)        {            //從后臺查詢實體            var oModel = server.Find(id);            if (oModel == null)            {                var resp = new HttpResponseMessage(HttpStatusCode.NotFound)                {                    Content = new StringContent(string.Format("沒有找到id={0}的對象", id)),                    ReasonPhrase = "object is not found"                };                throw new HttpResponseException(resp);            }            return oModel;        }復制代碼

執(zhí)行之后瀏覽器里面查看結果:

代碼釋疑:細心的朋友可能,發(fā)現(xiàn)了,這里既使用了HttpResponseMessage,又使用了HttpResponseException,那么,像這種可控的異常,我們是否可以直接以HttpResponseMessage的形式直接返回到客戶端而不用拋出異常呢?這里就要談談這兩個對象的區(qū)別了,博主的理解是HttpResonseMessage對象用來響應訊息并包含狀態(tài)碼及數(shù)據內容,HttpResponseException對象用來向客戶端返回包含錯誤訊息的異常。

在網上看到一篇 文章 這樣描述兩者的區(qū)別:當呼叫 Web API 服務時發(fā)生了與預期上不同的錯誤時,理當應該中止程序返回錯誤訊息,這時對于錯誤的返回就該使用 HttpResponseException,而使用 HttpResponseMessage 則是代表著當客戶端發(fā)送了一個工作請求而 Web API 正確的完成了這個工作,就能夠使用 HttpResponseMessage 返回一個 201 的訊息,所以 HttpResponseMessage 與 HttpResponseException 在使用上根本的目標就是不同的,用 HttpResponseMessage 去返回一個例外錯誤也會讓程序結構難以辨別且不夠清晰。

回到頂部

三、返回HttpError

HttpError對象提供一致的方法來響應正文中返回錯誤的信息。準確來說,HttpError并不是一個異常,只是用來包裝錯誤信息的一個對象。其實在某一定的程度上,HttpError和HttpResponseMessage使用比較相似,二者都可以向客戶端返回http狀態(tài)碼和錯誤訊息,并且都可以包含在HttpResponseException對象中發(fā)回到客戶端。但是,一般情況下,HttpError只有在向客戶端返回錯誤訊息的時候才會使用,而HttpResponseMessage對象既可以返回錯誤訊息,也可返回請求正確的消息。其實關于HttpError沒什么特別好講的,我們來看一個例子就能明白:

復制代碼
    public HttpResponseMessage Update(dynamic obj)        {            TB_Product oModel = null;            try            {                var id = Convert.ToString(obj.id);                oModel = Newtonsoft.Json.JsonConvert.DeserializeObject<TB_Product>(Convert.ToString(obj.dataModel));                //...復雜的業(yè)務邏輯            }            catch(Exception ex)            {                return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex.Message);            }            return Request.CreateResponse<TB_Product>(HttpStatusCode.OK, oModel);        }復制代碼

假如現(xiàn)在在執(zhí)行try里面復雜業(yè)務邏輯的時候發(fā)生了異常,我們捕獲到了異常然后向客戶端返回HttpError對象,這個對象里面包含我們自定義的錯誤訊息,如果正常則返回HttpResponseMessage對象。

如果請求異常:

如果請求正常

回到頂部

四、總結

 以上三種異常的處理方法,可以根據不同的場景選擇使用。

如果項目對異常處理要求并不高,只需要記錄好異常日志即可,那么使用異常篩選器就能夠搞定如果項目需要對不同的異常,客戶端做不同的處理。而這個時候使用異常篩選器不能詳盡所有的異常,可能使用HttpResponseException對象是更好的選擇,定義更加精細的異常和異常描述。對于何時使用HttpError,又何時使用HttpResponseMessage,可以參考上文三里面用法。當然實際項目中很可能以上兩種或者三種同時使用。

上文通過一些簡單的示例介紹了下WebApi里面異常的處理機制,可能不夠深入,但對于一般項目的異常處理基本夠用。其實有一點博主還沒有想明白,對于構造函數(shù)里面的異常該如何統(tǒng)一捕獲呢?通過異常篩選器是捕獲不到的,不知道園友們有沒有什么更好的辦法,不吝賜教,感謝感謝!如果本文能幫到你,不妨推薦下,您的推薦是博主繼續(xù)總結的動力!


發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 墨玉县| 建始县| 紫阳县| 瓦房店市| 长宁区| 郎溪县| 肃南| 侯马市| 大冶市| 广宁县| 涞源县| 栾城县| 荔浦县| 长春市| 嫩江县| 蓬安县| 靖远县| 双鸭山市| 泉州市| 电白县| 田东县| 舟山市| 乐业县| 宣城市| 伽师县| 富源县| 焦作市| 灯塔市| 元阳县| 遂平县| 桐梓县| 望江县| 乌鲁木齐县| 大关县| 延川县| 台南市| 泸州市| 乐东| 松原市| 醴陵市| 吉林省|