springboot中用@FeignClient 调用第三方服务,自定义设置Header


问题描述

在springboot项目中集成Feign访问第三方服务时,需要在header中设置token,具体操作步骤如下。
在使用@FeignClient之前需要引入的包:

	 	 
	    
		
		    org.springframework.cloud
		    spring-cloud-starter
		    3.0.1
		

		
		    org.springframework.cloud
		    spring-cloud-context
		     3.0.1
		

		
		    org.springframework.cloud
		    spring-cloud-starter-openfeign
		     3.0.1
		

需要在启动类上开启Feign

@EnableFeignClients
@SpringBootApplication
public class TestApplication {

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

创建client接口,添加 @Header注解:

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import feign.Headers;
import feign.Param;
import feign.RetryableException;

@FeignClient(value = "testClient", url = "${demo.test.url}")
public interface TestFeignClient {
	
	@RequestMapping(method = RequestMethod.POST, path = "/path")
	@Headers({"Content-Type: application/json","token: {token}"})
	String demo(@RequestBody String request,@Param("token")String token) throws RetryableException;
	
}

查看Feign的@Header文档如下:


feign.Headers

@Target(value={METHOD, TYPE})
@Retention(value=RUNTIME)

Expands headers supplied in the value. Variables to the the right of the colon areexpanded. 

 @Headers("Content-Type: application/xml")
 interface SoapApi {
 ...   
 @RequestLine("GET /")
 @Headers("Cache-Control: max-age=640000")
 ...

 @RequestLine("POST /")
 @Headers({
   "X-Foo: Bar",
   "X-Ping: {token}"
 }) void post(@Param("token") String token);
 ...
 

Notes: ?If you'd like curly braces literally in the header, urlencode them first.
?Headers do not overwrite each other. All headers with the same name will be included in therequest.

Relationship to JAXRS

The following two forms are identical. 

Feign:  @RequestLine("POST /")
 @Headers({
   "X-Ping: {token}"
 }) void post(@Named("token") String token);
 ...
 

JAX-RS:  @POST @Path("/")
 void post(@HeaderParam("X-Ping") String token);
 ...


由于feign的post方式方法中只允许传入一个@RequestBody,在项目启动的时候会报错:

Caused by: java.lang.IllegalStateException: Method has too many Body parameters: public abstract java.lang.String com.demo.TestFeignClient.demo(java.lang.String,java.lang.String) throws feign.RetryableException
Warnings:
- 
	at feign.Util.checkState(Util.java:129) ~[feign-core-10.10.1.jar:na]
	at feign.Contract$BaseContract.parseAndValidateMetadata(Contract.java:127) ~[feign-core-10.10.1.jar:na]
	at org.springframework.cloud.openfeign.support.SpringMvcContract.parseAndValidateMetadata(SpringMvcContract.java:194) ~[spring-cloud-openfeign-core-3.0.1.jar:3.0.1]
	at feign.Contract$BaseContract.parseAndValidateMetadata(Contract.java:62) ~[feign-core-10.10.1.jar:na]
	at feign.ReflectiveFeign$ParseHandlersByName.apply(ReflectiveFeign.java:151) ~[feign-core-10.10.1.jar:na]
	at feign.ReflectiveFeign.newInstance(ReflectiveFeign.java:49) ~[feign-core-10.10.1.jar:na]
	at feign.Feign$Builder.target(Feign.java:269) ~[feign-core-10.10.1.jar:na]
	at org.springframework.cloud.openfeign.DefaultTargeter.target(DefaultTargeter.java:30) ~[spring-cloud-openfeign-core-3.0.1.jar:3.0.1]
	at org.springframework.cloud.openfeign.FeignClientFactoryBean.getTarget(FeignClientFactoryBean.java:359) ~[spring-cloud-openfeign-core-3.0.1.jar:3.0.1]
	at org.springframework.cloud.openfeign.FeignClientFactoryBean.getObject(FeignClientFactoryBean.java:322) ~[spring-cloud-openfeign-core-3.0.1.jar:3.0.1]
	at org.springframework.cloud.openfeign.FeignClientsRegistrar.lambda$registerFeignClient$0(FeignClientsRegistrar.java:224) ~[spring-cloud-openfeign-core-3.0.1.jar:3.0.1]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1231) ~[spring-beans-5.3.4.jar:5.3.4]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1173) ~[spring-beans-5.3.4.jar:5.3.4]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:564) ~[spring-beans-5.3.4.jar:5.3.4]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524) ~[spring-beans-5.3.4.jar:5.3.4]
	... 29 common frames omitted

这下犯难了,能不能添加属性到header中先不说,项目都起不起来了,o(╥﹏╥)o
我们来慢慢分析,即使上面的@Header注解使用没有问题, Method has too many Body parameters的问题也无法跳过。为了不浪费时间,直接看下面的解决方案。

解决方案

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import feign.RetryableException;

@FeignClient(value = "testClient", url = "${demo.test.url}")
public interface TestFeignClient {
    @RequestMapping(method = RequestMethod.POST, path = "/path",headers = {"Content-Type=application/json"})
	String demo(@RequestBody String request,@RequestHeader(name = "token",required = true) String token) throws RetryableException;
}

需要注意的有两点:
1、设置Header的Content-Type属性,放在了@RequestMapping中,且格式为 Content-Type=application/json
2、添加验证token属性,使用了@RequestHeader注解,把token作为动态参数传入

重启项目,纵享丝滑O(∩_∩)O