Using Flurl's OnError to check for custom response headers in ASP.NET Core Web API

Viewed 245

I am developing a sort of middleware API, which is meant to connect to various systems. I am using the Flurl library to make requests to the individual systems.

One of these systems sometimes returns a custom header containing an error message, rather than just returning it in the response body (for reasons unbeknownst to me).

To return the correct error message, I am using Flurl's OnError event handler. I defined behaviour which checks the response for this custom header and, if found, extracts the decoded message and sets it as the response message of the request:

/// <summary>
/// Asynchronously checks responses from Thinkwise Indicium for TSFMessage headers and, if found,
/// decodes them and sets them as the actual error message of the response.
/// </summary>
/// <param name="request">The request of which the response must be checked for a Thinkwise message header.</param>
/// <returns>A Task.</returns>
public static async Task HandleThinkwiseErrorAsync(FlurlCall request)
{
  if (request.Response.Headers.TryGetFirst("TSFMessages", out var message))
  {
    var decodedMessage = JsonSerializer.Deserialize<ThinkwiseSoftwareFactoryMessage>(
                       Encoding.UTF8.GetString(Convert.FromBase64String(message)));

    var response = new HttpResponseMessage
    {
      StatusCode = HttpStatusCode.BadRequest,
      RequestMessage = new HttpRequestMessage(request.Request.Verb, request.Request.Url),
      Content = new StringContent(
        decodedMessage?.RawMessage.Split("\"")[1]
        ?? await request.Response.ResponseMessage.Content.ReadAsStringAsync(default))
      };

    request.HttpResponseMessage = response;
    request.ExceptionHandled = false;
  }
}

While I do have prior experience making HTTP requests using Flurl, configuring it like this is a first for me. I have configured Flurl as such in the DI container:

FlurlHttp.Configure(settings =>
{
  settings.OnErrorAsync = IndiciumFlurlHelper.HandleThinkwiseErrorAsync;
  settings.OnError = IndiciumFlurlHelper.HandleThinkwiseError; // Same as async method
});

I am having difficulty finding other articles, issues or SO questions which are trying to do similar things. I am probably making a rookie mistake somewhere, or just misunderstanding how the FlurlCall object is constructed and what the properties mean.

The problem I am running into is that, when I make a request I know should return an error message using this header, my middleware API returns a 204 response when it should return a 400 response with the error message in the body.

What am I doing wrong? The requests seem to work as expected, so I am pretty sure I am doing something wrong handling this response header this way.

Thanks in advance! Please do leave a comment, should you have further questions.

1 Answers

This isn't going to work because 204 isn't an error status, so Flurl won't raise the OnError event and your handler won't get hit. Although you can configure which statuses Flurl should treat as errors, there's no way to specify that any status in the 2xx range should be considered an error.

You could handle the AfterCall event instead, although at that point it's too late to change the response. I'm not sure that's a great idea anyway, it's like pretending the server returned something that it didn't. Probably better to just take the actual response and handle it accordingly.

Of course another option is to skip event-handling entirely and just handle this condition inline. The event handlers are good for cross-cutting concerns like logging, but dealing with error conditions is often just part of your normal application flow. The introduction of FlurlResponse in the 3.0 should make this flow easier as well.

Related