@内容类型为application/x-www-form-urlencoded的RequestParam在Spring Boot 2.2中不起作用

@内容类型为application/x-www-form-urlencoded的RequestParam在Spring Boot 2.2中不起作用,spring,spring-boot,content-type,Spring,Spring Boot,Content Type,从Spring Boot 2.1.10迁移到2.2.4后,下面的方法开始为params参数返回null。它在Spring中不是一个bug,因为它在我制作一个小样本项目时起作用。 它也适用于没有内容类型的普通GET和POST:application/x-www-form-urlencoded @PostMapping(path = "/test", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) public void test(@R

从Spring Boot 2.1.10迁移到2.2.4后,下面的方法开始为
params
参数返回null。它在Spring中不是一个bug,因为它在我制作一个小样本项目时起作用。 它也适用于没有内容类型的普通GET和POST:application/x-www-form-urlencoded

@PostMapping(path = "/test", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public void test(@RequestParam Map<String, String> params) {
    System.out.println(params);
}

任何帮助或想法都将不胜感激。另外,如果有人能告诉我Spring解析参数的地方,我可以试着调试,看看会发生什么

经过无数个小时的调试后,日志过滤器出现了问题。我是这样读请求的:

private static final class BufferedRequestWrapper extends HttpServletRequestWrapper {

    private final byte[] buffer;

    BufferedRequestWrapper(HttpServletRequest req) throws IOException {
        super(req);

        // Read InputStream and store its content in a buffer.
        InputStream is = req.getInputStream();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] buf = new byte[1024];
        int read;
        while ((read = is.read(buf)) > 0) {
            baos.write(buf, 0, read);
        }
        this.buffer = baos.toByteArray();
    }

    @Override
    public ServletInputStream getInputStream() {
        return new BufferedServletInputStream(new ByteArrayInputStream(this.buffer));
    }

    String getRequestBody() throws IOException {
        return IOUtils.readLines(this.getInputStream(), StandardCharsets.UTF_8).stream()
                .map(String::trim)
                .collect(Collectors.joining());
    }
}
这正在使用内容类型
application/x-www-form-urlencoded
中的查询参数。我的解决方案是排除读取
应用程序/x-www-form-urlencoded
的输入流

if (req.getContentType() == null || (req.getContentType() != null && !req.getContentType().startsWith("application/x-www-form-urlencoded"))) {
    // Read InputStream and store its content in a buffer.
    InputStream is = req.getInputStream();
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    byte[] buf = new byte[1024];
    int read;
    while ((read = is.read(buf)) > 0) {
        baos.write(buf, 0, read);
    }
    this.buffer = baos.toByteArray();
} else {
    buffer = new byte[0];
}

经过无数个小时的调试,结果证明这是日志过滤器中的一个问题。我是这样读请求的:

private static final class BufferedRequestWrapper extends HttpServletRequestWrapper {

    private final byte[] buffer;

    BufferedRequestWrapper(HttpServletRequest req) throws IOException {
        super(req);

        // Read InputStream and store its content in a buffer.
        InputStream is = req.getInputStream();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] buf = new byte[1024];
        int read;
        while ((read = is.read(buf)) > 0) {
            baos.write(buf, 0, read);
        }
        this.buffer = baos.toByteArray();
    }

    @Override
    public ServletInputStream getInputStream() {
        return new BufferedServletInputStream(new ByteArrayInputStream(this.buffer));
    }

    String getRequestBody() throws IOException {
        return IOUtils.readLines(this.getInputStream(), StandardCharsets.UTF_8).stream()
                .map(String::trim)
                .collect(Collectors.joining());
    }
}
这正在使用内容类型
application/x-www-form-urlencoded
中的查询参数。我的解决方案是排除读取
应用程序/x-www-form-urlencoded
的输入流

if (req.getContentType() == null || (req.getContentType() != null && !req.getContentType().startsWith("application/x-www-form-urlencoded"))) {
    // Read InputStream and store its content in a buffer.
    InputStream is = req.getInputStream();
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    byte[] buf = new byte[1024];
    int read;
    while ((read = is.read(buf)) > 0) {
        baos.write(buf, 0, read);
    }
    this.buffer = baos.toByteArray();
} else {
    buffer = new byte[0];
}

