在springboot 项目上传文件时出现了以下错误
Required request part 'file' is not present
我是通过在Filter中使用HttpServletRequestWrapper 的形式打印请求和返回日志的,经过排查发现,使用HttpServletRequestWrapper 打印请求参数时,发现当HttpServletRequest请求的Content-Type 为application/json时能够正常解析,但是当用表单提交的形式时,springboot 找不到表单提交的参数,返回参数缺失相关的异常。
根据源码查询发现,在RequestParamMethodArgumentResolver类中 resolveName方法 中调用了request.getParameterValues(name),结果返回为null
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
if (servletRequest != null) {
Object mpArg = MultipartResolutionDelegate.resolveMultipartArgument(name, parameter, servletRequest);
if (mpArg != MultipartResolutionDelegate.UNRESOLVABLE) {
return mpArg;
}
}
Object arg = null;
MultipartRequest multipartRequest = request.getNativeRequest(MultipartRequest.class);
if (multipartRequest != null) {
List<MultipartFile> files = multipartRequest.getFiles(name);
if (!files.isEmpty()) {
arg = (files.size() == 1 ? files.get(0) : files);
}
}
if (arg == null) {
String[] paramValues = request.getParameterValues(name);
if (paramValues != null) {
arg = (paramValues.length == 1 ? paramValues[0] : paramValues);
}
}
return arg;
}
HTML中的form表单有一个关键属性 enctype=application/x-www-form-urlencoded 或multipart/form-data。
enctype=application/x- www-form-urlencoded是默认的编码方式,这种编码方式很简单,编码后的结果通常是field1=value2&field2=value2&… 的形式,如 name=aaaa&Submit=Submit。这种编码的具体规则可以在 rfc2231 里查到, 通常使用的表单也 是采用这种方式编码的,Servlet 的 API 提供了对这种 编码方式解码的支持,只需要调用 ServletRequest 类中的getParameter()方法就可 以得到用户表单中的字段和数据。
而我们知道 request.getParameter(),request.getInputStream(),request.getReader()这三种方法是有冲突的,因为流只能读取一次。所以在我们通过表单提交的数据找不到。
我们根据HttpServletRequest 的Content-Type类型进行判断HttpServletRequestWrapper 进行哪种处理。
更改后的 RequestWrapper如下
@Slf4j
public class RequestWrapper extends HttpServletRequestWrapper {
private byte[] body;
/**
* Constructs a request object wrapping the given request.
*
* @param request The request to wrap
* @throws IllegalArgumentException if the request is null
*/
public RequestWrapper(HttpServletRequest request) {
super(request);
try {
if(HttpMethod.POST.matches(request.getMethod())){
log.error(request.getContentType()+"---------------");
if(request.getContentType().contains("application/x-www-form-urlencoded")){
// 通过getParameter的形式解析
body= JacksonUtil.objectToJson(request.getParameterMap()).getBytes();
}else if(request.getContentType().contains("multipart/form-data")){
body="文件类型,不解析".getBytes();
}else {
body = IOUtils.toByteArray(request.getInputStream());
}
}else { // get 请求通过getParameterMap 的方式获取
body= JacksonUtil.objectToJson(request.getParameterMap()).getBytes();
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public ServletInputStream getInputStream() throws IOException {
return new SignWrapperInputStream(body);
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
public byte[] getBody() {
return body;
}
public void setBody(byte[] body) {
this.body = body;
}
private class SignWrapperInputStream extends ServletInputStream {
private ByteArrayInputStream buffer;
public SignWrapperInputStream(byte[] body) {
body = (body == null) ? new byte[0] : body;
this.buffer = new ByteArrayInputStream(body);
}
@Override
public int read() throws IOException {
return buffer.read();
}
@Override
public boolean isFinished() {
return buffer.available() == 0;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener listener) {
throw new RuntimeException("Not implemented");
}
}
}
这样就可以进行json、表单、还有文件相关的日志打印了。按照道理来说 multipart/form-data类型应该也是通过 request.getInputStream()或者request.getReader()获取的,不知道为什么也是不能通过HttpServletRequestWrapper解析,有知道的小伙伴请留言告诉我一下,不胜感激。
网友评论