2012-08-26 22 views
17

Spring Servletで、AS-IS(POSTまたはGETコンテンツ)を別のサーバーに転送するHttpServletRequestリクエストがあります。別のサーバーへのHttpServletRequestの転送

Springフレームワークを使用してそれを行うにはどうすればよいでしょうか?

すべての情報を取得して新しいHTTPUrlConnectionを作成する必要がありますか?それとも簡単な方法がありますか?

答えて

4

残念ながら、これを行う簡単な方法はありません。基本的には含めて、要求を再構築する必要があります:任意のユーザエージェントを設定することはできません(

  • 正しいHTTPメソッド
  • リクエストパラメータ
  • リクエストヘッダHTTPUrlConnectionを、「Java/1.*」常に追加され、あなたは多くの作業が、それはあなたメートルにスレッドを1つ占有します、そのような各プロキシ呼び出し以降にスケールしませんもちろんのこと、だ

)のHttpClientをする必要がありますアキネ。

私のアドバイス:生のソケットまたはを使用し、最も低いレベルでインターセプトHTTPプロトコルを使用して、ただちに一部の値(例:Hostヘッダー)を置き換えてください。より多くの文脈を提供することができます、なぜあなたはこれを必要としますか?あなたはさておき、この方法で転送しないべきかどうかの

+0

私はクライアント、中間サーバー、および数台のメインサーバーを持っています。 クライアントは、自分の呼び出しをサーバーにディスパッチする中間サーバーとのみ話します。サーバーは中間サーバーに応答を返し、処理した後、クライアントに応答を返します。 – user1144031

+0

はリクエストパラメータ(2)をコピーするためにリクエストヘッダ(3)とボディ(4)をコピーすべきではありません(ポストパラメータはリクエスト本体の一部ですので、それらがURLの一部になるためです)。両方の手順を実行すると、(HTTPリクエストで)再割り当てされますか – mickeymoon

10

議論は、ここで私はそれをやった方法は次のとおりです。

package com.example.servlets; 

import java.net.HttpURLConnection; 
import java.net.URL; 
import java.util.Enumeration; 

import javax.servlet.http.HttpServlet; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletResponse; 

import com.example.servlets.GlobalConstants; 

@SuppressWarnings("serial") 
public class ForwardServlet extends HttpServlet { 

    @Override 
    public void doGet(HttpServletRequest req, HttpServletResponse resp) { 
     forwardRequest("GET", req, resp); 
    } 

    @Override 
    public void doPost(HttpServletRequest req, HttpServletResponse resp) { 
     forwardRequest("POST", req, resp); 
    } 

    private void forwardRequest(String method, HttpServletRequest req, HttpServletResponse resp) { 
     final boolean hasoutbody = (method.equals("POST")); 

     try { 
      final URL url = new URL(GlobalConstants.CLIENT_BACKEND_HTTPS // no trailing slash 
        + req.getRequestURI() 
        + (req.getQueryString() != null ? "?" + req.getQueryString() : "")); 
      HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 
      conn.setRequestMethod(method); 

      final Enumeration<String> headers = req.getHeaderNames(); 
      while (headers.hasMoreElements()) { 
       final String header = headers.nextElement(); 
       final Enumeration<String> values = req.getHeaders(header); 
       while (values.hasMoreElements()) { 
        final String value = values.nextElement(); 
        conn.addRequestProperty(header, value); 
       } 
      } 

      //conn.setFollowRedirects(false); // throws AccessDenied exception 
      conn.setUseCaches(false); 
      conn.setDoInput(true); 
      conn.setDoOutput(hasoutbody); 
      conn.connect(); 

      final byte[] buffer = new byte[16384]; 
      while (hasoutbody) { 
       final int read = req.getInputStream().read(buffer); 
       if (read <= 0) break; 
       conn.getOutputStream().write(buffer, 0, read); 
      } 

      resp.setStatus(conn.getResponseCode()); 
      for (int i = 0; ; ++i) { 
       final String header = conn.getHeaderFieldKey(i); 
       if (header == null) break; 
       final String value = conn.getHeaderField(i); 
       resp.setHeader(header, value); 
      } 

      while (true) { 
       final int read = conn.getInputStream().read(buffer); 
       if (read <= 0) break; 
       resp.getOutputStream().write(buffer, 0, read); 
      } 
     } catch (Exception e) { 
      e.printStackTrace(); 
      // pass 
     } 
    } 
} 

は明らかにこれは取り扱いなどをエラーに関連して、作業のビットを使用することができますが、それは機能的でした。私はそれを使用することをやめました。私の場合、CLIENT_BACKENDに直接電話するのは、2つの異なるドメイン間でクッキー、認証などを扱うよりも簡単だったからです。

2

私は同じことをする必要もあり、SpringコントローラとRestTemplateで最適ではないものの、より良い解決策を見つけました:Smiley's HTTP Proxy Servlet。利点は、実際にはApacheのmod_proxyと同じようにプロキシとして機能し、メモリ内の完全な要求/応答をキャッシュせずにストリーミングで実行することです。

単純に、別のサーバーにプロキシするパスに新しいサーブレットを登録し、このサーブレットにターゲットホストを初期化パラメータとして指定します。もちろん、あなたがannotation configで行くことができ、

<servlet> 
    <servlet-name>proxy</servlet-name> 
    <servlet-class>org.mitre.dsmiley.httpproxy.ProxyServlet</servlet-class> 
    <init-param> 
     <param-name>targetUri</param-name> 
     <param-value>http://target.uri/target.path</param-value> 
    </init-param> 
</servlet> 
<servlet-mapping> 
    <servlet-name>proxy</servlet-name> 
    <url-pattern>/mapping-path/*</url-pattern> 
</servlet-mapping> 

のか:あなたはweb.xmlの伝統的なWebアプリケーションを使用している場合は、次のようにそれを設定することができます。

あなたは春のブートを使用している場合は、それも簡単です:あなたが唯一必要な設定で、タイプServletRegistrationBeanのBeanを作成する必要があります。

@Bean 
public ServletRegistrationBean proxyServletRegistrationBean() { 
    ServletRegistrationBean bean = new ServletRegistrationBean(
      new ProxyServlet(), "/mapping-path/*"); 
    bean.addInitParameter("targetUri", "http://target.uri/target.path"); 
    return bean; 
} 

この方法で、あなたも春のプロパティを使用することができます環境内で利用可能です。

クラスProxyServletを拡張し、必要に応じてリクエスト/レスポンスヘッダーなどをカスタマイズするためのメソッドをオーバーライドすることもできます。

更新:しばらくSmileyのプロキシサーブレットを使用した後、タイムアウトの問題が発生していましたが、信頼性がありませんでした。 NetflixからZuulに切り替わったが、それ以降は問題はなかった。 Spring Bootで設定するためのチュートリアルは、this linkにあります。

関連する問題