67

Consider an ASP.NET Web API service that redirects

public class ThisController : ApiController
{
    /* more methods */

    public override HttpResponseMessage Post()
    {
        var result = new HttpResponseMessage(HttpStatusCode.MovedPermanently);
        // Post requests should be made to "ThatController" instead.
        string uri = Url.Route("That", null);
        result.Headers.Location = new Uri(uri, UriKind.Relative);
        return result;
    }
}

Trying to verify that POST'ing data to "api/this" will redirect you to "api/that", I have the following test method:

[TestMethod]
public void PostRedirects()
{
    using (var client = CreateHttpClient("application/json"))
    {
        var content = CreateContent(expected, "application/json");
        using (var responseMessage = client.PostAsync("api/this", content).Result)
        {
            Assert.AreEqual(HttpStatusCode.MovedPermanently, responseMessage.StatusCode);
            Assert.AreEqual(new Uri("https://api.example.com/api/that"), responseMessage.Headers.Location);
        }
    }
}

protected HttpClient CreateHttpClient(string mediaType)
{
    var client = new HttpClient();
    client.BaseAddress = new Uri("http://api.example.com/");
    MediaTypeWithQualityHeaderValue headerValue = MediaTypeWithQualityHeaderValue.Parse(mediaType);
    client.DefaultRequestHeaders.Accept.Add(headerValue);
    client.DefaultRequestHeaders.AcceptEncoding.Add(StringWithQualityHeaderValue.Parse("gzip"));
    client.DefaultRequestHeaders.AcceptEncoding.Add(StringWithQualityHeaderValue.Parse("deflate"));
    client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue(new ProductHeaderValue("MyProduct", "1.0")));
    client.MaxResponseContentBufferSize = 1024*1024*8;
    return client;
}

protected ObjectContent CreateContent(T model, string mediaType)
{
    var requestMessage = new HttpRequestMessage();
    MediaTypeFormatter mediaTypeFormatter = null;
    switch (mediaType)
    {
        case "application/json":
            mediaTypeFormatter = new JsonMediaTypeFormatter();
            break;

        case "application/xml":
        case "text/xml":
            mediaTypeFormatter = new XmlMediaTypeFormatter();
            break;

        default:
            Assert.Fail();
            break;
    }

    return requestMessage.CreateContent(
        model,
        new[] { mediaTypeFormatter },
        new FormatterSelector());
}

What really happens that is that a HTTP Status Code is sent to the client with the correct Location header and that HttpClient then automatically performs a GET on that URI. As a result, my test never passes.

How do I configure the HttpClient not to automatically redirect when it receives a 301 so that I can verify that my server response?

Anthony
  • 5,176
  • 6
  • 65
  • 87
bloudraak
  • 5,902
  • 5
  • 37
  • 52
  • 1
    Possible duplicate of [How can I get System.Net.Http.HttpClient to not follow 302 redirects?](http://stackoverflow.com/questions/10453892/how-can-i-get-system-net-http-httpclient-to-not-follow-302-redirects) – Michael Freidgeim Mar 14 '16 at 04:36

2 Answers2

127

Try:

var handler = new HttpClientHandler() 
{
    AllowAutoRedirect = false
};

HttpClient client = new HttpClient(handler);
Mike Wasson
  • 6,572
  • 2
  • 24
  • 20
  • 17
    Is it possible to do this on a per-request basis without needing two separate `HttpClient` instances (i.e. one that allows redirects and one that does not)? – Dai Apr 28 '19 at 23:45
  • 1
    Maybe you need this: `handler.MaxAutomaticRedirections = 1` – Konstantin S. Apr 18 '20 at 22:47
  • 1
    @Dai No, that's not possible without reinventing the wheel unfortunately. But if you don't mind using a 3rd-party library [that's exactly what I did in Flurl 3.0](https://github.com/tmenier/Flurl/issues/500). – Todd Menier Aug 09 '20 at 15:22
  • 1
    Almost a decade later... how you can you set this up for a clean created using `TestServer.CreateClient()` ?? – Vergil C. Jan 29 '21 at 17:54
0

To clarify this issue. In client-side Blazor WASM the best you can do is cause the redirect to not happen, which may be good enough for your needs. In this case you will get back a status code of zero with no response headers, no response content, etc. It is NOT POSSIBLE to actually get the redirect status code (301, etc.), headers, etc. from the redirect response.

This is apparently due to the underlying JavaScript APIs used to implement HttpClientHandler:

This makes coding an API client in browser hosted Blazor WebAssembly tricky if the API relies on redirect responses. You could probably eventually get it to work via user-facing navigation actions rather than HttpClient, but ugg.

Hope this saves someone some time.

Some references on the topic:

For anyone not running in a browser, here is a very nice example of doing this:

Todd
  • 99
  • 1
  • 4