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.
The new CreateResponse() method:
Hope this just saved someone's day... ;)