英文:
Spring form input values return null after read it once in filter, despite using request wrapper
问题
在我的Spring Boot项目中,我使用FreeMarker模板来创建示例表单。我需要添加一个过滤器来读取请求的有效负载并进行一些操作。我知道如果在过滤器中读取了有效负载,就需要重置请求体,因为它只能被读取一次。由于我以前遇到过这个问题,我知道我必须使用包装器。我期望能够像以前一样解决我的问题。然而,在控制器中,输入对象中的所有字段都是空的。
在这里我漏掉了什么?
我的过滤器:
public class KfsInMsgFilter extends GenericFilterBean {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
final HttpServletResponse response = (HttpServletResponse) servletResponse;
Map<String, String[]> extraParams = new TreeMap<String, String[]>();
WrappedRequest wrappedRequest = new WrappedRequest(request, extraParams);
String body = IOUtils.toString(new BufferedReader(new InputStreamReader(wrappedRequest.getInputStream(), Constants.UTF_8)));
// 使用body做一些操作
// ....
// 重置有效负载
wrappedRequest.resetStream(body.getBytes(Constants.UTF_8));
...
}
}
WrappedRequest类:
@Slf4j
public class WrappedRequest extends HttpServletRequestWrapper {
private final Map<String, String[]> modifiableParameters;
private ResettableServletInputStream servletStream;
private byte[] rawData;
private HttpServletRequest request;
private String payload;
/**
* 创建一个新的请求包装器,将附加的参数合并到请求对象中,而不会过早地从原始请求中读取参数。
*
* @param request
* @param additionalParams
*/
public WrappedRequest(final HttpServletRequest request,
final Map<String, String[]> additionalParams) {
super(request);
this.request = request;
this.modifiableParameters = new TreeMap<String, String[]>();
this.modifiableParameters.putAll(additionalParams);
this.servletStream = new ResettableServletInputStream();
}
/**
* @param newRawData
*/
public void resetStream(byte[] newRawData) {
servletStream.stream = new ByteArrayInputStream(newRawData);
}
/**
* @return
* @throws IOException
*/
@Override
public ServletInputStream getInputStream() throws IOException {
if (rawData == null) {
rawData = IOUtils.toByteArray(this.request.getReader());
servletStream.stream = new ByteArrayInputStream(rawData);
}
return servletStream;
}
/**
* @return
* @throws IOException
*/
@Override
public BufferedReader getReader() throws IOException {
if (rawData == null) {
rawData = IOUtils.toByteArray(this.request.getReader());
servletStream.stream = new ByteArrayInputStream(rawData);
}
return new BufferedReader(new InputStreamReader(servletStream, Constants.UTF_8));
}
/**
* @return
*/
private String getBodyAsString() {
StringBuffer buff = new StringBuffer();
buff.append(" BODY_DATA START [ ");
char[] charArr = new char[getContentLength()];
try {
BufferedReader reader = new BufferedReader(getReader());
reader.read(charArr, 0, charArr.length);
reader.close();
} catch (IOException e) {
log.error("", e);
}
buff.append(charArr);
buff.append(" ] BODY_DATA END ");
return buff.toString();
}
/**
* @return
*/
public String getPayload() {
return payload;
}
/**
* @param payload
*/
public void setPayload(String payload) {
this.payload = payload;
}
private static class ResettableServletInputStream extends ServletInputStream {
private InputStream stream;
@Override
public int read() throws IOException {
return stream.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
}
}
我期望在控制器中获得的请求体内容:
我实际获得的内容:
@PostMapping(value = "/edit")
public String editPlatform(EditInfo editInfo, Model model) {
Optional<Platform> p = platformService.findById(editInfo.getId());
List<SafeCustodyOffice> officeList = safeCustodyOfficeService.getAll();
if (p.isPresent()) {
model.addAttribute("platform", p.get());
model.addAttribute("offices", officeList);
return "platform-edit";
} else {
throw new KfsException(ErrorCodes.KFS19);
}
}
重要更改:
我发现了一些有趣的内容,这些内容为我解决问题提供了线索。这可能对其他人来说更有意义。
我注意到输入的内容类型会改变结果,就像这样:
是否有任何方法可以让第5行的组合像第3行一样工作?
英文:
In my spring-boot project, I use freemarker templates for sample forms. I needed to add filter in order to read payload and do some stuff. I know if you read payload in filter, you need to reset request body. Because it can be read once. Since I encountered this problem before, I knew that I must have used wrapper. I expected solve my problem as before. However, in the controller, all fields in input objects are null.
What am I missing in here ?
My filter:
public class KfsInMsgFilter extends GenericFilterBean {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
final HttpServletResponse response = (HttpServletResponse) servletResponse;
Map<String, String[]> extraParams = new TreeMap<String, String[]>();
WrappedRequest wrappedRequest = new WrappedRequest(request, extraParams);
String body = IOUtils.toString(new BufferedReader(new InputStreamReader(wrappedRequest.getInputStream(), Constants.UTF_8)));
// doing some stuff using body
// ....
// resetting payload
wrappedRequest.resetStream(body.getBytes(Constants.UTF_8));
...
}
}
WrappedRequest class:
@Slf4j
public class WrappedRequest extends HttpServletRequestWrapper {
private final Map<String, String[]> modifiableParameters;
private ResettableServletInputStream servletStream;
private byte[] rawData;
private HttpServletRequest request;
private String payload;
/**
* Create a new request wrapper that will merge additional parameters into
* the request object without prematurely reading parameters from the
* original request.
*
* @param request
* @param additionalParams
*/
public WrappedRequest(final HttpServletRequest request,
final Map<String, String[]> additionalParams) {
super(request);
this.request = request;
this.modifiableParameters = new TreeMap<String, String[]>();
this.modifiableParameters.putAll(additionalParams);
this.servletStream = new ResettableServletInputStream();
}
/**
* @param newRawData
*/
public void resetStream(byte[] newRawData) {
servletStream.stream = new ByteArrayInputStream(newRawData);
}
/**
* @return
* @throws IOException
*/
@Override
public ServletInputStream getInputStream() throws IOException {
if (rawData == null) {
rawData = IOUtils.toByteArray(this.request.getReader());
servletStream.stream = new ByteArrayInputStream(rawData);
}
return servletStream;
}
/**
* @return
* @throws IOException
*/
@Override
public BufferedReader getReader() throws IOException {
if (rawData == null) {
rawData = IOUtils.toByteArray(this.request.getReader());
servletStream.stream = new ByteArrayInputStream(rawData);
}
return new BufferedReader(new InputStreamReader(servletStream, Constants.UTF_8));
}
/**
* @return
*/
private String getBodyAsString() {
StringBuffer buff = new StringBuffer();
buff.append(" BODY_DATA START [ ");
char[] charArr = new char[getContentLength()];
try {
BufferedReader reader = new BufferedReader(getReader());
reader.read(charArr, 0, charArr.length);
reader.close();
} catch (IOException e) {
log.error("", e);
}
buff.append(charArr);
buff.append(" ] BODY_DATA END ");
return buff.toString();
}
/**
* @return
*/
public String getPayload() {
return payload;
}
/**
* @param payload
*/
public void setPayload(String payload) {
this.payload = payload;
}
private static class ResettableServletInputStream extends ServletInputStream {
private InputStream stream;
@Override
public int read() throws IOException {
return stream.read();
}
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
}
}
Body I expected to get in controller:
@PostMapping(value = "/edit")
public String editPlatform(EditInfo editInfo, Model model) {
Optional<Platform> p = platformService.findById(editInfo.getId());
List<SafeCustodyOffice> officeList = safeCustodyOfficeService.getAll();
if (p.isPresent()) {
model.addAttribute("platform", p.get());
model.addAttribute("offices", officeList);
return "platform-edit";
} else {
throw new KfsException(ErrorCodes.KFS19);
}
}
Important Edit:
I discovered someting I found interesting and gives me clues about the problem. This may be makes more sense for anybody but me.
I see that the content type of input changes the result like this:
Is there any workaround to make row 5 combination work like row 3?
专注分享java语言的经验与见解,让所有开发者获益!
评论