2012-08-26 3 views
17

J'ai reçu une requête HttpServletRequest dans ma servlet Spring que je voudrais transférer AS-IS (c'est-à-dire le contenu POST ou GET) à un serveur différent.Transférer HttpServletRequest vers un serveur différent

Quelle serait la meilleure façon de le faire en utilisant Spring framework?

Dois-je saisir toutes les informations et construire un nouveau HTTPUrlConnection? ou y a-t-il un moyen plus facile?

Répondre

4

Malheureusement, il n'y a pas de moyen facile de le faire. Fondamentalement, vous devrez reconstruire la demande, y compris:

  • méthode HTTP correcte
  • paramètres de la requête
  • têtes de requêtes (HTTPUrlConnection ne permet pas de définir l'agent utilisateur arbitraire, « Java/1.* » est toujours ajouté, vous aurez besoin HttpClient)
  • corps

C'est beaucoup de travail, sans parler il ne sera pas l'échelle puisque chaque appel proxy occupera un fil sur votre m achine.

Mon conseil: utiliser des sockets raw ou et intercepter le protocole HTTP au plus bas niveau, juste en remplaçant certaines valeurs (comme Host header) à la volée. Pouvez-vous fournir plus de contexte, pourquoi avez-vous besoin de cela?

+0

J'ai un client, un serveur intermédiaire et deux serveurs principaux. Le client ne parle qu'au serveur intermédiaire qui envoie son appel à un serveur. Le serveur renvoie une réponse au serveur intermédiaire, qu'il traite ensuite, puis renvoie la réponse au client. – user1144031

+0

ne devrait pas copier les en-têtes de requête (3) et le corps (4) aussi pour copier les paramètres de demande (2) (puisque les paramètres post font partie du corps de la requête, pour qu'ils fassent partie de l'URL)? Serait-il redtuant (dans la requête http) si j'effectue les deux étapes – mickeymoon

10

Discussions de si vous devez ne transmettant ainsi de côté, voici comment je l'ai fait:

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

Il est évident que cela pourrait utiliser un peu de travail en ce qui concerne la gestion des erreurs, etc., mais il était fonctionnel . J'ai arrêté de l'utiliser, car il était plus facile dans mon cas de faire des appels directement au CLIENT_BACKEND que de traiter les cookies, auth, etc. sur deux domaines distincts.

2

Je devais aussi faire la même chose, et après certains non optimaux avec les contrôleurs Spring et RestTemplate, j'ai trouvé une meilleure solution: Smiley's HTTP Proxy Servlet. L'avantage est, il fait vraiment AS-IS proxy, tout comme mod_proxy d'Apache, et il le fait d'une manière en streaming, sans mettre en cache la demande/réponse complète dans la mémoire.

Simplement, vous enregistrez une nouvelle servlet dans le chemin que vous souhaitez attribuer à un autre serveur, et donnez à cette servlet l'hôte cible en tant que paramètre init. Si vous utilisez une application web traditionnelle avec un web.xml, vous pouvez le configurer comme suit:

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

ou, bien sûr, vous pouvez aller avec le annotation config.

Si vous utilisez Spring Boot, il est encore plus facile: Il vous suffit de créer un bean de type ServletRegistrationBean, avec la configuration requise:

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

De cette façon, vous pouvez également utiliser les propriétés de printemps qui sont disponibles dans l'environnement.

Vous pouvez même étendre la classe ProxyServlet et remplacer ses méthodes pour personnaliser les en-têtes de requête/réponse, etc., au cas où vous en auriez besoin.

Mise à jour: Après avoir utilisé le servlet proxy de Smiley pendant un certain temps, nous avons eu quelques problèmes de timeout, cela ne fonctionnait pas de manière fiable. Commuté à Zuul de Netflix, n'a pas eu de problèmes après cela. Un tutoriel sur la configuration avec Spring Boot peut être trouvé sur this link.

Questions connexes