为什么应用程序/ json上的Spring解码+(加号)会得到请求?我该怎么办呢?

pup*_*eno 6 url encoding spring resttemplate

我有一个Spring应用程序接收类似的请求http://localhost/foo?email=foo+bar@example.com.这会触发一个大致如下所示的控制器:

@RestController
@RequestMapping("/foo")
public class FooController extends Controller {
    @GetMapping
    public void foo(@RequestParam("email") String email) {
       System.out.println(email)
    }
}
Run Code Online (Sandbox Code Playgroud)

当我可以访问时email,它已被转换为foo bar@example.com而不是原始的foo+bar@example.com.根据何时将空间编码为加号(+)或%20?这应该只发生在内容所在的请求中application/x-www-form-urlencoded.我的请求的内容类型为application/json.请求的完整MIME标头如下所示:

=== MimeHeaders ===
accept = application/json
content-type = application/json
user-agent = Dashman Configurator/0.0.0-dev
content-length = 0
host = localhost:8080
connection = keep-alive
Run Code Online (Sandbox Code Playgroud)

为什么Spring会将plus解码为空格?如果这是它应该工作的方式,为什么它不像编写%2B请求时那样编码

我发现了这个关于它的错误报告:https://jira.spring.io/browse/SPR-6291这可能暗示这是在3.0.5版本上修复的,我使用的是Spring> 5.0.0.我可能会误解错误报告的某些内容.

我还发现了关于这些值的RestTemplate处理的讨论:https://jira.spring.io/browse/SPR-5516(我的客户端正在使用RestTemplate).

所以,我的问题是,为什么Spring这样做?我该如何禁用它?我应该禁用它还是应该在客户端编码加号,即使请求是json?

只是为了澄清,我在这里任何地方都不使用HTML或JavaScript.有一个Spring Rest Controller,客户端是Spring的RestTemplate,UriTemplate或者UriComponentsBuilder都没有像Spring解码它那样编码加号.

Tar*_*ani 6

原始答案

你正在混合2件事,+请求正文中的a表示标题有空格application/x-www-form-urlencoded.请求的主体或内容将取决于标头,但请求可以只有a url,no headers和no body.

因此,URI的编码不能由任何标头控制

请参阅https://en.wikipedia.org/wiki/Query_string中URL Encoding部分

某些字符不能是URL的一部分(例如,空格),而其他一些字符在URL中具有特殊含义:例如,字符#可用于进一步指定文档的子部分(或片段).在HTML表单中,character =用于将名称与值分开.URI通用语法使用URL编码来处理此问题,而HTML表单进行一些额外的替换,而不是对所有这些字符应用百分比编码.SPACE编码为"+"或"%20".[10]

HTML 5指定了使用"get"方法向Web服务器提交HTML表单的以下转换.1以下是算法的简要概述:

无法转换为正确字符集的字符将替换为HTML数字字符引用[11] SPACE编码为"+"或"%20"字母(A-Z和a-z),数字(0-9)和字符'*',' - ','.' 并且'_'保持原样所有其他字符编码为%HH十六进制表示,其中任何非ASCII字符首先编码为UTF-8(或其他指定编码)允许对应于波形符("〜")的八位字节在RFC3986的查询字符串中,但需要在HTML表单中将百分比编码为"%7E".

将SPACE编码为"+"并选择"原样"字符将此编码与RFC 3986区分开来.

并且您可以google.com从下面的屏幕截图中看到相同的行为

+翻译为空间

%2B翻译为+

您也可以在其他框架中看到相同的行为.下面是Python Flask的一个例子

烧瓶演示

所以你看到的是正确的,你只是将它与一个文档进行比较,该文档引用了请求的正文内容而不是URL

编辑1:5月22日

经过调试后,似乎解码在Spring中都没有发生.我package org.apache.tomcat.util.buf;UDecode课堂上都有

/**
 * URLDecode, will modify the source.
 * @param mb The URL encoded bytes
 * @param query <code>true</code> if this is a query string
 * @throws IOException Invalid %xx URL encoding
 */
public void convert( ByteChunk mb, boolean query )
    throws IOException
{
    int start=mb.getOffset();
Run Code Online (Sandbox Code Playgroud)

以下是转换实际发生的地方

    if( buff[ j ] == '+' && query) {
        buff[idx]= (byte)' ' ;
    } else if( buff[ j ] != '%' ) {
Run Code Online (Sandbox Code Playgroud)

这意味着它是一个嵌入式tomcat服务器,它执行此转换,而spring甚至不参与此操作.没有配置可以更改此行为,如类代码中所示.所以你必须忍受它