2016-09-05 39 views
2

我有一個MVC 5 Web API,它在出現意外異常或未找到控制器或操作的情況下返回自定義響應。從本質上講,我完成了如圖所示的完成:http://weblogs.asp.net/imranbaloch/handling-http-404-error-in-asp-net-web-api一切都像魅力一樣工作。將自定義HttpControllerSelector和HttpActionSelector的值轉發到其描述符

問題是:我想提交從SelectController()SelectAction()到我的ErrorController的錯誤代碼。這樣我就不會有重複的代碼,所有的邏輯都將在控制器中。

不幸的是,我沒有找到任何可能的方式將錯誤代碼提交給我的控制器。所有的例子都重定向到一個特定的錯誤行爲(例如ErrorController.NotFound404)我想重定向到ErrorController.Main並在那裏做所有的魔法。

自定義ApiControllerActionSelector的另一個問題是ErrorController中的Request屬性爲null。自定義DefaultHttpControllerSelector不存在此問題。

任何想法?

最好的問候, 卡斯滕

回答

3

幸運的是,我能夠找到解決自己。讓我告訴你我是如何運作起來的。

  1. 定製控制部和動作選擇器被轉發所請求的語言和當前的HTTP響應代碼:
public class CustomDefaultHttpControllerSelector: DefaultHttpControllerSelector 
{ 
    public CustomDefaultHttpControllerSelector(HttpConfiguration configuration) : base(configuration) 
    { 
    } 

    public override HttpControllerDescriptor SelectController(HttpRequestMessage request) 
    { 
     HttpControllerDescriptor descriptor = null; 
     try 
     { 
      descriptor = base.SelectController(request); 
     } 
     catch (HttpResponseException e) 
     { 
      var routeValues = request.GetRouteData().Values; 
      routeValues.Clear(); 
      routeValues["controller"] = "Error"; 
      routeValues["action"] = "Main"; 
      routeValues["code"] = e.Response.StatusCode; 
      routeValues["language"] = request.Headers?.AcceptLanguage?.FirstOrDefault()?.Value ?? "en"; 

      descriptor = base.SelectController(request); 
     } 
     return descriptor; 
    } 
} 

public class CustomControllerActionSelector: ApiControllerActionSelector 
{ 
    public CustomControllerActionSelector() 
    { 
    } 

    public override HttpActionDescriptor SelectAction(HttpControllerContext controllerContext) 
    { 
     HttpActionDescriptor descriptor = null; 
     try 
     { 
      descriptor = base.SelectAction(controllerContext); 
     } 
     catch (HttpResponseException e) 
     { 
      var routeData = controllerContext.RouteData; 
      routeData.Values.Clear(); 
      routeData.Values["action"] = "Main"; 
      routeData.Values["code"] = e.Response.StatusCode; 
      routeData.Values["language"] = controllerContext.Request?.Headers?.AcceptLanguage?.FirstOrDefault()?.Value ?? "en"; 
      IHttpController httpController = new ErrorController(); 
      controllerContext.Controller = httpController; 
      controllerContext.ControllerDescriptor = new HttpControllerDescriptor(controllerContext.Configuration, "Error", httpController.GetType()); 
      descriptor = base.SelectAction(controllerContext); 
     } 
     return descriptor; 
    } 
} 

兩個重要的變化:

1.1。路由值列表需要清除。否則,它會嘗試在ErrorController中查找映射到此值列表的操作。

1.2。增加了codelanguage

  • ErrorController本身:
  • [RoutePrefix("error")] 
    public class ErrorController: BaseController 
    { 
        [HttpGet, HttpPost, HttpPut, HttpDelete, HttpHead, HttpOptions, AcceptVerbs("PATCH")] 
        [Route("{code}/{language}")] 
        public HttpResponseMessage Main(string code, string language) 
        { 
         HttpStatusCode parsedCode; 
         var responseMessage = new HttpResponseMessage(); 
         if (!Enum.TryParse(code, true, out parsedCode)) 
         { 
          parsedCode = HttpStatusCode.InternalServerError; 
         } 
         responseMessage.StatusCode = parsedCode; 
         ... 
        } 
    } 
    
  • 我已經去除了路由映射routes.MapHttpRoute(...)。無論我在瀏覽器中輸入什麼,它都不會調用Handle404

  • HTTP狀態400(壞請求)未被覆蓋。按照http://www.asp.net/web-api/overview/formats-and-model-binding/model-validation-in-aspnet-web-api(「處理驗證錯誤」部分)中的說明使用ValidationModelAttribute可以輕鬆實現此目的。

  • 也許這將幫助別人......

    +0

    1.1點幫助別人。 :) – teedyay