70

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?

1

2 Answers 2

131

Try:

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

HttpClient client = new HttpClient(handler);
Sign up to request clarification or add additional context in comments.

4 Comments

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)?
Maybe you need this: handler.MaxAutomaticRedirections = 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.
Almost a decade later... how you can you set this up for a clean created using TestServer.CreateClient() ??
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:

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.