在springboot 項(xiàng)目上傳文件時(shí)出現(xiàn)了以下錯(cuò)誤
Required request part 'file' is not present
我是通過在Filter中使用HttpServletRequestWrapper 的形式打印請(qǐng)求和返回日志的,經(jīng)過排查發(fā)現(xiàn)去枷,使用HttpServletRequestWrapper 打印請(qǐng)求參數(shù)時(shí)怖辆,發(fā)現(xiàn)當(dāng)HttpServletRequest請(qǐng)求的Content-Type 為application/json時(shí)能夠正常解析是复,但是當(dāng)用表單提交的形式時(shí),springboot 找不到表單提交的參數(shù)竖螃,返回參數(shù)缺失相關(guān)的異常淑廊。
根據(jù)源碼查詢發(fā)現(xiàn),在RequestParamMethodArgumentResolver類中 resolveName方法 中調(diào)用了request.getParameterValues(name)特咆,結(jié)果返回為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表單有一個(gè)關(guān)鍵屬性 enctype=application/x-www-form-urlencoded 或multipart/form-data季惩。
enctype=application/x- www-form-urlencoded是默認(rèn)的編碼方式,這種編碼方式很簡(jiǎn)單腻格,編碼后的結(jié)果通常是field1=value2&field2=value2&… 的形式画拾,如 name=aaaa&Submit=Submit。這種編碼的具體規(guī)則可以在 rfc2231 里查到荒叶, 通常使用的表單也 是采用這種方式編碼的碾阁,Servlet 的 API 提供了對(duì)這種 編碼方式解碼的支持,只需要調(diào)用 ServletRequest 類中的getParameter()方法就可 以得到用戶表單中的字段和數(shù)據(jù)些楣。
而我們知道 request.getParameter()脂凶,request.getInputStream(),request.getReader()這三種方法是有沖突的愁茁,因?yàn)榱髦荒茏x取一次蚕钦。所以在我們通過表單提交的數(shù)據(jù)找不到。
我們根據(jù)HttpServletRequest 的Content-Type類型進(jìn)行判斷HttpServletRequestWrapper 進(jìn)行哪種處理鹅很。
更改后的 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 請(qǐng)求通過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");
}
}
}
這樣就可以進(jìn)行json、表單促煮、還有文件相關(guān)的日志打印了邮屁。按照道理來說 multipart/form-data類型應(yīng)該也是通過 request.getInputStream()或者request.getReader()獲取的,不知道為什么也是不能通過HttpServletRequestWrapper解析菠齿,有知道的小伙伴請(qǐng)留言告訴我一下佑吝,不勝感激。