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

时间:2018-05-10 10:05:09

标签: spring url encoding 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)
    }
}

当我可以访问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 UriTemplateUriComponentsBuilder,它们都不像Spring解码它那样编码加号。

3 个答案:

答案 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以及屏幕截图

中看到相同的行为

+ translated to space

%2B translated to +

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

Flask Demo

所以你看到的是正确的,你只是将它与一个引用请求的正文内容而不是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的优先级是次要

SPR-11047

去年我在旧的Spring工作时,我遇到了这个问题。我们可以通过多种方式在Spring v3.0.5中获取数据。因此,其中两个是通过controller@RequestParam注释

正如其他人所提到的,我认为它是春天的内部问题而不是特别属于@PathVariable 编码,因为我是通过URL请求发送数据但它是有点编码问题。但我也同意其他人的观点,因为现在它只在POST中存在问题。

所以我知道两个解决方案

  1. 您可以使用URL代替@PathVariable,因为@RequestParam SPR-6291 {+ 1}}中的加号问题已修复仍为“@PathVariable

  2. @RequestParam开放
  3. 我的春天版本甚至没有通过SPR-11047注释接受加号,所以这就是我克服问题的方法(我不记得它是一步一步的步骤,但它会给你提示)。

  4. 在您的情况下,您可以在发送请求之前通过@PathVariableJS加号获取字段。像这样:

    escape
相关问题