Handling Httpclient Redirects

Handling HttpClient Redirects

The default behaviour of HttpClient is compliant with the requirements of the HTTP specification (RFC 2616)


10.3.3 302 Found
...

If the 302 status code is received in response to a request other
than GET or HEAD, the user agent MUST NOT automatically redirect the
request unless it can be confirmed by the user, since this might
change the conditions under which the request was issued.

You can override the default behaviour of HttpClient by sub-classing DefaultRedirectStrategy and overriding its #isRedirected() method.

How to follow-through on HTTP 303 status code when using HttpClient in Java 11 and later?

Problem

You're using HttpClient#newHttpClient(). The documentation of that method states:

Returns a new HttpClient with default settings.

Equivalent to newBuilder().build().

The default settings include: the "GET" request method, a preference of HTTP/2, a redirection policy of NEVER [emphasis added], the default proxy selector, and the default SSL context.

As emphasized, you are creating an HttpClient with a redirection policy of NEVER.



Solution

There are at least two solutions to your problem.

Automatically Follow Redirects

If you want to automatically follow redirects then you need to use HttpClient#newBuilder() (instead of #newHttpClient()) which allows you to configure the to-be-built client. Specifically, you need to call HttpClient.Builder#followRedirects(HttpClient.Redirect) with an appropriate redirect policy before building the client. For example:

HttpClient client = 
HttpClient.newBuilder()
.followRedirects(HttpClient.Redirect.NORMAL) // follow redirects
.build();

The different redirect policies are specified by the HttpClient.Redirect enum:

Defines the automatic redirection policy.

The automatic redirection policy is checked whenever a 3XX response code is received. If redirection does not happen automatically, then the response, containing the 3XX response code, is returned, where it can be handled manually.

There are three constants: ALWAYS, NEVER, and NORMAL. The meaning of the first two is obvious from their names. The last one, NORMAL, behaves just like ALWAYS except it won't redirect from https URLs to http URLs.

Manually Follow Redirects

As noted in the documentation of HttpClient.Redirect you could instead manually follow a redirect. I'm not well versed in HTTP and how to properly handle all responses so I won't give an example here. But I believe, at a minimum, this requires you:

  1. Check the status code of the response.
  2. If the code indicates a redirect, grab the new URI from the response headers.
  3. If the new URI is relative then resolve it against the request URI.
  4. Send a new request.
  5. Repeat 1-4 as needed.

Obviously configuring the HttpClient to automatically follow redirects is much easier (and less error-prone), but this approach would give you more control.

HttpClient follow redirect

You need to follow the redirect (status 302) returned by the response. Check out this answer.

HttpClient 4 - how to capture last redirect URL

That would be the current URL, which you can get by calling

  HttpGet#getURI();

EDIT: You didn't mention how you are doing redirect. That works for us because we handle the 302 ourselves.

Sounds like you are using DefaultRedirectHandler. We used to do that. It's kind of tricky to get the current URL. You need to use your own context. Here are the relevant code snippets,

        HttpGet httpget = new HttpGet(url);
HttpContext context = new BasicHttpContext();
HttpResponse response = httpClient.execute(httpget, context);
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK)
throw new IOException(response.getStatusLine().toString());
HttpUriRequest currentReq = (HttpUriRequest) context.getAttribute(
ExecutionContext.HTTP_REQUEST);
HttpHost currentHost = (HttpHost) context.getAttribute(
ExecutionContext.HTTP_TARGET_HOST);
String currentUrl = (currentReq.getURI().isAbsolute()) ? currentReq.getURI().toString() : (currentHost.toURI() + currentReq.getURI());

The default redirect didn't work for us so we changed but I forgot what was the problem.

C# HttpClient not handling 304/307 redirects

It turns out this is not a .NET issue at all. I raised this issue on GitHub as all evidence was pointing towards the HttpClientHandler breaking after .NET Core 2.1. Stephen was able to point out to me that before .NET Core 2.1, a Connection: Keep-Alive header is set by default in the HttpClient.

The culprit URLs that I have been testing against seem to require one of the following things to work:

  • A Connection: Keep-Alive header
  • An Accept: */* header, or something more specific to what is being requested
  • The request to be sent using the HTTP/2 protocol.

For reference, to apply one of the Header fixes, use httpClient.DefaultRequestHeaders.Add(string, string), and to set the HTTP protocol, use httpClient.DefaultRequestVersion = HttpVersion.Version20;

This issue turns out to not be a direct issue with C#, but that the default headers that are sent changed after .NET Core 2.1. I found the issue to be reproducible on Postman if you disable all headers (including the Postman token header).

How to handle redirect by httpClient fluent?

The HTTP specification requires entity enclosing methods such as POST and PUT be redirected after human intervention only. HttpClient honors this requirement by default. .

10.3 Redirection 3xx

This class of status code indicates that further action needs to be
taken by the user agent in order to fulfill the request. The action
required MAY be carried out by the user agent without interaction
with the user if and only if the method used in the second request is
GET or HEAD.

...

   If the 302 status code is received in response to a request other
than GET or HEAD, the user agent MUST NOT automatically redirect the
request unless it can be confirmed by the user, since this might
change the conditions under which the request was issued.

One can use a custom redirect strategy to relax restrictions on automatic redirection if necessary.

    DefaultHttpClient client = new DefaultHttpClient();
client.setRedirectStrategy(new LaxRedirectStrategy());
Executor exec = Executor.newInstance(client);
String s = exec.execute(Request
.Post("http://localhost:9999/food2go/booking/placeOrder")
.bodyForm(...)).returnContent().asString();


Related Topics



Leave a reply



Submit