在本教程中,我们将了解FeignClient以及如何在Spring Boot/Spring Cloud应用程序中使用它。
FeignClient是一个以声明方式创建REST API客户端的库。因此,如果我们不是手动编写远程API客户端,就可能使用Springs RestTemplate,而使用Feign声明客户端定义很方便,其余部分在运行时生成供使用。
我们将构建一个小型命令行应用程序,它模拟我们一个看板API的完整测试。示例应用程序将创建一个新用户,登录,检索所有板并再次注销用户。它捕获了最常见的用例(POST,GET,DELETE + AuthN)
添加依赖项
我们在本教程中使用Maven。由于我们不想弄乱版本号,最简单的方法是在Maven POM 中的dependencyManagement中包含Spring Cloud设置。
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Finchley.SR1</version> <type>pom</type> <scope><b>import</b></scope> </dependency> </dependencies> </dependencyManagement>
现在,我们可以使用经典的Spring Boot启动程序将依赖项添加到Feign:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
Feign客户端使用声明方法来访问API。要使用它,我们必须首先在我们的Spring Boot应用程序上使用@EnableFeignClients注解启用Spring Cloud支持。
@SpringBootApplication @EnableFeignClients <b>public</b> <b>class</b> FeignIntroductionApplication implements ApplicationRunner { <font><i>//omitted </i></font><font> } </font>
下一步是声明用于访问我们的API的接口。我们将其命名为KanbanClient,因为它将提供调用远程API的方法。
@FeignClient(name=<font>"KanbanClient"</font><font>, url= </font><font>"https://kanbanbackend.herokuapp.com/"</font><font>) <b>public</b> <b>interface</b> KanbanClient { } </font>
要将其转换为Feign客户端,我们必须在界面上设置@FeignClient注释,并为其指定名称属性,并使用url属性设置远程URL 。这里支持SpEL,因此我们可以将值外部化为属性文件。我们也可以使用Eureka在这里使用服务发现,而不是使用URL,如果使用Eureka服务发现,这里只要:
@FeignClient(<font>"KanbanApp"</font><font>) </font>
KanbanApp是注册在Eureka服务注册器里面的应用名称,也就是对方生产者Srping.application.name=KanbanApp中配置的名称。
下面要定义远程调用的各个方法,不同远程API对应不同的接口方法,需要使用Spring MVC中的一些注释,这些注释通常也是用在REST服务器端的@Controller上。它们的行为相同,这里只是用在REST客户端。
POST
在方法上添加@PostMapping批注并在其中传递参数,将该方法转换为POST调用。在PostMapping注释中,我们提供了相对于在@FeignClient注释上设置的URL的端点(具体URL路径)。
@PostMapping(value = <font>"/register"</font><font>) String registerUser(User user); </font>
用户是一个简单的POJO,带有用户名和密码字段。Feign,Spring会自动将其转换为JSON。
GET
原理与上面相同,但我们在方法上使用@GetMapping。我们还发送了身份验证标头。API使用X-Auth-Token。
@GetMapping(<font>"/boards"</font><font>) List<Board> listBoards( @RequestHeader(</font><font>"X-Auth-Token"</font><font>) String authHeader ); </font>
/boards将返回用户的所有看板板的列表。Board是POJO,仅包含id和name。
PUT
同样,这次只需使用@PutMapping注释。
@PutMapping(<font>"/board/{id}"</font><font>) Board changeBoard( @RequestHeader(</font><font>"X-Auth-Token"</font><font>) String authHeader, @PathVariable(</font><font>"id"</font><font>) Long id, Board board ); </font>
我们可以通过将PUT提交看板的端点(/ board / {id})实现更改看板的名称。路径变量请参见下面的使用变量进行调用部分。
DELETE
只需要@DeleteMapping注释。
@DeleteMapping(<font>"/unregister"</font><font>) ResponseEntity<Void> unregisterUser( @RequestHeader(</font><font>"X-Auth-Token"</font><font>) String authToken, Confirmation user ); </font>
此端点需要具有用户密码的Confirmation对象才能删除该帐户,并且如果成功则还需要返回200。
使用变量进行调用
如果我们的端点需要基于像ids这样的实体的变量,我们可以在方法参数上使用@PathVariable注释。它的行为与Spring MVC @Controllers相同。
然后可以在@PutMapping的端点声明中使用已定义的变量如:
@PutMapping(<font>"/board/{id}"</font><font>) Board changeBoard( @RequestHeader(</font><font>"X-Auth-Token"</font><font>) String authHeader, @PathVariable(</font><font>"id"</font><font>) Long id, Board board ); </font>
通过身份认证调用
我们将使用登录端点。它要求用户凭据作为基本身份验证发送,并将返回令牌以进行进一步身份验证。当我们成功调用/ login端点时,它将在响应头中返回auth令牌:
@PostMapping(<font>"/login"</font><font>) ResponseEntity<Void> loginUser( @RequestHeader(</font><font>"Authorization"</font><font>) String authHeader ); </font>
向下传递附加信息的第一种方法是在其上添加带有@RequestHeader注释的方法参数。参数的值将设置为注释中定义的HTTP标头的值。
在身份认证的情况下,它是Authorization标头。作为一个值,我们给它基本的auth编码字符串。
在随后的Kanban API调用中,我们将使用带有令牌的X-Auth-Token标头。
响应头不能作为方法返回值直接返回,但我们可以使用Spring的ResponseEntity,它是一个响应包装器。由于我们的端点不会在响应主体中返回任何内容,因此需要将Void作为参数化类型。
当在看板API中使用Spring Session时,需要通过X-Auth-Token标头交换auth令牌。
要检索我们调用的值:
String token = response.getHeaders().getFirst(<font>"X-Auth-Token"</font><font>); </font>
其他认证方法
我们总是可以在每个方法上使用@RequestHeader注释传递身份验证标头。但是,还有一种在全局范围内指定的替代方法。
像Spring MVC一样,Feign有一个拦截器概念,可用于在远程调用之前执行特定的操作。入口点是RequestInterceptor接口。
使用Spring,我们只需要提供一个Bean来实现Spring上下文的特定接口,它将自动被使用。
@Bean AuthInterceptor authFeign() { <b>return</b> <b>new</b> AuthInterceptor(); } <b>class</b> AuthInterceptor implements RequestInterceptor { @Override <b>public</b> <b>void</b> apply(RequestTemplate template) { template.header(<font>"Authorization"</font><font>, </font><font>"<your token>"</font><font>); } } </font>
我们的拦截器是一个Spring Bean,因此我们可以使用Spring的强大功能并将authN信息外部化为属性,甚至可以从会话范围的bean中检索它,我们会根据每个用户来保存信息。
使用FeignClient
现在,我们可以像任何其他Spring Bean一样将KanbanClient注入我们的代码中。在启动时,Spring Cloud将为我们设置Feign客户端并为我们提供常规的Spring Proxy,以便我们可以简单地开始使用远程端。
我们使用Feign的客户端代码最终看起来像这样:
@FeignClient(name=<font>"KanbanClient"</font><font>, url= </font><font>"https://kanbanbackend.herokuapp.com/"</font><font>) <b>public</b> <b>interface</b> KanbanClient { @PostMapping(value = </font><font>"/register"</font><font>) String registerUser(User user); @DeleteMapping(</font><font>"/unregister"</font><font>) ResponseEntity<Void> unregisterUser( @RequestHeader(</font><font>"X-Auth-Token"</font><font>) String authToken, Confirmation user ); @PostMapping(</font><font>"/login"</font><font>) ResponseEntity<Void> loginUser( @RequestHeader(</font><font>"Authorization"</font><font>) String authHeader ); @GetMapping(</font><font>"/boards"</font><font>) List<Board> listBoards( @RequestHeader(</font><font>"X-Auth-Token"</font><font>) String authHeader ); @PostMapping(</font><font>"/boards"</font><font>) Board createBoard( @RequestHeader(</font><font>"X-Auth-Token"</font><font>) String authHeader, Board board ); @PutMapping(</font><font>"/board/{id}"</font><font>) Board changeBoard( @RequestHeader(</font><font>"X-Auth-Token"</font><font>) String authHeader, @PathVariable(</font><font>"id"</font><font>) Long id, Board board ); } </font>
完整的源码 在GitHub上