web-dev-qa-db-ja.com

Spring MVCレストサービスリダイレクト/フォワード/プロキシ

RESTサービスを公開するために、Spring MVCフレームワークを使用してWebアプリケーションを構築しました。例:

@Controller
@RequestMapping("/movie")
public class MovieController {

@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public @ResponseBody Movie getMovie(@PathVariable String id, @RequestBody user) {

    return dataProvider.getMovieById(user,id);

}

ここで、アプリケーションを展開する必要がありますが、次の問題があります。クライアントは、アプリケーションが存在するコンピューターに直接アクセスできません(ファイアウォールがあります)。したがって、実際の休憩サービスを呼び出すプロキシマシン(クライアントからアクセス可能)にリダイレクトレイヤーが必要です。

RestTemplateを使用して新しい呼び出しを作成しようとしました:例:

@Controller
@RequestMapping("/movieProxy")
public class MovieProxyController {

    private String address= "http://xxx.xxx.xxx.xxx:xx/MyApp";

    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    public @ResponseBody Movie getMovie(@PathVariable String id,@RequestBody user,final HttpServletResponse response,final HttpServletRequest request) {

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        RestTemplate restTemplate = new RestTemplate();
        return restTemplate.exchange( address+ request.getPathInfo(), request.getMethod(), new HttpEntity<T>(user, headers), Movie.class);

}

これは問題ありませんが、resttemplateを使用するには、コントローラーの各メソッドを書き直す必要があります。また、これにより、プロキシマシンで冗長なシリアル化/逆シリアル化が発生します。

Restemplateを使用して汎用関数を記述しようとしましたが、うまくいきませんでした。

@Controller
@RequestMapping("/movieProxy")
public class MovieProxyController {

    private String address= "http://xxx.xxx.xxx.xxx:xx/MyApp";

