1. 概要

この記事では、フロントエンドアプリケーションと個別にデプロイされたRESTAPIとの間の通信について説明します。

目標は、ブラウザのCORSと同一生成元ポリシーの制限を回避し、同じ生成元を共有していなくてもUIがAPIを呼び出せるようにすることです。

基本的に、UIアプリケーションと単純なREST APIの2つの別個のアプリケーションを作成し、UIアプリケーションでZuulプロキシを使用してRESTAPIへの呼び出しをプロキシします。

Zuulは、NetflixによるJVMベースのルーターおよびサーバー側のロードバランサーです。 また、Spring Cloudは、組み込みのZuulプロキシとうまく統合されています。これをここで使用します。

2. Maven構成

まず、Spring CloudからUIアプリケーションのpom.xmlへのzuulサポートへの依存関係を追加する必要があります。

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
    <version>2.2.0.RELEASE</version>
</dependency>

最新バージョンはここにあります。

3. ズールのプロパティ

次に、Zuulを構成する必要があります。Spring Bootを使用しているため、application.ymlで構成します。

zuul:
  routes:
    foos:
      path: /foos/**
      url: http://localhost:8081/spring-zuul-foos-resource/foos

ご了承ください:

  • リソースサーバーFoosにプロキシしています。
  • /foos / 」で始まるUIからのすべてのリクエストは、 http:// loclahost:8081/spring-zuul-foosのFoosリソースサーバーにルーティングされます。 -resource / foos /

4. API

私たちのAPIアプリケーションはシンプルなSpringBootアプリです。

この記事では、ポート8081でを実行しているサーバーにデプロイされたAPIについて検討します。

まず、使用するリソースの基本的なDTOを定義しましょう。

public class Foo {
    private long id;
    private String name;

    // standard getters and setters
}

そして、単純なコントローラー:

@RestController
public class FooController {

    @GetMapping("/foos/{id}")
    public Foo findById(
      @PathVariable long id, HttpServletRequest req, HttpServletResponse res) {
        return new Foo(Long.parseLong(randomNumeric(2)), randomAlphabetic(4));
    }
}

5. UIアプリケーション

私たちのUIアプリケーションもシンプルなSpringBootアプリケーションです。

この記事では、ポート8080でを実行しているサーバーにデプロイされたAPIについて検討します。

メインのindex.htmlから始めましょう–少しAngularJSを使用します:

<html>
<body ng-app="myApp" ng-controller="mainCtrl">
<script src="angular.min.js"></script>
<script src="angular-resource.min.js"></script>

<script>
var app = angular.module('myApp', ["ngResource"]);

app.controller('mainCtrl', function($scope,$resource,$http) {
    $scope.foo = {id:0 , name:"sample foo"};
    $scope.foos = $resource("/foos/:fooId",{fooId:'@id'});
    
    $scope.getFoo = function(){
        $scope.foo = $scope.foos.get({fooId:$scope.foo.id});
    }  
});
</script>

<div>
    <h1>Foo Details</h1>
    <span>{{foo.id}}</span>
    <span>{{foo.name}}</span>
    <a href="#" ng-click="getFoo()">New Foo</a>
</div>
</body>
</html>

ここで最も重要な側面は、相対URLを使用してAPI にアクセスする方法です!

APIアプリケーションはUIアプリケーションと同じサーバーにデプロイされていないため、相対URLは機能しないため、プロキシがないと機能しないことに注意してください。

ただし、プロキシを使用すると、Zuulプロキシを介して Foo リソースにアクセスします。もちろん、これらのリクエストは、APIが実際にデプロイされている場所にルーティングするように構成されています。

そして最後に、実際にブートが有効なアプリケーション:

@EnableZuulProxy
@SpringBootApplication
public class UiApplication extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(UiApplication.class, args);
    }
}

単純なBootアノテーションに加えて、Zuulプロキシにもenableスタイルのアノテーションを使用していることに注意してください。これは非常にクールでクリーンで簡潔です。

6. ルーティングをテストする

次に、UIアプリケーションをテストしてみましょう。

@Test
public void whenSendRequestToFooResource_thenOK() {
    Response response = RestAssured.get("http://localhost:8080/foos/1");
 
    assertEquals(200, response.getStatusCode());
}

7. カスタムズールフィルター

There are multiple Zuul filters available, and we can also create our own custom one:

@Component
public class CustomZuulFilter extends ZuulFilter {

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        ctx.addZuulRequestHeader("Test", "TestSample");
        return null;
    }

    @Override
    public boolean shouldFilter() {
       return true;
    }
    // ...
}

この単純なフィルターは、「 Test 」というヘッダーをリクエストに追加するだけですが、もちろん、ここでリクエストを拡張するために必要なだけ複雑にすることができます。

8. カスタムズールフィルターをテストする

最後に、カスタムフィルターが機能していることをテストしてみましょう。最初に、FoosリソースサーバーでFooControllerを変更します。

@RestController
public class FooController {

    @GetMapping("/foos/{id}")
    public Foo findById(
      @PathVariable long id, HttpServletRequest req, HttpServletResponse res) {
        if (req.getHeader("Test") != null) {
            res.addHeader("Test", req.getHeader("Test"));
        }
        return new Foo(Long.parseLong(randomNumeric(2)), randomAlphabetic(4));
    }
}

それでは、テストしてみましょう。

@Test
public void whenSendRequest_thenHeaderAdded() {
    Response response = RestAssured.get("http://localhost:8080/foos/1");
 
    assertEquals(200, response.getStatusCode());
    assertEquals("TestSample", response.getHeader("Test"));
}

9. 結論

この記事では、Zuulを使用してUIアプリケーションからRESTAPIにリクエストをルーティングすることに焦点を当てました。 CORSと同一生成元ポリシーをうまく回避し、転送中のHTTPリクエストをカスタマイズして拡張することもできました。

The full implementation of this tutorial can be found in the GitHub project.