2012-08-26 15 views
17

Ho ricevuto una richiesta HttpServletRequest nel mio Spring Servlet che vorrei inoltrare AS-IS (ovvero il contenuto POST o GET) su un altro server.Inoltra HttpServletRequest a un altro server

Quale sarebbe il modo migliore per farlo utilizzando il framework Spring?

Devo prendere tutte le informazioni e creare un nuovo HTTPUrlConnection? O c'è un modo più semplice?

risposta

4

Sfortunatamente non esiste un modo semplice per farlo. In pratica si dovrà ricostruire la richiesta, tra cui:

  • corretto metodo HTTP
  • parametri di richiesta
  • richieste le intestazioni (HTTPUrlConnection non permette di impostare user agent arbitrari, "Java/1.*" è sempre aggiunto, avrete bisogno di HttpClient)
  • corpo

Questo è un sacco di lavoro, per non parlare che non scala dal momento che ogni tale chiamata delega occuperà un thread sul m achine.

Il mio consiglio: utilizzare socket raw o e intercettare il protocollo HTTP al livello più basso, sostituendo semplicemente alcuni valori (come l'intestazione Host) al volo. Puoi fornire più contesto, perché hai bisogno di questo?

+0

Ho un client, un server intermedio e un paio di server principali. Il client comunica solo con il server intermedio che invia la chiamata a un server. Il server restituisce una risposta al server intermedio, che quindi elabora, quindi restituisce la risposta al client. – user1144031

+0

non dovrebbe copiare le intestazioni delle richieste (3) e il corpo (4) compensare anche i parametri della richiesta di copia (2) (poiché i parametri del post fanno parte del corpo della richiesta, per far sì che siano parte dell'URL)? Sarebbe ridondante (nella richiesta http) se eseguo entrambi i passaggi – mickeymoon

10

discussioni su se dovrebbe non inoltrare questo modo a parte, ecco come ho fatto:

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 
     } 
    } 
} 

Ovviamente questo potrebbe utilizzare un po 'di lavoro per quanto riguarda la gestione degli errori e simili, ma era funzionale . Ho smesso di usarlo, tuttavia, perché nel mio caso era più facile effettuare chiamate direttamente allo CLIENT_BACKEND che a gestire cookie, auth, ecc. In due domini distinti.

2

Avevo anche bisogno di fare lo stesso, e dopo un po 'non ottimale con i controller Spring e RestTemplate, ho trovato una soluzione migliore: Smiley's HTTP Proxy Servlet. Il vantaggio è che fa AS-IS proxy, proprio come Apache mod_proxy, e lo fa in modo streaming, senza memorizzare nella cache la richiesta/risposta completa nella memoria.

Semplicemente, si registra un nuovo servlet nel percorso che si desidera eseguire il proxy su un altro server e si fornisce a questo servlet l'host di destinazione come parametro di inizializzazione. Se si utilizza un'applicazione web tradizionale con un web.xml, è possibile configurarlo come segue:

<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> 

o, naturalmente, si può andare con la annotation config.

Se si utilizza Primavera di avvio, è ancora più facile: Hai solo bisogno di creare un chicco di tipo ServletRegistrationBean, con la configurazione richiesta:

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

In questo modo, è anche possibile utilizzare le proprietà di primavera che sono disponibili nell'ambiente.

È anche possibile estendere la classe ProxyServlet e sovrascrivere i suoi metodi per personalizzare le intestazioni di richiesta/risposta ecc. Nel caso sia necessario.

Aggiornamento: Dopo aver utilizzato il servlet proxy di Smiley per un po ', abbiamo avuto alcuni problemi di timeout, non funzionava in modo affidabile. Passato a Zuul da Netflix, non ho avuto problemi dopo. Un tutorial sulla configurazione con Spring Boot può essere trovato su this link.

Problemi correlati