HTTPClient重定向

使用的HTTPClient版本4.5.2,默认使用的重定向策略是DefaultRedirectStrategy
如果是使用的POST,然后301重定向是不会执行重定向后的请求的,而是直接获取到301状态码。

主要的代码:

    public boolean isRedirected(
            final HttpRequest request,
            final HttpResponse response,
            final HttpContext context) throws ProtocolException {
        Args.notNull(request, "HTTP request");
        Args.notNull(response, "HTTP response");

        final int statusCode = response.getStatusLine().getStatusCode();
        final String method = request.getRequestLine().getMethod();
        final Header locationHeader = response.getFirstHeader("location");
        switch (statusCode) {
        case HttpStatus.SC_MOVED_TEMPORARILY:
            return isRedirectable(method) && locationHeader != null;
        case HttpStatus.SC_MOVED_PERMANENTLY:
        case HttpStatus.SC_TEMPORARY_REDIRECT:
            return isRedirectable(method);
        case HttpStatus.SC_SEE_OTHER:
            return true;
        default:
            return false;
        } //end of switch
    }
    private static final String[] REDIRECT_METHODS = new String[] {
        HttpGet.METHOD_NAME,
        HttpHead.METHOD_NAME
    };
    protected boolean isRedirectable(final String method) {
        for (final String m: REDIRECT_METHODS) {
            if (m.equalsIgnoreCase(method)) {
                return true;
            }
        }
        return false;
    }

除了303状态码,其他的就判断请求方法,请求方法支持的只有GETHEAD两种,所以POST请求是不会被重定向,除非返回的是303状态码。

下面是一个我实现的重定向策略:

package com.acgist.utils.http;

import java.net.URI;

import org.apache.http.Header;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.ProtocolException;
import org.apache.http.client.RedirectStrategy;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.protocol.HttpContext;

/**
 * 重定向策略
 * 307重定向请求,使用原请求方法,POST携带POST参数
 * 303/302/301重定向到GET请求,POST请求将不携带POST参数
 */
public class AcgistRedirectStrategy implements RedirectStrategy {

	@Override
	public boolean isRedirected(final HttpRequest request, final HttpResponse response, final HttpContext context) throws ProtocolException {
//		final String method = request.getRequestLine().getMethod();
		final int statusCode = response.getStatusLine().getStatusCode();
		switch (statusCode) {
			case HttpStatus.SC_TEMPORARY_REDIRECT:
			case HttpStatus.SC_MOVED_PERMANENTLY:
			case HttpStatus.SC_MOVED_TEMPORARILY:
			case HttpStatus.SC_SEE_OTHER:
				return true;
			default:
				return false;
		}
	}

	@Override
	public HttpUriRequest getRedirect(final HttpRequest request, final HttpResponse response, final HttpContext context) throws ProtocolException {
		final URI uri = getLocationURI(request, response, context);
//		final String method = request.getRequestLine().getMethod();
		final int status = response.getStatusLine().getStatusCode();
		if (status == HttpStatus.SC_TEMPORARY_REDIRECT) { // 307使用原始request
			return RequestBuilder.copy(request).setUri(uri).build();
		} else { // 其他直接转换为GET请求,参数使用返回的location携带,原请求参数将会丢失
			return new HttpGet(uri);
		}
	}
	
	private URI getLocationURI(final HttpRequest request, final HttpResponse response, final HttpContext context) throws ProtocolException {
		final Header locationHeader = response.getFirstHeader("location");
		if (locationHeader == null) {
			throw new ProtocolException("未返回重定向location首部");
		}
		final String location = locationHeader.getValue();
		final URI requestURI = URI.create(request.getRequestLine().getUri());
		return requestURI.resolve(location);
	}
	
}