前言 在开发 Spring Cloud 微服务的时候,我们知道,服务之间都是以 HTTP 接口的形式对外提供服务的,因此消费者在进行调用的时候,底层就是通过 HTTP Client 的这种方式进行访问。
当然我们可以使用JDK原生的 URLConnection、Apache 的 HTTP Client、Netty 异步 Http Client,Spring 的 RestTemplate 去实现服务间的调用。
但是最方便、最优雅的方式是通过 Spring Cloud Open Feign 进行服务间的调用 Spring Cloud 对 Feign 进行了增强,使 Feign 支持 Spring Mvc 的注解,并整合了 Ribbon 等,从而让 Feign 的使用更加方便。
Feign 概述 什么是 Feign
Feign 是一个声明式的 Web Service 客户端。它的出现使开发 Web Service 客户端变得很简单。使用 Feign 只需要创建一个接口加上对应的注解,比如:@FeignClient 注解。 Feign 有可插拔的注解,包括 Feign 注解和 AX-RS 注解。Feign 也支持编码器和解码器,Spring Cloud Open Feign 对 Feign 进行增强支持 Spring Mvc 注解,可以像 Spring Web 一样使用 HttpMessageConverters 等。
Feign 是一种声明式、模板化的 HTTP 客户端。在 Spring Cloud 中使用 Feign,可以做到使用 HTTP 请求访问远程服务,就像调用本地方法一样的,开发者完全感知不到这是在调用远程方法,更感知不到在访问 HTTP 请求。
入门案例 此处以调用 Github API 查询服务为例。
引入依赖
1 2 3 4 <dependency > <groupId > org.springframework.cloud</groupId > <artifactId > spring-cloud-starter-openfeign</artifactId > </dependency >
启动类加入如下注解:
1 2 3 4 5 6 import org.springframework.cloud.openfeign.EnableFeignClients;@SpringBootApplication @EnableFeignClients @ComponentScan({"cn.psvmc.zapicall.controller", "cn.psvmc.zapicall.feign"})
假如我们要访问
https://api.github.com/search/repositories?q=java
Feign接口编写
1 2 3 4 5 6 7 8 9 10 11 12 import org.springframework.cloud.openfeign.FeignClient;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestParam;@FeignClient(name = "github-client", url = "https://api.github.com") public interface GitHubFeign { @GetMapping("/search/repositories") String searchRepo (@RequestParam("q") String q) ; }
Controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import cn.psvmc.zapicall.feign.GitHubFeign;import org.springframework.http.MediaType;import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;import javax.servlet.http.HttpServletResponse;@RestController @RequestMapping( value = "/github" ) public class GithubController { @Resource private GitHubFeign gitHubFeign; @RequestMapping( value = "/search", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE ) String searchRepo (@RequestParam("q") String q, HttpServletResponse response) { response.setHeader("token" , "123456" ); return gitHubFeign.searchRepo(q); } }
注意
设置响应头Content-Type要使用produces = MediaType.APPLICATION_JSON_VALUE。
MediaType.APPLICATION_JSON_UTF8_VALUE 已被标记为不推荐使用,并在 Spring Framework 的最新版本中被废弃。相应的替代值是 MediaType.APPLICATION_JSON_VALUE。MediaType.APPLICATION_JSON_VALUE 是一个常量,它表示 JSON 格式的媒体类型,并且默认使用 UTF-8 字符编码
代码中设置response.setHeader("Content-Type", "application/json");无效,会被覆盖掉。
我们就可以通过以下地址访问
http://localhost:8080/github/search?q=java
Get请求 映射属性 1 2 3 4 5 6 @FeignClient(name = "feignApiClient", url = "http://localhost:8080/user") public interface FeignApiInteferce { @GetMapping("/post6") String mGet (@RequestParam("name") String name, @RequestParam("sex") String sex) ; }
映射对象 1 2 3 4 5 6 @FeignClient(name = "feignApiClient", url = "http://localhost:8080/user") public interface FeignApiInteferce { @GetMapping(value = "/post6") String mGet (@SpringQueryMap User user) ; }
Post请求 application/json 参数通过 @RequestBody标识 请求头Content-Type在 @Headers注解中指定
1 2 3 4 5 6 @FeignClient(name = "feignApiClient", url = "http://localhost:8080/user") public interface FeignApiInteferce { @Headers({"Content-Type: application/json"}) @PostMapping("/post1") String post1 (@RequestBody User user) ; }
对应的接口
1 2 3 4 @PostMapping("/post1") public String post1 (@RequestBody User user) throws JsonProcessingException { return new JsonMapper ().writeValueAsString(user); }
1 2 3 4 5 6 @FeignClient(name = "feignApiClient", url = "http://localhost:8080/user") public interface FeignApiInteferce { @Headers({"Content-Type: application/x-www-form-urlencoded;charset=UTF-8"}) @PostMapping("/post5") String post5 (@RequestBody MultiValueMap<String, Object> user) ; }
调用
1 2 3 4 5 6 LinkedMultiValueMap<String, Object> multiValueMap = new LinkedMultiValueMap <>(); multiValueMap.add("name" ,"臭小子" ); multiValueMap.add("sex" , "男" ); feignApiInteferce.post5(multiValueMap);
对应的接口
1 2 3 4 @PostMapping("/post5") public String post5 (User user) throws JsonProcessingException { return new JsonMapper ().writeValueAsString(user); }
1 2 3 4 5 @FeignClient public interface UploadClient { @PostMapping(value = "/uploadFile", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) String uploadFile (@RequestPart(value = "file") MultipartFile file, @RequestParam String uploadPath) ; }
有时候接口验签,header 中需要追加一些参数;通过@RequestHeader注解实现 例如header中增加参数 authorization
1 2 3 4 5 6 7 @FeignClient(name = "feignApiClient", url = "http://localhost:8080/user") public interface FeignApiInteferce { @Headers({"Content-Type:application/json","Authorization:{authorization}"}) @PostMapping("/post2") String post2 (@RequestHeader(name = "authorization") String authorization, @RequestBody User user) ;}
对应的接口
1 2 3 4 5 6 @PostMapping("/post2") public String post2 (@RequestBody User user, HttpServletRequest request) throws JsonProcessingException { String authorization = request.getHeader("authorization" ); return new JsonMapper ().writeValueAsString(user)+", authorization=" + authorization; }
URL后添加参数 除了放到body中的参数,还能直接在url后面追加参数,@RequestParam注解 例如增加参数title http://xxxxxxxx?title=xxxx
1 2 3 4 5 6 7 8 @FeignClient(name = "feignApiClient", url = "http://localhost:8080/user") public interface FeignApiInteferce { @Headers({"Content-Type:application/json","Authorization:{authorization}"}) @PostMapping("/post3") String post3 (@RequestHeader(name = "authorization") String authorization, @RequestParam("title") String title, @RequestBody User user) ;}
URL中添加参数 1 2 3 4 5 6 7 8 9 @FeignClient(name = "feignApiClient", url = "http://localhost:8080/user") public interface FeignApiInteferce { @Headers({"Content-Type:application/json","Authorization:{authorization}"}) @PostMapping("/post3/{userId}?title={title}") String post4 (@RequestHeader(name = "authorization") String authorization, @PathVariable("userId") String userId, @PathVariable("title") String title, @RequestBody User user) ;}
禁用SSL认证 如果请求报错
unable to find valid certification path to requested target
这是请求了https的地址,但是没有设置证书,我们可以关闭证书验证。
我们添加依赖
1 2 3 4 <dependency > <groupId > org.springframework.cloud</groupId > <artifactId > spring-cloud-starter-openfeign</artifactId > </dependency >
这时候添加禁用SSL认证的配置
application.properties
1 feign.httpclient.disableSslValidation=true
但是这个时候,配置并不生效,还要添加
1 2 3 4 <dependency > <groupId > io.github.openfeign</groupId > <artifactId > feign-httpclient</artifactId > </dependency >