    @RequestMapping(value = "/**")
    public ? redirect(final HttpServletResponse response,final HttpServletRequest request) {        
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        RestTemplate restTemplate = new RestTemplate();
        return restTemplate.exchange( address+ request.getPathInfo(), request.getMethod(), ? , ?);

}

要求および応答オブジェクトで機能するresttemplateのメソッドが見つかりませんでした。

また、スプリングリダイレクトとフォワードを試しました。しかし、リダイレクトはリクエストのクライアントIPアドレスを変更しないため、この場合は役に立たないと思います。別のURLにも転送できませんでした。

これを達成するためのより適切な方法はありますか?前もって感謝します。

42
nilgun

これですべてのリクエストをミラーリング/プロキシできます:

private String server = "localhost";
private int port = 8080;

@RequestMapping("/**")
@ResponseBody
public String mirrorRest(@RequestBody String body, HttpMethod method, HttpServletRequest request) throws URISyntaxException
{
    URI uri = new URI("http", null, server, port, request.getRequestURI(), request.getQueryString(), null);

    ResponseEntity<String> responseEntity =
        restTemplate.exchange(uri, method, new HttpEntity<String>(body), String.class);

    return responseEntity.getBody();
}

これはヘッダーをミラーリングしません。

57
koe

Netflix Zuulを使用して、スプリングアプリケーションに着信するリクエストを別のスプリングアプリケーションにルーティングできます。

2つのアプリケーションがあるとします:1.songs-app、2.api-gateway

Api-gatewayアプリケーションで、最初にzuul dependecyを追加してから、次のようにapplication.ymlでルーティングルールを簡単に定義できます。

pom.xml

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zuul</artifactId>
    <version>LATEST</version>
</dependency>

application.yml

server:
  port: 8080
zuul:
  routes:
    foos:
      path: /api/songs/**
      url: http://localhost:8081/songs/

最後に、api-gatewayアプリケーションを次のように実行します。

@EnableZuulProxy
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

これで、ゲートウェイはすべての/api/songs/リクエストをhttp://localhost:8081/songs/にルーティングします。

実際の例はこちらです: https://github.com/muatik/spring-playground/tree/master/spring-api-gateway

別のリソース: http://www.baeldung.com/spring-rest-with-zuul-proxy

16
Muatik

以下に、元の回答の修正版を示します。これは4つの点で異なります。

  1. 要求本文を必須にすることはなく、GET要求が失敗することもありません。
  2. 元のリクエストに存在するすべてのヘッダーをコピーします。別のプロキシ/ Webサーバーを使用している場合、コンテンツの長さ/ gzip圧縮が原因で問題が発生する可能性があります。ヘッダーを本当に必要なものに制限します。
  3. notクエリパラメータまたはパスを再エンコードします。とにかくエンコードされると期待しています。 URLの他の部分もエンコードされる場合があることに注意してください。その場合は、UriComponentsBuilderの可能性を最大限に活用してください。
  4. サーバーから適切にエラーコードを返します。
@RequestMapping("/**")
public ResponseEntity mirrorRest(@RequestBody(required = false) String body, 
    HttpMethod method, HttpServletRequest request, HttpServletResponse response) 
    throws URISyntaxException {
    String requestUrl = request.getRequestURI();

    URI uri = new URI("http", null, server, port, null, null, null);
    uri = UriComponentsBuilder.fromUri(uri)
                              .path(requestUrl)
                              .query(request.getQueryString())
                              .build(true).toUri();

    HttpHeaders headers = new HttpHeaders();
    Enumeration<String> headerNames = request.getHeaderNames();
    while (headerNames.hasMoreElements()) {
        String headerName = headerNames.nextElement();
        headers.set(headerName, request.getHeader(headerName));
    }

    HttpEntity<String> httpEntity = new HttpEntity<>(body, headers);
    RestTemplate restTemplate = new RestTemplate();
    try {
        return restTemplate.exchange(uri, method, httpEntity, String.class);
    } catch(HttpStatusCodeException e) {
        return ResponseEntity.status(e.getRawStatusCode())
                             .headers(e.getResponseHeaders())
                             .body(e.getResponseBodyAsString());
    }
}
12
Veluria

oauth2を使用したプロキシコントローラー

@RequestMapping("v9")
@RestController
@EnableConfigurationProperties
public class ProxyRestController {
    Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    OAuth2ProtectedResourceDetails oAuth2ProtectedResourceDetails;

    @Autowired
    private ClientCredentialsResourceDetails clientCredentialsResourceDetails;

    @Autowired
    OAuth2RestTemplate oAuth2RestTemplate;


    @Value("${gateway.url:http://gateway/}")
    String gatewayUrl;

    @RequestMapping(value = "/proxy/**")
    public String proxy(@RequestBody(required = false) String body, HttpMethod method, HttpServletRequest request, HttpServletResponse response,
                        @RequestHeader HttpHeaders headers) throws ServletException, IOException, URISyntaxException {

        body = body == null ? "" : body;
        String path = request.getRequestURI();
        String query = request.getQueryString();
        path = path.replaceAll(".*/v9/proxy", "");
        StringBuffer urlBuilder = new StringBuffer(gatewayUrl);
        if (path != null) {
            urlBuilder.append(path);
        }
        if (query != null) {
            urlBuilder.append('?');
            urlBuilder.append(query);
        }
        URI url = new URI(urlBuilder.toString());
        if (logger.isInfoEnabled()) {
            logger.info("url: {} ", url);
            logger.info("method: {} ", method);
            logger.info("body: {} ", body);
            logger.info("headers: {} ", headers);
        }
        ResponseEntity<String> responseEntity
                = oAuth2RestTemplate.exchange(url, method, new HttpEntity<String>(body, headers), String.class);
        return responseEntity.getBody();
    }


    @Bean
    @ConfigurationProperties("security.oauth2.client")
    @ConditionalOnMissingBean(ClientCredentialsResourceDetails.class)
    public ClientCredentialsResourceDetails clientCredentialsResourceDetails() {
        return new ClientCredentialsResourceDetails();
    }

    @Bean
    @ConditionalOnMissingBean
    public OAuth2RestTemplate oAuth2RestTemplate() {
        return new OAuth2RestTemplate(clientCredentialsResourceDetails);
    }


1
love adu

Mod_proxyのような低レベルのソリューションを使用して回避できる場合は、より簡単な方法ですが、より多くの制御(セキュリティ、翻訳、ビジネスロジックなど)が必要な場合は、Apache Camelを見てください。 http://camel.Apache.org/how-to-use-camel-as-a-http-proxy-between-a-client-and-server.html

1
Chris H.

実際に呼び出しをリダイレクトするjetty transparent proxyのようなものが必要であり、必要に応じてリクエストを上書きする機会があります。詳細は http://reanimatter.com/2016/01/25/embedded-jetty-as-http-proxy/ で入手できます。

0
krmanish007