обработка исключений web API и MVC

В настоящее время мы повторно разрабатываем нашу систему веб-форм в веб-API и MVC (это новая технология для нас) До сих пор все, кажется, в порядке, однако мы изо всех сил пытаемся отправить обратно ошибки из приложения Web API в приложение MVC. Мы понимаем, что нам нужно захватить любые исключения, и они преобразуются в HTTP-ответы

контроллер продукта Web API выглядит следующим образом:

public HttpResponseMessage GetProducts()
    {
        BAProduct c = new BAProduct();
        var d = c.GetProducts();
         if (d == null)
            return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, "This is a custom error message");
        else
            return Request.CreateResponse(HttpStatusCode.OK, d);
    }

приложение MVC вызовет веб-API следующим образом код:-

public T Get<T>()

               using (HttpClient client = new HttpClient())
                    {
                        client.BaseAddress = new Uri(Config.API_BaseSite);

                        client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
                        HttpResponseMessage response = client.GetAsync("api/myapplicaton/products").Result;
                        response.EnsureSuccessStatusCode();
                        T res = response.Content.ReadAsAsync<T>().Result;
                        return (T)res;
                    }
            }

то, что мы пытаемся достичь, когда HTTP-ошибка получена из веб-API в приложении MVC, пользователь либо перенаправляется на пользовательскую страницу ошибки, либо отображает пользовательское сообщение об ошибке в текущем представлении (в зависимости от серьезности ошибки). Проблема, которая у нас есть, заключается в том, что: -

  1. как получить доступ к пользовательскому сообщению об ошибке, которое мы отправили обратно? ( из примера кода это будет "это обычай сообщение об ошибке", мы прошли через каждый атрибут в res и не можем увидеть это сообщение)

  2. В зависимости от кода состояния, как мы можем захватить это и перенаправить пользователей на отдельные страницы ошибок, т. е. 404 страницы, 500 страниц и отобразить пользовательское ответное сообщение, которое было отправлено обратно. мы были в глобальном масштабе.asax route

    protected void Application_Error(object sender, EventArgs e)
    {
        Exception exception = Server.GetLastError();
        Response.Clear();
        HttpException httpException = exception as HttpException;
    

однако наше httpExecption всегда равно NULL

мы искали etc, и пока еще, не можем найти что-нибудь подходящее, надеюсь, кто-нибудь укажет нам верное направление.

2 ответов


причина, по которой ваш экземпляр httpException имеет значение null, заключается в том, что response.EnsureSuccessStatusCode(); метод не бросил HttpException что вы пытаетесь бросить. Это бросание HttpRequestException который отличается, но не имеет простого способа получить более подробную информацию (например, код состояния).

в качестве альтернативы вызову этого метода вы можете проверить IsSuccessStatusCode логическое свойство и бросить HttpException сам себе:

public T Get()
{
    using (HttpClient client = new HttpClient())
    {
        client.BaseAddress = new Uri(Config.API_BaseSite);

        client.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
        HttpResponseMessage response = client.GetAsync("api/myapplicaton/products").Result;
        if (!response.IsSuccessStatusCode)
        {
            string responseBody = response.Content.ReadAsStringAync().Result;
            throw new HttpException((int)response.StatusCode, responseBody);
        }

        T res = response.Content.ReadAsAsync<T>().Result;
        return (T)res;
    }
}

этой HttpException теперь может быть пойман в Application_Error и в зависимости от кода состояния продолжить обработку:

protected void Application_Error()
{
    var exception = Server.GetLastError();
    var httpException = exception as HttpException;
    Response.Clear();
    Server.ClearError();

    var routeData = new RouteData();
    routeData.Values["controller"] = "Errors";
    routeData.Values["action"] = "Http500";
    routeData.Values["exception"] = exception;

    Response.StatusCode = 500;
    Response.TrySkipIisCustomErrors = true;

    if (httpException != null)
    {
        Response.StatusCode = httpException.GetHttpCode();
        switch (Response.StatusCode)
        {
            case 403:
                routeData.Values["action"] = "Http403";
                break;
            case 404:
                routeData.Values["action"] = "Http404";
                break;

            // TODO: Add other cases if you want to handle
            // different status codes from your Web API
        }
    }

    IController errorsController = new ErrorsController();
    var rc = new RequestContext(new HttpContextWrapper(Context), routeData);
    errorsController.Execute(rc);
}

в этом примере я предполагаю, что у вас есть ErrorsController с соответствующими действиями (Http500, Http403, Http404,...). Соответствующее действие будет вызываться в зависимости от кода состояния, и вы можете возвращать разные представления.


обновление:

вы можете захватить дополнительные артефакты HTTP-запроса, такие как фраза причины, чтобы отобразить его в страницы ошибок. В этом случае вы можете просто написать свое собственное исключение, которое будет содержать необходимую вам информацию:

public class ApiException : Exception
{
    public HttpStatusCode StatusCode { get; set; }
    public string Reason { get; set; }
    public string ResponseBody { get; set; }
}

что вы могли бы бросить:

if (!response.IsSuccessStatusCode)
{
    throw new ApiException    
    {
        StatusCode = response.StatusCode,
        Reason = response.ReasonPhrase,
        ResponseBody = response.Content.ReadAsStringAync().Result,
    };
}

а затем работать с этим пользовательским исключением в вашем Application_Error.


отличный пост для решения вышеуказанной проблемы здесь

он в основном имеет два основных шага -

Шаг-1: десериализует содержимое ответа в результатах ошибки HTTP, например

var httpErrorObject = response.Content.ReadAsStringAsync().Result;

// Create an anonymous object to use as the template for deserialization:
var anonymousErrorObject = 
    new { message = "", ModelState = new Dictionary<string, string[]>() };

// Deserialize:
var deserializedErrorObject = 
    JsonConvert.DeserializeAnonymousType(httpErrorObject, anonymousErrorObject);

Шаг-2: Добавьте ошибки в клиентские словари ModelState

foreach (var error in apiEx.Errors)
{
     ModelState.AddModelError("", error);
}

строковые сообщения об ошибках добавляются в apiEx.Ошибки при использовании deserializedErrorObject.

посмотреть ссылке