如何在HttpClient response.ReasonPhrase中获取真正的错误消息?

时间:2016-10-19 15:47:06

标签: c# asp.net .net asp.net-web-api

我的设置

我有2个WebApi项目,其中包含以下流程:

  1. 用户 API 1
  2. 发出请求
  3. API 1 代表用户向 API 2 发出请求(使用HttpClient)。
  4. using (var client = new HttpClient())
    {
        client.SetBearerToken(token);
    
        string endpoint = PbbSettings.Identity.Users.Delete.Replace("{userId}", userId);
    
        // Attempt deletion of the user
        using (var response = await client.DeleteAsync(endpoint))
        {
            // Throw exception if not succeeded
            EnsureSuccess(response);
        }
    }
    

    问题

    因此,控制和信息流动工作正常。 问题是,当 API 2 响应错误时,response.ReasonPhrase会显示“错误请求”或“内部服务器错误”,而不是我在异常中设置的消息。

    现在已经整整一天吐血了。任何见解?

    更多信息(TLDR)

    为清楚起见,我的所有API都注册了一个全局异常过滤器来处理错误:

    public class RepositoryExceptionsHandlerAttribute : ExceptionFilterAttribute
    {
        public override void OnException(HttpActionExecutedContext context)
        {
            HandleException(context);
    
            base.OnException(context);
        }
    
        public override Task OnExceptionAsync(HttpActionExecutedContext context, CancellationToken cancellationToken)
        {
            HandleException(context);
    
            return base.OnExceptionAsync(context, cancellationToken);
        }
    
        /// <summary>
        /// Recognizes common repository exceptions and if necessary creates a response and updates the context.
        /// </summary>
        /// <param name="context">The context in which the exception was thrown.</param>
        private void HandleException(HttpActionExecutedContext context)
        {
            var response = CreateResponse(context.Request, context.Exception);
    
            if (response != null)
                context.Response = response;
        }
    
        /// <summary>
        /// Recognizes common repository exceptions and creates a corresponding error response.
        /// </summary>
        /// <param name="request">The request to which the response should be created.</param>
        /// <param name="ex">The exception to handle.</param>
        /// <returns>An error response containing the status code and exception data, or null if this is not a common exception.</returns>
        private HttpResponseMessage CreateResponse(HttpRequestMessage request, Exception ex)
        {
            string message = ex.Message;
    
            if (ex is KeyNotFoundException)        return request.CreateErrorResponse(HttpStatusCode.NotFound, message);
            if (ex is ArgumentException)           return request.CreateErrorResponse(HttpStatusCode.BadRequest, message);
            if (ex is InvalidOperationException)   return request.CreateErrorResponse(HttpStatusCode.BadRequest, message);
            if (ex is UnauthorizedAccessException) return request.CreateErrorResponse(HttpStatusCode.Unauthorized, message);
    #if !DEBUG
            // For security reasons, when an exception is not handled the system should return a general error, not exposing the real error information
            // In development time, the programmer will need the details of the error, so this general message is disabled.
            request.CreateErrorResponse(HttpStatusCode.InternalServerError, Errors.InternalServerError);
    #endif
    
            return null;
        }
    }
    

    这适用于用户 API 1 。 但是当 API 1 API 2 执行操作时,响应创建会忽略我输入的消息并将状态设置为原因。

1 个答案:

答案 0 :(得分:5)

新的CreateResponse()方法:

    /// <summary>
    /// Recognizes common repository exceptions and creates a corresponding error response.
    /// </summary>
    /// <param name="request">The request to which the response should be created.</param>
    /// <param name="ex">The exception to handle.</param>
    /// <returns>An error response containing the status code and exception data, or null if this is not a common exception.</returns>
    private HttpResponseMessage CreateResponse(HttpRequestMessage request, Exception ex)
    {
        string message = ex.Message;

        HttpStatusCode code = 0;

        if      (ex is KeyNotFoundException)        code = HttpStatusCode.NotFound;
        else if (ex is ArgumentException)           code = HttpStatusCode.BadRequest;
        else if (ex is InvalidOperationException)   code = HttpStatusCode.BadRequest;
        else if (ex is UnauthorizedAccessException) code = HttpStatusCode.Unauthorized;
        else if (ex is HttpException)
        {
            // HttpExceptions are thrown when request between IdentityServer and the API server have failed.
            // IdentityServer has generated an error, the API server received it and now it needs to relay it back to the client.
            var httpException = (HttpException) ex;

            code = (HttpStatusCode) httpException.GetHttpCode();
            message = httpException.Message;
        }
        else
        {
            code = HttpStatusCode.InternalServerError;

            // For security reasons, when an exception is not handled the system should return a general error, not exposing the real error information
            // In development time, the programmer will need the details of the error, so this general message is disabled.
#if DEBUG
            message = ex.Message;
#else
            message = Errors.InternalServerError;
#endif
        }

        // For some reason the request.CreateErrorResponse() method ignores the message given to it and parses its own message.
        // The error response is constructed manually.
        return CreateErrorResponse(request, code, message);
    }

    private HttpResponseMessage CreateErrorResponse(HttpRequestMessage request, HttpStatusCode code, string message)
    {
        var content = new { Message = message };

        return new HttpResponseMessage(code)
        {
            ReasonPhrase = message,
            RequestMessage = request,
            Content = new ObjectContent(content.GetType(), content, new JsonMediaTypeFormatter())
        };
    }
}

希望这只是拯救了某人......;)