Search code examples
asp.net-corehttp-redirectblazor-webassemblydotnet-httpclientflurl

Redirect (302) between endpoints of controller in Blazor WebAssembly Hosted project using IFlurlClient


I am dealing with an issue regarding redirecting in server controller in hosted Blazor WebAssembly project.

As an example, I modified default WeatherForecastController to redirect one endpoint to another.

[HttpGet]
public IActionResult Get()
{
  return Redirect("WeatherForecast/redirected");
}

[HttpGet("redirected")]
public IEnumerable<WeatherForecast> GetRedirected()
{
    // default implementation returning some random WeatherForecasts
}

In the client, there are two implementations of service, which requests said endpoint. First one makes request using regular HttpClient, second with IFlurlClient.

class HttpWeatherForecastClient : IWeatherForecastClient
{

  readonly HttpClient httpClient;

   // constructor and stuff

  public async Task<WeatherForecast[]> GetForecastsAsync()
  {
    return await httpClient.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast")
      ?? Array.Empty<WeatherForecast>();
  }
}

class FlurlWeatherForecastClient : IWeatherForecastClient
{
  readonly IFlurlClient flurlClient;

  // constructor and stuff
  public async Task<WeatherForecast[]> GetForecastsAsync()
  {
    var response = await flurlClient.Request("WeatherForecast").GetAsync();
    Console.WriteLine($"{response.StatusCode}: {response.ResponseMessage}");
    return await response.GetJsonAsync<WeatherForecast[]>()
      ?? Array.Empty<WeatherForecast>();
  }
}

Implementation with HttpClient works fine. Implementation with IFlurClient keep returning response with ReasonPhrase opaqueredirect and StatusCode with value 0.

Is there way to configure IFlurClient to work with redirection?

I tried to implement custom IFlurlClientFactory to modify configuration of IFlurlClient. But I have no idea which option I need to change :(

public class AluPerBaseUrlFlurlClientFactory : PerBaseUrlFlurlClientFactory
{
  protected override IFlurlClient Create(Url url)
  {
    return base
      .Create(url)
      .WithAutoRedirect(true)          
    ;
  }
}

If there is something relevant in documentation, I don't get it.

Example project: https://github.com/GodEmperorOfTime/FlurlWasmRedirect/

Edit - possible solution

Based on the answer provided by Todd Menier, I think a reasonable solution is to register IFlurlClient like this:

builder.Services.AddScoped(services => {
  return new HttpClient() { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) };
});
builder.Services.AddScoped<IFlurlClient>(services => {
  var httpClient = services.GetRequiredService<HttpClient>();
  return new FlurlClient(httpClient);
});

Solution

  • The response you're getting is clearly originating from the client-side fetch() operation that is ultimately handling the call:

    opaqueredirect: The fetch request was made with redirect: "manual". The Response's status is 0, headers are empty, body is null and trailer is empty.

    I believe at some point WASM is using a custom (and, sadly, internal) handler to somehow ensure that the fetch() request uses "follow" instead of "manual" for that property.

    Fortunately there's a simpler option here than trying to reverse-engineer this to work the same way with Flurl: just allow WASM to provide the HttpClient (as in your example that actually works), but within your service class, create a FlurlClient that wraps it:

    class FlurlWeatherForecastClient : IWeatherForecastClient
    {
      readonly IFlurlClient _flurlClient;
    
      public FlurlWeatherForecastClient(HttpClient httpClient)
      {
        _flurlClient = new FlurlClient(httpClient);
      }
    
      public async Task<WeatherForecast[]> GetForecastsAsync()
      {
        // use _flurlClient here
      }
    }
    

    Now you can use a FlurlClient that defers its work to the injected HttpClient, which in turn defers its work to that handler we don't have direct access to. Although I've not tried this, my expectation is it should just work, and you shouldn't even have to care about those underlying details.