如何在 Spring MVC 中代理 HTTP 请求?
How to proxy HTTP requests in Spring MVC?
我有一个基于 Spring MVC 构建的应用程序。
我想编写一个简单的代理来处理如下请求:
- 向某个特定服务器发送相同的 HTTP 请求
- 捕获来自该特定服务器的 HTTP 响应
- return 对请求客户的相同回答
这是我目前得到的:
public void proxyRequest(HttpServletRequest request, HttpServletResponse response) {
try {
HttpUriRequest proxiedRequest = createHttpUriRequest(request);
HttpResponse proxiedResponse = httpClient.execute(proxiedRequest);
writeToResponse(proxiedResponse, response);
} catch (URISyntaxException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private void writeToResponse(HttpResponse proxiedResponse, HttpServletResponse response){
for(Header header : proxiedResponse.getAllHeaders()){
response.addHeader(header.getName(), header.getValue());
}
OutputStream os = null;
InputStream is = null;
try {
is = proxiedResponse.getEntity().getContent();
os = response.getOutputStream();
IOUtils.copy(is, os);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (is != null) {
try {
is.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
private HttpUriRequest createHttpUriRequest(HttpServletRequest request) throws URISyntaxException{
URI uri = new URI(geoserverConfig.getUrl()+"/wms?"+request.getQueryString());
RequestBuilder rb = RequestBuilder.create(request.getMethod());
rb.setUri(uri);
Enumeration<String> headerNames = request.getHeaderNames();
while(headerNames.hasMoreElements()){
String headerName = headerNames.nextElement();
String headerValue = request.getHeader(headerName);
rb.addHeader(headerName, headerValue);
}
HttpUriRequest proxiedRequest = rb.build();
return proxiedRequest;
}
它工作正常,但不是在所有情况下。我检查了 Chrome 的网络监视器,发现一些使用此代理的请求失败了。
这里是 headers 的示例失败请求响应:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Disposition: inline; filename=JEDN_EWID.png
Transfer-Encoding: chunked
Date: Thu, 16 Jul 2015 10:31:49 GMT
Content-Type: image/png;charset=UTF-8
Content-Length: 6727
这里是 headers 的示例成功请求响应:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Disposition: inline; filename=JEDN_EWID.png
Transfer-Encoding: chunked
Date: Thu, 16 Jul 2015 10:31:49 GMT
Content-Type: image/png;charset=UTF-8
Transfer-Encoding: chunked
更重要的是Chrome在控制台中抛出错误:
GET http://localhost:8080/<rest of url> net::ERR_INVALID_CHUNKED_ENCODING
我正在代理的请求是 WMS GetMap 请求,我的代理正在将它们转发到隐藏的 Geoserver。我注意到失败的请求应该 return 透明的 512x512 .png 图像,这些图像都是空的。成功的 return 512x512 .png 图像不仅透明而且包含一些颜色。
看起来远程服务器在大小变得太大时以分块响应响应,Apache HttpClient 库将所有分块元素收集在一个大的 HttpResponse 中,但留下 Transfer-Encoding: chunked
header.
我无法测试,但您应该过滤掉 Transfer-Encoding: chunked
以解决此问题:
private void writeToResponse(HttpResponse proxiedResponse, HttpServletResponse response){
for(Header header : proxiedResponse.getAllHeaders()){
if ((! header.getName().equals("Transfer-Encoding")) || (! header.getValue().equals("chunked"))) {
response.addHeader(header.getName(), header.getValue());
}
}
...
我有一个基于 Spring MVC 构建的应用程序。
我想编写一个简单的代理来处理如下请求:
- 向某个特定服务器发送相同的 HTTP 请求
- 捕获来自该特定服务器的 HTTP 响应
- return 对请求客户的相同回答
这是我目前得到的:
public void proxyRequest(HttpServletRequest request, HttpServletResponse response) {
try {
HttpUriRequest proxiedRequest = createHttpUriRequest(request);
HttpResponse proxiedResponse = httpClient.execute(proxiedRequest);
writeToResponse(proxiedResponse, response);
} catch (URISyntaxException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private void writeToResponse(HttpResponse proxiedResponse, HttpServletResponse response){
for(Header header : proxiedResponse.getAllHeaders()){
response.addHeader(header.getName(), header.getValue());
}
OutputStream os = null;
InputStream is = null;
try {
is = proxiedResponse.getEntity().getContent();
os = response.getOutputStream();
IOUtils.copy(is, os);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (is != null) {
try {
is.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
private HttpUriRequest createHttpUriRequest(HttpServletRequest request) throws URISyntaxException{
URI uri = new URI(geoserverConfig.getUrl()+"/wms?"+request.getQueryString());
RequestBuilder rb = RequestBuilder.create(request.getMethod());
rb.setUri(uri);
Enumeration<String> headerNames = request.getHeaderNames();
while(headerNames.hasMoreElements()){
String headerName = headerNames.nextElement();
String headerValue = request.getHeader(headerName);
rb.addHeader(headerName, headerValue);
}
HttpUriRequest proxiedRequest = rb.build();
return proxiedRequest;
}
它工作正常,但不是在所有情况下。我检查了 Chrome 的网络监视器,发现一些使用此代理的请求失败了。
这里是 headers 的示例失败请求响应:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Disposition: inline; filename=JEDN_EWID.png
Transfer-Encoding: chunked
Date: Thu, 16 Jul 2015 10:31:49 GMT
Content-Type: image/png;charset=UTF-8
Content-Length: 6727
这里是 headers 的示例成功请求响应:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Disposition: inline; filename=JEDN_EWID.png
Transfer-Encoding: chunked
Date: Thu, 16 Jul 2015 10:31:49 GMT
Content-Type: image/png;charset=UTF-8
Transfer-Encoding: chunked
更重要的是Chrome在控制台中抛出错误:
GET http://localhost:8080/<rest of url> net::ERR_INVALID_CHUNKED_ENCODING
我正在代理的请求是 WMS GetMap 请求,我的代理正在将它们转发到隐藏的 Geoserver。我注意到失败的请求应该 return 透明的 512x512 .png 图像,这些图像都是空的。成功的 return 512x512 .png 图像不仅透明而且包含一些颜色。
看起来远程服务器在大小变得太大时以分块响应响应,Apache HttpClient 库将所有分块元素收集在一个大的 HttpResponse 中,但留下 Transfer-Encoding: chunked
header.
我无法测试,但您应该过滤掉 Transfer-Encoding: chunked
以解决此问题:
private void writeToResponse(HttpResponse proxiedResponse, HttpServletResponse response){
for(Header header : proxiedResponse.getAllHeaders()){
if ((! header.getName().equals("Transfer-Encoding")) || (! header.getValue().equals("chunked"))) {
response.addHeader(header.getName(), header.getValue());
}
}
...