如何在 Spring MVC 中代理 HTTP 请求?

Posted

技术标签:

【中文标题】如何在 Spring MVC 中代理 HTTP 请求?【英文标题】:How to proxy HTTP requests in Spring MVC? 【发布时间】:2015-10-05 18:33:05 【问题描述】:

我有一个基于 Spring MVC 构建的应用程序。

我想写一个简单的代理来处理请求如下:

    向某个特定的服务器发送相同的 HTTP 请求 从这个特定的服务器捕获 HTTP 响应 向请求的客户返回相同的答案

这是我目前得到的:

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 的网络监视器,使用此代理的一些请求失败了。

以下是示例失败请求响应的标头:

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

这里是示例成功请求响应的标头:

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。我注意到失败的请求应该返回透明的 512x512 .png 图像,这些图像都是空的。成功的会返回 512x512 的 .png 图像,这些图像不仅是透明的,而且还包含一些颜色。

【问题讨论】:

【参考方案1】:

看起来,当大小变得太大时,远程服务器会以分块响应进行响应,Apache HttpClient 库将所有​​分块元素收集在一个大 HttpResponse 中,但留下了 Transfer-Encoding: chunked 标头。

我无法测试,但你应该过滤掉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());
        
    
    ...

【讨论】:

很好的答案。谢谢。我深入研究了 HTTP 文档并发现了这一点:“代理/网关必须在通过符合 MIME 的协议转发消息之前删除任何传输编码。” w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.4.6

以上是关于如何在 Spring MVC 中代理 HTTP 请求?的主要内容,如果未能解决你的问题,请参考以下文章

nginx 代理https后,spring mvc web应用redirect https变成http

如何在spring mvc中启用ZuulProxy

Http请求中Content-Type讲解以及在Spring MVC中的应用

spring mvc 默认请求是get 还是 post

如何在Spring MVC中基于http请求头启用json的动态漂亮打印?

如何访问 Spring MVC REST 控制器中的 HTTP 标头信息?