我有一个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)
}
}
当我可以访问email
时,它已转换为foo bar@example.com
而非原始foo+bar@example.com
。根据{{3}},这应该只发生在内容为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
为什么Spring会将plus解码为空格?如果这是它的工作方式,When to encode space to plus (+) or %20??
我发现了这个关于它的错误报告:why isn't it encoding pluses as %2B
when making requests这可能暗示这是在3.0.5版本上修复的,我使用的是Spring> 5.0.0。我可能会误解错误报告。
我还发现了关于这些值的RestTemplate处理的讨论:https://jira.spring.io/browse/SPR-6291(我的客户端正在使用RestTemplate)。
所以,我的问题是,为什么Spring这样做?我该如何禁用它?我应该禁用它还是应该在客户端编码加号,即使请求是json?
只是为了澄清,我在这里任何地方都不使用HTML或JavaScript。有一个Spring Rest Controller,而客户端是Spring的RestTemplate
UriTemplate
或UriComponentsBuilder
,它们都不像Spring解码它那样编码加号。
答案 0 :(得分:6)
原始答案
你混合了两件事,当标题有+
时,请求正文中的application/x-www-form-urlencoded
意味着一个空格。请求的正文或内容将取决于标头,但请求可以只有url
而不是headers
而不是body
。
因此,URI的编码不能由任何标题控制
请参阅https://en.wikipedia.org/wiki/Query_string
中的URL Encoding
部分
某些字符不能是URL的一部分(例如,空格),而其他一些字符在URL中具有特殊含义:例如,字符#可用于进一步指定a的子部分(或片段)文献。在HTML表单中,character =用于将名称与值分开。 URI通用语法使用URL编码来处理此问题,而HTML表单进行一些额外的替换,而不是对所有这些字符应用百分比编码。 SPACE编码为' +'或"%20"。[10]
HTML 5指定了使用" get"提交HTML表单的以下转换。 Web服务器的方法。1以下是算法的简要概述:
无法转换为正确字符集的字符将替换为HTML数字字符引用[11] SPACE编码为' +'或者'%20' 字母(A-Z和a-z),数字(0-9)和字符' *',' - ','。'和' _'保持原样 所有其他字符编码为%HH十六进制表示,其中任何非ASCII字符首先编码为UTF-8(或其他指定编码) RFC3986在查询字符串中允许对应于代字号("〜")的八位字节,但需要在HTML表单中进行百分比编码为"%7E"。
SPACE的编码为' +'并选择" as-is"字符将此编码与RFC 3986区分开来。
您可以在google.com
以及屏幕截图
此外,您也可以在其他框架中看到相同的行为。下面是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();
下面是转换内容实际发生的地方
if( buff[ j ] == '+' && query) {
buff[idx]= (byte)' ' ;
} else if( buff[ j ] != '%' ) {
这意味着它是一个嵌入式tomcat服务器,它执行此转换,而spring甚至不参与此操作。没有配置可以更改此行为,如类代码中所示。所以你必须忍受它
答案 1 :(得分:2)
如果您有此请求:
http://localhost/foo?email=foo+bar@example.com
然后原始为foo bar@example.com
。如果您说原应该 foo+bar@example.com
,那么请求应为:
http://localhost/foo?email=foo%2Bbar@example.com
所以Spring正如所期望的那样工作。也许在客户端上,您应该检查URI是否正确编码。客户端URL编码负责构建正确的HTTP请求。
如果您在JavaScript中生成请求,请参阅encodeURI();如果您在Spring中生成请求,请参阅uriToString()。
构建您的请求字符串(?
之后的部分),没有任何编码,使用未编码的值,例如foo+bar@email.com
,并且只在最后,在GET
中实际使用它之前,编码所有这些都与客户端平台上的可用内容有关。如果您想使用POST
,那么您应该根据您选择的MIME类型对其进行编码。
答案 2 :(得分:2)
SPR-6291已在let tableView = UITableView(frame: self.view.frame, style: UITableViewStyle.grouped)
tableView.delegate = self
tableView.dataSource = self
tableView.estimatedSectionHeaderHeight = 0
tableView.estimatedSectionFooterHeight = 0
tableView.register(UITableViewCell.classForCoder(), forCellReuseIdentifier: "cellID")
let tableView = UITableView(frame: self.view.frame, style: UITableViewStyle.grouped)
tableView.delegate = self
tableView.dataSource = self
tableView.estimatedSectionHeaderHeight = 0
tableView.estimatedSectionFooterHeight = 0
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cellID")
中修复了此问题,但在SPR-11047尚未解决的其他情况下,此问题仍未得到解决。虽然SPR-6291的优先级是 Major ,但SPR-11047的优先级是次要。
去年我在旧的Spring工作时,我遇到了这个问题。我们可以通过多种方式在Spring v3.0.5
中获取数据。因此,其中两个是通过controller
或@RequestParam
注释
正如其他人所提到的,我认为它是春天的内部问题而不是特别属于@PathVariable
编码,因为我是通过URL
请求发送数据但它是有点编码问题。但我也同意其他人的观点,因为现在它只在POST
中存在问题。
所以我知道两个解决方案:
您可以使用URL
代替@PathVariable
,因为@RequestParam
SPR-6291
{+ 1}}中的加号问题已修复仍为“@PathVariable
@RequestParam
开放
我的春天版本甚至没有通过SPR-11047
注释接受加号,所以这就是我克服问题的方法(我不记得它是一步一步的步骤,但它会给你提示)。
在您的情况下,您可以在发送请求之前通过@PathVariable
和JS
加号获取字段。像这样:
escape