添加另一个答案,因为我遇到了相同的问题,但以不同的方式解决了它

我也有一个请求日志过滤器,它以Jonas Pedersen在回答中描述的类似方式包装传入请求并缓存响应输入流。Spring Boot已从2.1.2.RELEASE更新为2.3.4.RELEASE

我将传入请求包装在缓存输入流的缓存请求包装器中。 对我来说,问题在于,由于某种原因(到目前为止还不知道),request.getParameterValue(字符串键)方法返回null,即使包装的请求显然有一个非空的参数映射。 简单地访问wrapped requests参数映射就解决了这个问题……非常奇怪

使用Spring Boot 2.1.2.RELEASE的原始包装类:

public class CachingRequestWrapper extends HttpServletRequestWrapper {

    private final byte[] cachedBody;

    public CachingRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        InputStream requestInputStream = request.getInputStream();
        this.cachedBody = StreamUtils.copyToByteArray(requestInputStream);
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        return new CachedInputStream(this.cachedBody);
    }

    @Override
    public BufferedReader getReader() throws IOException {
        ByteArrayInputStream byteArrayInputStream =
                new ByteArrayInputStream(this.cachedBody);
        String encoding = StringUtils.isEmpty(this.getCharacterEncoding())
                ? StandardCharsets.UTF_8.name()
                : this.getCharacterEncoding();
        return new BufferedReader(new InputStreamReader(byteArrayInputStream, encoding));
    }
}
为了简洁起见,省略了CachedInputStream类的实现

仅仅访问包装好的请求映射似乎就解决了整个问题。对于Spring Boot 2.3.4.RELEASE,此版本有效(我删除了一些详细信息):

公共类CachingRequestWrapper扩展了HttpServletRequestWrapper{
私有最终字节[]cachedBody;
私有最终映射参数映射;
公共CachingRequestWrapper(HttpServletRequest请求)引发IOException{
超级(请求);

parameterMap=request.getParameterMap();//添加了另一个答案,因为我遇到了相同的问题,但以不同的方式解决了它

我也有一个请求日志过滤器,它以Jonas Pedersen在回答中描述的类似方式包装传入请求并缓存响应输入流

我将传入请求包装在缓存输入流的缓存请求包装器中。 对我来说,问题在于,由于某种原因(到目前为止还不知道),request.getParameterValue(字符串键)方法返回null,即使包装的请求显然有一个非空的参数映射。 简单地访问wrapped requests参数映射就解决了这个问题……非常奇怪

使用Spring Boot 2.1.2.RELEASE的原始包装类:

public class CachingRequestWrapper extends HttpServletRequestWrapper {

    private final byte[] cachedBody;

    public CachingRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        InputStream requestInputStream = request.getInputStream();
        this.cachedBody = StreamUtils.copyToByteArray(requestInputStream);
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        return new CachedInputStream(this.cachedBody);
    }

    @Override
    public BufferedReader getReader() throws IOException {
        ByteArrayInputStream byteArrayInputStream =
                new ByteArrayInputStream(this.cachedBody);
        String encoding = StringUtils.isEmpty(this.getCharacterEncoding())
                ? StandardCharsets.UTF_8.name()
                : this.getCharacterEncoding();
        return new BufferedReader(new InputStreamReader(byteArrayInputStream, encoding));
    }
}
为了简洁起见,省略了CachedInputStream类的实现

简单地访问包装好的请求映射似乎就解决了整个问题。对于Spring Boot 2.3.4.RELEASE,此版本有效(我删除了一些细节):

公共类CachingRequestWrapper扩展了HttpServletRequestWrapper{
私有最终字节[]cachedBody;
私有最终映射参数映射;
公共CachingRequestWrapper(HttpServletRequest请求)引发IOException{
超级(请求);

parameterMap=request.getParameterMap();//POST和request参数?没有看到…可能是您应该查找servlet api的实现…但这似乎不是一个错误。POST和request参数?没有看到…可能是您应该查找servlet api的实现…但这似乎不是一个错误。感谢您发布此消息,它为我指出了正确的方向方向,尽管我以不同的方式解决了它。感谢你发布这篇文章,它为我指明了正确的方向,尽管我以不同的方式解决了它。