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 the3XX
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:
- Check the status code of the response.
- If the code indicates a redirect, grab the new URI from the response headers.
- If the new URI is relative then resolve it against the request URI.
- Send a new request.
- 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
Java's Date(...) Constructor Is Deprecated; What Does That Mean
Singleton Design Pattern: Pitfalls
How to Keep the Iteration Order of a List When Using Collections.Tomap() on a Stream
Parsing JSON with Gson, Object Sometimes Contains List Sometimes Contains Object
Simple Way to Count Character Occurrences in a String
Sorting a List with Stream.Sorted() in Java
Validation of a List of Objects in Spring
Java 8 Method Reference Unhandled Exception
What Is the Replacement for Javax.Activation Package in Java 9
Jdbc Connection to Mssql Server in Windows Authentication Mode
No Dialect Mapping for Jdbc Type: 1111
Why Is Javac Failing on @Override Annotation
Highlights Substring in the Tablecell(S) Which Is Using for Jtable Filetering