一、在調用其他服務時,使用實體對象作為參數。按照以下配置。
1、feign接口。
@RequestMapping(value = "/user", method = RequestMethod.POST, consumes = "application/json")
String getUserId(@RequestBody User user);
- 1、consumes: 指定處理請求的提交內容類型(Content-Type),例如application/json, text/html;
- 2、produces: 指定返回的內容類型,僅當request請求頭中的(Accept)類型中包含該指定類型才返回;
- 3、或者 = MediaType.APPLICATION_JSON_VALUE
- 4、正常來說,在FeignClient的接口中,也不需要在參數上注解@RequestBody ,只需要在實現類上添加@RequestBody 注解即可。
2、其他服務實現。
@RequestMapping(value = "/user", method = RequestMethod.POST)
public String getUserId(@RequestBody User user){
if("a".equals(user.getUserName())){
return "1001";
}
return "1003";
}
feign對應的服務實現上的參數@RequestBody 注解必不可少,否則接受到參數可能為null。
二、如果以上依然實現不了實體對象的傳輸,可以增加以下方法修改。以下配置是參考網上別人所寫,具體是誰我給忘記了,如果知道,請告訴我一聲,我會備注的。
1、修改調用@FeignClient 接口的pom文件。
feign: httpclient: enabled: true
2、修改pom文件。添加以下配置:
<!-- 使用Apache HttpClient替換Feign原生httpclient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.3</version>
</dependency>
<dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-httpclient</artifactId>
<version>8.18.0</version>
</dependency>
3、為什么要替換?主要是為了使用GET方法時也可以傳入實體作為參數。否則,如果使用實體作為參數傳值,默認會改為POST請求方式。看源碼
private synchronized OutputStream getOutputStream0() throws IOException { try { if(!this.doOutput) { throw new ProtocolException("cannot write to a URLConnection if doOutput=false - call setDoOutput(true)"); } else { if(this.method.equals("GET")) { this.method = "POST"; }
Feign在默認情況下使用的是JDK原生的URLConnection發送HTTP請求,沒有連接池,但是對每個地址會保持一個長連接,即利用HTTP的persistence connection 。我們可以用Apache的HTTP Client替換Feign原始的http client, 從而獲取連接池、超時時間等與性能息息相關的控制能力。Spring Cloud從Brixtion.SR5版本開始支持這種替換,首先在項目中聲明Apache HTTP Client和feign-httpclient依賴。
4、看一個簡單的示例。
// portal層 @RequestMapping(value = "/teacher/teacher", method = RequestMethod.GET) public Teacher getTeacher(){ Student student = new Student(); student.setStudentId(1); student.setStudentName("王小二"); student.setAge(12); return teacherFeign.getTeacher(student); } // feign api層 @FeignClient(value = "feign-server") public interface TeacherFeign { @RequestMapping(value = "/teacher/teacher", method = RequestMethod.GET,consumes = MediaType.APPLICATION_JSON_VALUE) Teacher getTeacher( Student student); } // feign-server層 @RestController public class TeacherRest implements TeacherFeign { @Override public Teacher getTeacher(@RequestBody Student student) { System.out.println(student.toString()); return teacherList().get(0); } }
三、使用表單模式傳輸數據,相當於GET方式直接接受實體參數。方式一,構建k1=v1&k2=v2
/** * 在舊系統中,使用了原始的表單提交,並且這也是一個微服務 */ @RequestMapping(value = "/patients/{id}", method = RequestMethod.PUT) public APIResult UpdatePatientInfo(@PathVariable(value = "id") String id,PatientParameter patientParameter){...}
所以我的feign接口如下
3-1、Feign接口
@FeignClient("xx-server") public interface PatientLegacyAdapterFeign { /** * 編輯客戶 * @param patientId * @param patientRequest 編輯客戶請求數據 * @return */ @RequestMapping(value = "/patients/{id}",method = RequestMethod.PUT) Map<String,Object> editPatientInfo(@PathVariable("id")String patientId, PatientParameter patientParameter);
雖然這樣可以請求過去,但是參數中的值並不會去過去,patientParameter結果為null。
3-2、 分析
Content-type=application/x-www-form-urlencoded,參數上不能增加@RequestBody的注解。
- 1、因為使用的表單模式提交,所以這個提交方式是application/x-www-form-urlencoded。
- 2、采用這種類型:提交到服務端實際上是一個MultiValueMap,如果value中的對象也是一個對象,那么在構建這個參數時就非常困難。采用key1=value1&key2=value2這種形式將所有參數拼接起來;value要進行編碼,編碼之后的對調試者不友好;value是復雜對象的情況更加糟糕,一般只能通過程序來序列化得到參數,想手寫基本不可能。
3-3、解決方案
@FeignClient("xx-server") public interface OldPatientFeign { /** * 編輯患者 * @param id * @param patientParameter * @return */ @RequestMapping(value = "/patient/{id}", method = RequestMethod.PUT) Map<String,Object> updatePatientInfo(@PathVariable(value = "id") String id, MultiValueMap patientParameter); }
因為我們知道表單提交時,服務端是一個MultiValueMap,所以我們直接使用這個對象去傳。
// 調用前使用MultiValueMap 。 LinkedMultiValueMap m = new LinkedMultiValueMap(); m.setAll( BeanUtils.describe(getPatientParameterByPatientRequest(patientRequest))); return oldPatientFeign.updatePatientInfo(patientId,m);
這樣就可以完美的使用表單格式提交了。
四、Feign實現Form表單提交,方式二,參考周立大大的文章
4-1、添加依賴
<dependency> <groupId>io.github.openfeign.form</groupId> <artifactId>feign-form</artifactId> <version>3.2.2</version> </dependency> <dependency> <groupId>io.github.openfeign.form</groupId> <artifactId>feign-form-spring</artifactId> <version>3.2.2</version> </dependency>
4-2、Feign Client示例:
@FeignClient(name = "xxx", url = "http://www.itmuch.com/", configuration = TestFeignClient.FormSupportConfig.class) public interface TestFeignClient { @PostMapping(value = "/test", consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE}, produces = {MediaType.APPLICATION_JSON_UTF8_VALUE} ) void post(Map<String, ?> queryParam); class FormSupportConfig { @Autowired private ObjectFactory<HttpMessageConverters> messageConverters; // new一個form編碼器,實現支持form表單提交 @Bean public Encoder feignFormEncoder() { return new SpringFormEncoder(new SpringEncoder(messageConverters)); } // 開啟Feign的日志 @Bean public Logger.Level logger() { return Logger.Level.FULL; } } }
4-3、調用示例:
@GetMapping("/user/{id}") public User findById(@PathVariable Long id) { HashMap<String, String> param = Maps.newHashMap(); param.put("username","zhangsan"); param.put("password","pwd"); this.testFeignClient.post(param); return new User(); }