Spring REST with a Zuul Proxy – 带有Zuul代理的Spring REST

最后修改: 2016年 1月 30日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this article, we’ll explore the communication between a front-end application and a REST API that are deployed separately.

在这篇文章中,我们将探讨分别部署的前端应用程序和REST API之间的通信

The goal is to work around CORS and the Same Origin Policy restriction of the browser and allow the UI to call the API even though they don’t share the same origin.

其目的是绕过CORS和浏览器的同源政策限制,允许用户界面调用API,即使它们不共享同一来源。

We’ll basically create two separate applications – a UI application and a simple REST API, and we’ll use the Zuul proxy in the UI application to proxy calls to the REST API.

我们基本上将创建两个独立的应用程序–一个UI应用程序和一个简单的REST API,我们将在UI应用程序中使用Zuul代理来代理对REST API的调用。

Zuul is a JVM based router and server side load balancer by Netflix. And Spring Cloud has a nice integration with an embedded Zuul proxy – which is what we’ll use here.

Zuul是Netflix的一个基于JVM的路由器和服务器端的负载平衡器。而Spring Cloud与嵌入式Zuul代理进行了很好的整合–这就是我们在这里要使用的东西。

2. Maven Configuration

2.Maven配置

First, we need to add a dependency to the zuul support from Spring Cloud to our UI application’s pom.xml:

首先,我们需要在UI应用程序的pom.xml中添加一个对Spring Cloud的zuul支持的依赖。

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

The latest version can be found here.

最新版本可以在这里找到。

3. Zuul Properties

3.Zuul Properties

Next – we need to configure Zuul, and since we’re using Spring Boot, we’re going to do that in the application.yml:

接下来–我们需要配置Zuul,由于我们使用的是Spring Boot,我们将在application.yml中进行配置。

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

Note that:

请注意,。

  • We are proxying to our resource server Foos.
  • All requests from the UI that starts with “/foos/” will be routed to our Foos Resource server at http://loclahost:8081/spring-zuul-foos-resource/foos/

4. The API

4.API

Our API application is a simple Spring Boot app.

我们的API应用是一个简单的Spring Boot应用。

Within this article, we’re going to consider the API deployed in a server running on port 8081.

在这篇文章中,我们将考虑将API部署在一个运行在8081端口的服务器中。

Let’s first define the basic DTO for the Resource we’re going to be using:

让我们首先为我们将要使用的资源定义基本的DTO。

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

    // standard getters and setters
}

And a simple controller:

还有一个简单的控制器。

@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. The UI Application

5.UI应用

Our UI application is also a simple Spring Boot application.

我们的UI应用也是一个简单的Spring Boot应用。

Within this article, we’re going to consider the API deployed in a server running on port 8080.

在这篇文章中,我们将考虑将API部署在一个运行在8080端口的服务器中。

Let’s start with the main index.html – using a bit of AngularJS:

让我们从主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>

The most important aspect here is how we’re accessing the API using relative URLs!

这里最重要的方面是我们如何访问API 使用相对的URL!

Keep in mind that the API application isn’t deployed on the same server as the UI application, so relative URLs shouldn’t work, and won’t work without the proxy.

请记住,API应用程序并没有部署在与UI应用程序相同的服务器上,所以相对的URL不应该工作,而且没有代理也不会工作。

With the proxy, however, we’re accessing the Foo resources through the Zuul proxy, which is of course configured to route these requests to wherever the API is actually deployed.

然而,通过代理,我们通过Zuul代理访问Foo资源,当然,Zuul代理被配置为将这些请求发送到API实际部署的地方。

And finally, the actually Boot enabled application:

最后,实际上是启动启用的应用程序。

@EnableZuulProxy
@SpringBootApplication
public class UiApplication extends SpringBootServletInitializer {

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

Beyond the simple Boot annotation, notice that we’re using the enable-style of annotation for the Zuul proxy as well, which is pretty cool, clean and concise.

除了简单的Boot注解外,注意到我们对Zuul代理也使用了enable-style的注解,这相当酷,干净而简洁。

6. Test the Routing

6.测试路由

Now – let’s test our UI application – as follows:

现在–让我们测试一下我们的用户界面应用–如下。

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

7. A Custom Zuul Filter

7.一个自定义的Zuul过滤器

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

有多种Zuul过滤器可用,我们也可以创建自己的自定义过滤器。

@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;
    }
    // ...
}

This simple filter just adds a header called “Test” to the request – but of course, we can get as complex as we need to here augment our requests.

这个简单的过滤器只是在请求中添加了一个名为”Test“的标头–当然,我们可以根据需要在这里增加我们的请求,变得尽可能复杂。

8. Test Custom Zuul Filter

8.测试自定义Zuul过滤器

Finally, let’s test make sure our custom filter is working – first we will modify our FooController at Foos resource server:

最后,让我们测试一下,确保我们的自定义过滤器能够正常工作–首先我们将在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));
    }
}

Now – let’s test it out:

现在–让我们来测试一下。

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

9. Conclusion

9.结论

In this write-up, we focused on using Zuul to route requests from a UI application to a REST API. We successfully worked around CORS and the same-origin policy and we also managed to customize and augment the HTTP request in transit.

在这篇文章中,我们着重于使用Zuul将请求从一个UI应用程序路由到一个REST API。我们成功地绕过了CORS和同源策略,我们还设法在传输过程中定制和增强了HTTP请求。

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

本教程的完整实现可以在GitHub项目中找到。