Commons HttpClient - 如何向GET/POST请求中添加查询字符串参数

87

我正在使用commons HttpClient向Spring servlet发起http调用。我需要在查询字符串中添加一些参数。因此,我按照以下方式进行:

HttpRequestBase request = new HttpGet(url);
HttpParams params = new BasicHttpParams();
params.setParameter("key1", "value1");
params.setParameter("key2", "value2");
params.setParameter("key3", "value3");
request.setParams(params);
HttpClient httpClient = new DefaultHttpClient();
httpClient.execute(request);

但是,当我尝试使用servlet读取参数时,会出现以下情况

((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest().getParameter("key");

它返回null。实际上,parameterMap完全为空。当我在创建HttpGet请求之前手动将参数附加到URL时,参数可在servlet中使用。当我使用带有queryString的URL从浏览器中击中servlet时也是如此。

这里出了什么问题?在httpclient 3.x中,GetMethod具有setQueryString()方法来附加查询字符串。4.x中的等效方法是什么?

7个回答

151

以下是使用HttpClient 4.2及更高版本添加查询字符串参数的方法:

URIBuilder builder = new URIBuilder("http://example.com/");
builder.setParameter("parts", "all").setParameter("action", "finish");

HttpPost post = new HttpPost(builder.build());

生成的URI将类似于:

http://example.com/?parts=all&action=finish

34

如果您想在创建请求之后添加查询参数,请尝试将 HttpRequest 强制转换为 HttpBaseRequest。 然后,您可以更改强制转换的请求的 URI:

HttpGet someHttpGet = new HttpGet("http://google.de");

URI uri = new URIBuilder(someHttpGet.getURI()).addParameter("q",
        "That was easy!").build();

((HttpRequestBase) someHttpGet).setURI(uri);

13

HttpParams 接口的作用不是用于指定查询字符串参数,而是用于指定 HttpClient 对象的运行时行为。

如果您想传递查询字符串参数,需要自己在 URL 上组装它们,例如:

new HttpGet(url + "key1=" + value1 + ...);

记得先对值进行编码(使用 URLEncoder)。


3
已经创建了请求对象后是否有添加查询字符串参数的方法?如果没有,是否有其他标准方法可以传递参数到Servlet以供任何请求方法使用(如GET/PUT/POST)? - Oceanic

5

我正在使用httpclient 4.4。

对于solr查询,我使用了以下方法,并且它有效。

NameValuePair nv2 = new BasicNameValuePair("fq","(active:true) AND (category:Fruit OR category1:Vegetable)");
nvPairList.add(nv2);
NameValuePair nv3 = new BasicNameValuePair("wt","json");
nvPairList.add(nv3);
NameValuePair nv4 = new BasicNameValuePair("start","0");
nvPairList.add(nv4);
NameValuePair nv5 = new BasicNameValuePair("rows","10");
nvPairList.add(nv5);

HttpClient client = HttpClientBuilder.create().build();
HttpGet request = new HttpGet(url);
URI uri = new URIBuilder(request.getURI()).addParameters(nvPairList).build();
            request.setURI(uri);

HttpResponse response = client.execute(request);    
if (response.getStatusLine().getStatusCode() != 200) {

}

BufferedReader br = new BufferedReader(
                             new InputStreamReader((response.getEntity().getContent())));

String output;
System.out.println("Output  .... ");
String respStr = "";
while ((output = br.readLine()) != null) {
    respStr = respStr + output;
    System.out.println(output);
}

这个答案需要更多的解释才能更好地理解。 - D. Ben Knoble
这个答案对我的情况非常有用,因为我找不到一种方法来使用完整的URI(例如http://myserver/apipath)初始化URIBuilder。当使用它进行初始化时,URIBuilder只使用了http://myserver并忽略了/apipath。URI是外部提供的,所以我不想手动解析它以便使用URIBuilder。 - nuoritoveri

2

这种方法可以使用,但是当你动态获取参数时,比如SOLR搜索查询(例如),有时会有1个、2个、3个或更多的参数,那么这种方法就不适用了。

下面是一个更加灵活的解决方案。粗糙但可改进。

public static void main(String[] args) {

    String host = "localhost";
    String port = "9093";

    String param = "/10-2014.01?description=cars&verbose=true&hl=true&hl.simple.pre=<b>&hl.simple.post=</b>";
    String[] wholeString = param.split("\\?");
    String theQueryString = wholeString.length > 1 ? wholeString[1] : "";

    String SolrUrl = "http://" + host + ":" + port + "/mypublish-services/carclassifications/" + "loc";

    GetMethod method = new GetMethod(SolrUrl );

    if (theQueryString.equalsIgnoreCase("")) {
        method.setQueryString(new NameValuePair[]{
        });
    } else {
        String[] paramKeyValuesArray = theQueryString.split("&");
        List<String> list = Arrays.asList(paramKeyValuesArray);
        List<NameValuePair> nvPairList = new ArrayList<NameValuePair>();
        for (String s : list) {
            String[] nvPair = s.split("=");
            String theKey = nvPair[0];
            String theValue = nvPair[1];
            NameValuePair nameValuePair = new NameValuePair(theKey, theValue);
            nvPairList.add(nameValuePair);
        }
        NameValuePair[] nvPairArray = new NameValuePair[nvPairList.size()];
        nvPairList.toArray(nvPairArray);
        method.setQueryString(nvPairArray);       // Encoding is taken care of here by setQueryString

    }
}

GetMethod?它是来自httpclient吗?因为问题是关于它的。 - Walfrat
是的,来自org.apache.commons.httpclient.methods。 - Rahul Saini
啊,是的,但似乎这是针对3.x版本的,在4.x中,现在是org.apache.http.client.methods.HttpGet。 - Walfrat

0

我正在使用Java 8和Apache HttpClient 4.5.13

HashMap<String, String> customParams = new HashMap<>();
customParams.put("param1", "ABC");
customParams.put("param2", "123");

URIBuilder uriBuilder = new URIBuilder(baseURL);

for (String paramKey : customParams.keySet()) {
    uriBuilder.addParameter(paramKey, customParams.get(paramKey));
}

System.out.println(uriBuilder.build().toASCIIString()); // ENCODED URL
System.out.println(uriBuilder.build().toString); // NORMAL URL

使用DTO的完整示例

public class HttpResponseDTO {
    private Integer statusCode;
    private String body;
    private String errorMessage;
    
    public Integer getStatusCode() {
        return statusCode;
    }
    public void setStatusCode(Integer statusCode) {
        this.statusCode = statusCode;
    }
    public String getBody() {
        return body;
    }
    public void setBody(String body) {
        this.body = body;
    }
    public String getErrorMessage() {
        return errorMessage;
    }
    public void setErrorMessage(String errorMessage) {
        this.errorMessage = errorMessage;
    }
}

    /**
     * 
     * @param destinationURL
     * @param params
     * @param headers
     * @return HttpResponseDTO
     */
    public static HttpResponseDTO get(String baseURL, Boolean encodeURL, HashMap<String, String> params, HashMap<String, String> headers) {

        final HttpResponseDTO httpResponseDTO = new HttpResponseDTO();      

        // ADD PARAMS IF
        if (params != null && Boolean.FALSE.equals(params.isEmpty())) {
            URIBuilder uriBuilder;
            try {
                uriBuilder = new URIBuilder(baseURL);

                for (String paramKey : params.keySet()) {
                    uriBuilder.addParameter(paramKey, params.get(paramKey));
                }

                // CODIFICAR URL ?
                if (Boolean.TRUE.equals(encodeURL)) {
                    baseURL = uriBuilder.build().toASCIIString();
                } else {
                    baseURL = uriBuilder.build().toString();
                }
            } catch (URISyntaxException e) {
                httpResponseDTO.setStatusCode(500);
                httpResponseDTO.setErrorMessage("ERROR AL CODIFICAR URL: " + e.getMessage());
                return httpResponseDTO;
            }
        }


        // HACER PETICION HTTP
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {            
            final HttpGet get = new HttpGet(baseURL);   

            // ADD HEADERS
            if (headers != null && Boolean.FALSE.equals(headers.isEmpty())) {           
                for (String headerKey : headers.keySet()) {
                    get.setHeader(headerKey, headers.get(headerKey));
                }           
            }

            try (CloseableHttpResponse response = httpClient.execute(get);) {
                HttpEntity httpEntity = response.getEntity();
                if (httpEntity != null) {
                    httpResponseDTO.setBody(EntityUtils.toString(httpEntity));
                    httpResponseDTO.setStatusCode(response.getStatusLine().getStatusCode());
                }
            } catch(Exception e) {
                httpResponseDTO.setStatusCode(500);
                httpResponseDTO.setErrorMessage(e.getMessage());
                return httpResponseDTO;
            }
        } catch(Exception e) {
            httpResponseDTO.setStatusCode(500);
            httpResponseDTO.setErrorMessage(e.getMessage());
            return httpResponseDTO;
        }

        return httpResponseDTO;
    }

1
这基本上是被接受的答案,但加入了一些用于测试的示例代码。 - HardcoreGamer

0
这是我实现URL构建器的方式。 我创建了一个服务类来提供URL参数。
public interface ParamsProvider {

    String queryProvider(List<BasicNameValuePair> params);

    String bodyProvider(List<BasicNameValuePair> params);
}

以下是方法的实现

@Component
public class ParamsProviderImp implements ParamsProvider {
    @Override
    public String queryProvider(List<BasicNameValuePair> params) {
        StringBuilder query = new StringBuilder();
        AtomicBoolean first = new AtomicBoolean(true);
        params.forEach(basicNameValuePair -> {
            if (first.get()) {
                query.append("?");
                query.append(basicNameValuePair.toString());
                first.set(false);
            } else {
                query.append("&");
                query.append(basicNameValuePair.toString());
            }
        });
        return query.toString();
    }

    @Override
    public String bodyProvider(List<BasicNameValuePair> params) {
        StringBuilder body = new StringBuilder();
        AtomicBoolean first = new AtomicBoolean(true);
        params.forEach(basicNameValuePair -> {
            if (first.get()) {
                body.append(basicNameValuePair.toString());
                first.set(false);
            } else {
                body.append("&");
                body.append(basicNameValuePair.toString());
            }
        });
        return body.toString();
    }
}


当我们需要URL的查询参数时,我只需调用服务并构建它。 以下是一个示例。
Class Mock{
@Autowired
ParamsProvider paramsProvider;
 String url ="http://www.google.lk";
// For the query params price,type
 List<BasicNameValuePair> queryParameters = new ArrayList<>();
 queryParameters.add(new BasicNameValuePair("price", 100));
 queryParameters.add(new BasicNameValuePair("type", "L"));
url = url+paramsProvider.queryProvider(queryParameters);
// You can use it in similar way to send the body params using the bodyProvider

}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接