一、总体说明
现在的Web后端基本上都是采用微服务架构,服务间交互走http协议,通过restful接口。调用restful接口的方式很多,有用httpclient、RestTemplate, 使用第三组件如CXF。下面介绍使用第三件Feign做http请求客户端封装。
不论采用哪种方式,都是为了使http接口请求native化–让客户端代码像调用本地接口那样调用远程接口。
二、服务端发布restful接口
1、定义接口中使用的模型User
package com.elon.feign.client.model;
import lombok.Data;
import java.io.Serializable;
@Data
public class User implements Serializable {
private static final long serialVersionUID = -5699625542142109964L;
private int id = -1;
private String name = "";
private int age = 0;
}
2、发布Rest接口
package com.elon.feign.server.rest;
import com.elon.feign.server.model.User;
import org.springframework.web.bind.annotation.*;
import java.io.UnsupportedEncodingException;
/**
* 用于测试feign的rest接口定义。
*/
@RestController
@RequestMapping("/v1/feign-server")
public class FeignServerController {
/**
* 根据ID查询用户
* @param id ID
* @return 用户信息
*/
@GetMapping("/user/{id}")
public User queryUserById(@PathVariable("id") int id) {
System.out.println("id:" + id);
User user = new User();
user.setId(id);
return user;
}
/**
* 根据名称查询用户
* @param name 名称
* @return 用户信息
*/
@GetMapping("/user/query-by-name")
public User queryUserByName(@RequestParam("name") String name) {
System.out.println("name:" + name);
User user = new User();
user.setName(name);
return user;
}
/**
* 修改用户信息
* @param name 姓名
* @param age 年龄
* @param id 用户ID
* @return 用户信息
*/
@PostMapping("/user/update-user/{id}")
public User updateUserInfo(@RequestHeader("name") String name, @RequestHeader("age") int age,
@PathVariable("id") int id){
// header 是8859编码,不做转码的话,中文会乱码
try {
name = new String(name.getBytes("ISO-8859-1"), "utf8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
System.out.println("id:" + id + "|name:" + name + "|age:" + age);
User user = new User();
user.setId(id);
user.setName(name);
user.setAge(age);
return user;
}
/**
* 添加用户
* @param user 用户信息
* @return 处理结果
*/
@PutMapping(value = "/user")
public boolean addUser(@RequestBody User user){
System.out.println("user:" + user);
return true;
}
/**
* 删除用户。
* @param userId 用户ID
* @return 处理结果
*/
@DeleteMapping("/user/{userId}")
public boolean deleteUser(@PathVariable("userId") int userId) {
System.out.println("Delete user. userId:" + userId);
return true;
}
}
三、客户端调用代码
1、在pom.xml中引入feign的依赖
<dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-jackson</artifactId>
<version>8.18.0</version>
</dependency>
<dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-core</artifactId>
<version>8.18.0</version>
</dependency>
2、定义代理接口
package com.elon.feign.client;
import com.elon.feign.client.model.User;
import feign.Headers;
import feign.Param;
import feign.RequestLine;
/**
* 客户端代理接口
*/
@Headers({"content-type: application/json", "accept: application/json"})
public interface ProxyInterface {
@RequestLine("GET /v1/feign-server/user/{id}")
User queryUserById(@Param("id") int id);
@RequestLine("GET /v1/feign-server/user/query-by-name?name={name}")
User queryUserByName(@Param("name") String name);
@Headers({"name: {name}", "age: {age}"})
@RequestLine("POST /v1/feign-server/user/update-user/{id}")
User updateUserInfo(@Param("name") String name, @Param("age") int age, @Param("id") int id);
@RequestLine("PUT /v1/feign-server/user")
boolean addUser(User user);
@RequestLine("DELETE /v1/feign-server/user/{userId}")
boolean deleteUser(@Param("userId") int userId);
}
上面的样例代码中包含了常见的GET、POST、PUT、DELETE请求,参数也包含Path参数、Query参数、Header参数和Body参数。需要注意的是addUser接口使用了自定义的User对象做参数,指定使用json格式传输。
3、写测试代码调用接口
package com.elon.feign.client;
import com.elon.feign.client.model.User;
import feign.Feign;
import feign.jackson.JacksonDecoder;
import feign.jackson.JacksonEncoder;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class FeignClientApplication {
public static void main(String[] args) {
SpringApplication.run(FeignClientApplication.class);
System.out.println("Start up Feign client success!");
ProxyInterface proxyInterface = buildInterfaceInstance(ProxyInterface.class,
"http://localhost:10001/feignserver/");
User user = proxyInterface.queryUserById(123);
System.out.println("GET请求&&path参数:" + user);
User user2 = proxyInterface.queryUserByName("张三");
System.out.println("GET请求&&query参数:" + user2);
User user3 = proxyInterface.updateUserInfo("李四", 25, 100);
System.out.println("POST请求&&header参数:" + user3);
User user4 = new User();
user4.setId(10001);
user4.setName("王五");
user4.setAge(30);
boolean result = proxyInterface.addUser(user4);
System.out.println("PUT请求&&Body参数:" + result);
boolean result2 = proxyInterface.deleteUser(9999);
System.out.println("DELETE请求&&Path参数:" + result2);
}
private static <T> T buildInterfaceInstance(Class<T> type, String url) {
return Feign.builder().encoder(new JacksonEncoder()).decoder(new JacksonDecoder())
.target(type, url);
}
}
调用接口前先构建代理接口实例, 传入的Url需要包含到context-path。