问题背景
项目开发过程中,由于业务需求,需要通过feign实现微服务间文件传输,但其报错 Error converting request body,通过以下方式进行解决:
1.封装feign注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@FeignClient(configuration = MyFeignClientConfiguration.class)
public @interface MyFeignClient {
@AliasFor(annotation = FeignClient.class)
String name();
@AliasFor(annotation = FeignClient.class)
String url();
}
2.引入gradle依赖
api 'org.springframework.cloud:spring-cloud-starter-openfeign'
implementation 'io.github.openfeign:feign-httpclient'
implementation 'io.github.openfeign:feign-jaxb'
implementation group: 'io.github.openfeign.form', name: 'feign-form', version: '3.8.0'
implementation group: 'io.github.openfeign.form', name: 'feign-form-spring', version: '3.8.0'
implementation group: 'commons-fileupload', name: 'commons-fileupload', version: '1.3.3'
3.被调用方对feign进行配置
@Slf4j
public class MyFeignClientConfiguration {
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
@Bean
@ConditionalOnClass(HttpServletRequest.class)
public RequestInterceptor oauth2FeignRequestInterceptor() {
return new TransitiveAuthRequestInterceptor();
}
private static final String REDIRECT_TO_PROPERTY = "endpoint.redirect-to";
/**
* 调试时, 可以通过设置 {@link #REDIRECT_TO_PROPERTY endpoint.redirect-to} 参数改变 client 的 目标服务器.
*
* <p> 例如, 添加下面的参数, 可以将所有微服务模块的请求发往测试环境.
*
* <p> in your {@code application.yml} file:
*
* <pre>{@code
* endpoint:
* redirect-to: http://{appName}.internal.mytest.tech
* }</pre>
*/
@Bean
@ConditionalOnClass(HttpServletRequest.class)
@ConditionalOnProperty(REDIRECT_TO_PROPERTY)
public RequestInterceptor redirectionInterceptor(@Value("${" + REDIRECT_TO_PROPERTY + "}") String redirectTo) {
log.warn("Property [{}] set to [{}]. So all @MyFeignClient requests will be redirected. "
+ "Please make sure you are for the debugging purpose. ", REDIRECT_TO_PROPERTY, redirectTo);
return new EndpointRedirectionInterceptor(redirectTo);
}
@Bean
public FeignErrorDecoder errorDecoder(ObjectMapper objectMapper) {
return new FeignErrorDecoder(objectMapper);
}
@Autowired
private ObjectFactory<HttpMessageConverters> messageConverters;
@Bean
//通过此配置解决微服务间调用报错问题
public Encoder feignFormEncoder() {
return new SpringFormEncoder(new SpringEncoder(messageConverters));
}
}
4.被调用方提供sdk
@MyClient(url = "${endpoint.provider:provider:8061}", name = "provider")
public interface ProviderClient {
@PostMapping(value = "/v1/provider/open/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
MyResponse upload(@RequestPart("myFile") MultipartFile creativeFile,
@RequestParam("myParam1") String myParam1,
@RequestParam("myParam2") String myParam2,
@RequestParam("myParam3") String myParam3,
@RequestParam("myParam4") String myParam4);
}
5.调用方service使用
@Slf4j
@Service
@RequiredArgsConstructor
public class CallerService {
private final ProviderClient providerClient;
public MyResponse upload(HttpServletRequest request) {
final String myParam1= request.getParameter("myParam1");
final String myParam2= request.getParameter("myParam2");
final String myParam3= request.getParameter("myParam3");
final String myParam4= request.getParameter("myParam4");
MultipartHttpServletRequest multipartHttpServletRequest = (MultipartHttpServletRequest) request;
MultipartFile file = multipartHttpServletRequest.getFile("myFile");
return providerClient.upload(file,
myParam1, myParam2, myParam3, myParam4);
}
}
6.调用方controller层
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/v1/call")
public class CallerController {
private final CallerService callerService ;
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@ApiOperation("上传")
@CrossOrigin
public MyResponse upload(HttpServletRequest request) {
return callerService .upload(request);
}
}
7.总结
出现问题第一时间看是什么问题,再根据问题去定位到官方网站去查询,要会正确的搜索是很重要的。