前提
由于本人喜欢使用feign做请求转发,故而时常会遇到一些奇奇怪怪的问题
在这里声明一下,feign不是万能的,确实有一些请求不能无缝转发
基础的使用可以参考我之前的文章,可以解决因为版本升级导致无法使用等问题
https://blog.csdn.net/yuexiahunone/article/details/132175038
可以解决的问题
- Content type ‘’ not supported
- Failed to parse multipart servlet request
- Current request is not a multipart request
- feign.codec.EncodeException: Error converting request body
- file=org.springframework.web.multipart.support.StandardMultipartHttpServletRequest%24StandardMultipartFile%402bab8d83
- 读取流失败
- 转换流失败
本人遇到的问题
可以略过
在服务端B读取流信息转换失败
这里报的错 Request.parseParts()
流程大概就是处理表单
获取参数 StandardMultipartHttpServletRequest.getParameterMap
表单参数不是空,初始化并转换表单信息 StandardMultipartHttpServletRequest.initializeMultipart()/parseRequest(HttpServletRequest request)
发现是文件处理文件 Request.parseParts(2562行)
初始化文件(FileItemIteratorImpl.init)
获取流长度
读取流信息
获取编码格式
获取content-type
获取boundary值 FileUploadBase.getBoundary(我这里报错了)
把流信息读取到文件中
需要断点的文件
StandardMultipartHttpServletRequest
HttpServletRequestWrapper
Request
FileUploadBase
FileUpload
FileItemIteratorImpl
MultipartStream
开发环境
- 前端为vue,使用axios插件,表单方式提交
- 后台为springboot
- 局域网(这个没影响)
- 上传的是excel文件
注意事项
- 后台feign接口参数一定要对应上
- 头信息要设置对
- 高版本的spring一定要修改代码,参考链接
代码排查
- 主要是排查后台代码
- 服务端A,feign的接参,可以使用@RequestPart和@RequestParam,我使用的是@RequestPart
- 服务端A,feign的content-type是否设置正确,consumes = “multipart/form-data”
- 服务端B,接口的参数要和服务端A的feign接口一致,但是不需要设置content-type
- 服务端B在拦截器中要判断一下content-type,如果是multipart/form-data,则略过,不要在拦截器中读取流信息
- 服务端A,不要在feign的拦截器中设置content-type,会覆盖接口上的content-type,
例如template.header(“Content-Type”, “multipart/form-data”);,此举会导致服务端B无法读取content-lenght以及boundary值(我就是栽倒在这里了)
上代码
以下代码为伪代码
//前端
//post方式,使用data作为数据载体
export function apiUploadFile(data) {
return request({
url: '/backend/oak/cardInfo/uploadCardInfo',
method: 'post',
data: data
})
}
function uploadFile(file){
//这里使用表单方式提交
let forms = new FormData()
forms.append('file', file)
this.apiUploadFile(forms).then(res => {
///......
});
}
//服务端A
@RestController
@RequestMapping(value = "/api")
@FeignClient(name = "UploadFileProvider", url = "${apiUrl}", path = "/api", configuration = {FeignUploadFileInterceptor.class})
public interface UploadFileApi {
//consumes就是content-type,因为是文件(流信息)所以设置为multipart/form-data
//然后feign会自动设置content-length以及boundary值,这里的boundary值会被修改
@Operation(summary = "上传文件")
@PostMapping(value = "/uploadFile", consumes = "multipart/form-data")
FeignResult<Object> uploadCardInfo(@RequestPart("file") MultipartFile file);
}
//服务端B
@RestController
@RequestMapping(value = "/api")
@RequiredArgsConstructor
public class UploadFileController {
private final OssService ossService;
@Operation(summary = "上传卡信息")
@PostMapping("/uploadFile")
public Result<Void> uploadFile(@RequestPart("file") MultipartFile file) {
return Result.data(() -> ossService.uploadFile(file));
}
}
//拦截器代码 .....
//这里不写了,没啥东西,记住不要在这里设置content-type