在 Spring Cloud 中使用 OpenFeign 调用服务,处理 header 和 body 参数的简单设置方法:
1. 最简单的配置方式
1.1 定义 Feign 客户端接口
@FeignClient(name = "b-service", url = "${b-service.url}")
public interface BServiceClient {
@PostMapping("/api/endpoint")
ResponseEntity<String> callBService(
@RequestHeader Map<String, String> headers, // 接收所有header
@RequestBody Object body // 接收请求体
);
// 或者指定特定的header
@PostMapping("/api/endpoint")
ResponseEntity<String> callBServiceWithSpecificHeaders(
@RequestHeader("Authorization") String token,
@RequestHeader("X-Request-Id") String requestId,
@RequestBody Object body
);
}
1.2 在 Controller 中直接传递
@RestController
@RequestMapping("/api")
public class AController {
@Autowired
private BServiceClient bServiceClient;
@PostMapping("/call-b")
public ResponseEntity<?> callBService(
HttpServletRequest request,
@RequestBody Object requestBody) {
// 方法1:传递所有header
Map<String, String> headers = new HashMap<>();
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
headers.put(headerName, request.getHeader(headerName));
}
return bServiceClient.callBService(headers, requestBody);
}
}
2. 更优雅的拦截器方式(推荐)
2.1 创建 Feign 拦截器
@Component
public class FeignRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
// 从 RequestContextHolder 获取当前请求的上下文
ServletRequestAttributes attributes =
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes != null) {
HttpServletRequest request = attributes.getRequest();
// 复制所有header
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
String headerValue = request.getHeader(headerName);
template.header(headerName, headerValue);
}
}
// 可以添加公共header
template.header("X-Service-Name", "a-service");
}
}
2.2 简化的 Feign 客户端
@FeignClient(
name = "b-service",
url = "${b-service.url}",
configuration = FeignConfig.class
)
public interface BServiceClient {
@PostMapping("/api/endpoint")
ResponseEntity<String> callBService(@RequestBody Object body);
// 不需要显式传递header,拦截器会自动处理
}
2.3 简化的 Controller
@RestController
@RequestMapping("/api")
public class AController {
@Autowired
private BServiceClient bServiceClient;
@PostMapping("/call-b")
public ResponseEntity<?> callBService(@RequestBody Object requestBody) {
// 只需要传body,header会自动传递
return bServiceClient.callBService(requestBody);
}
}
3. 使用 Feign Builder 简化配置
3.1 配置类
@Configuration
public class FeignConfig {
@Bean
public RequestInterceptor requestInterceptor() {
return template -> {
ServletRequestAttributes attributes =
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes != null) {
HttpServletRequest request = attributes.getRequest();
// 复制特定的重要header
String authHeader = request.getHeader("Authorization");
if (authHeader != null) {
template.header("Authorization", authHeader);
}
template.header("X-Request-Id", request.getHeader("X-Request-Id"));
template.header("Content-Type", request.getContentType());
}
};
}
}
4. 全局配置(application.yml)
feign:
client:
config:
default: # 全局默认配置
connectTimeout: 5000
readTimeout: 5000
loggerLevel: basic
b-service: # 特定服务配置
connectTimeout: 3000
readTimeout: 10000
5. 最简单的完整示例
// Controller
@PostMapping("/simple-call")
public ResponseEntity<?> simpleCall(
@RequestHeader Map<String, String> headers,
@RequestBody Map<String, Object> body) {
// 直接转发
return bServiceClient.callBService(headers, body);
}
// Feign Client
@FeignClient(name = "b-service", url = "${b-service.url}")
public interface BServiceClient {
@PostMapping(value = "/api/process", consumes = "application/json")
ResponseEntity<Map<String, Object>> callBService(
@RequestHeader Map<String, String> headers,
@RequestBody Map<String, Object> body
);
}
建议
最简单实用的方案 :使用 拦截器方式(方案2),原因:
-
代码最简洁,Controller 只需要处理业务逻辑
-
Header 传递对调用方透明
-
可以统一处理认证、日志等公共逻辑
-
维护性好,修改 header 传递逻辑只需改一处
注意事项:
-
确保
RequestContextHolder在异步调用中可用 -
敏感 header 可能需要过滤
-
注意 body 对象的序列化/反序列化
-
设置合理的超时时间