How do I get the real error message in HttpClient response.ReasonPhrase?
The new CreateResponse() method:
/// <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())
};
}
}
Hope this just saved someone's day... ;)
Shy Agam
By day 🌅: Team leader. Full-stack web developer specializing in .NET environment and Angular. By night 🌃: Salsa dancer, guitar player, lover, and pizza baker. I'm a social person, I love people and enjoy good company. Trying to make the world better with my spirit, inspire people and do good. Wanna see more? Visit https://linktr.ee/shyagam Happy coding!
Updated on June 29, 2022Comments
-
Shy Agam almost 2 years
My Setting
I have 2 WebApi projects with the following flow:
- User makes request to API 1
- API 1 makes request to API 2 on behalf of the user (using an
HttpClient
).
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); } }
The Problem
So the flow of control and information works fine. The problem is that when API 2 responds with an error,
response.ReasonPhrase
says "Bad Request" or "Internal Server Error", instead of the message I set in the exception.Been spitting blood on this for a whole day now. Any insights?
More Info (TLDR)
For clarity, all my APIs have a global exception filter registered to handle errors:
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; } }
This works fine between the user and API 1. But when API 1 and API 2 do their thing, the response creation ignores the message I put in and sets the status as the reason.