【问题标题】:Spring REST Controller understanding arrays of strings when having special characters like blank spaces or commasSpring REST Controller 在具有空格或逗号等特殊字符时理解字符串数组
【发布时间】:2014-08-24 21:46:17
【问题描述】:

我正在尝试编写一个 Spring REST 控制器,将字符串数组作为 HTTP GET 请求的输入参数。

问题出现在GET请求中,在数组的某些字符串中,我使用了逗号,、空格或正斜杠/等特殊字符,不管我URL 编码 URL HTTP GET 请求的查询部分

这意味着字符串“1/4 cup ricotta, yogurt”(edit 需要被视为作为输入数组的字符串元素包含的唯一成分)采用以下任一格式:

http://127.0.0.1:8080/[...]/parseThis?[...]&ingredients=1/4 cup ricotta, yogurt

这种格式(请注意空格编码为+加号,而不是十六进制代码):

http://127.0.0.1:8080/[...]/parseThis?[...]&ingredients=1%2F4+cup+ricotta%2C+yogurt

或者这种格式(请注意空格编码为十六进制代码%20):

http://127.0.0.1:8080/[...]/parseThis?[...]&ingredients=1%2F4%20cup%20ricotta%2C%20yogurt

没有正确渲染。

  • 系统无法将输入字符串识别为数组的单个元素。
  • 在第 2 种和第 3 种情况下,系统以逗号分隔输入字符串并返回一个由 2 个元素而不是 1 个元素组成的数组。我希望这里有 1 个元素。

控制器的相关代码为:

@RequestMapping(
        value = "/parseThis",
        params = {
                "language",
                "ingredients"

        }, method = RequestMethod.GET, headers = HttpHeaders.ACCEPT + "=" + MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public HttpEntity<CustomOutputObject> parseThis(
        @RequestParam String language,
        @RequestParam String[] ingredients){

    try {

        CustomOutputObject responseFullData = parsingService.parseThis(ingredients, language);

        return new ResponseEntity<>(responseFullData, HttpStatus.OK);

    } catch (Exception e) {
        // TODO
    }

}

我需要对这个 Spring 控制器执行 HTTP GET 请求,这是一个要求(所以这里不能使用 HTTP POST)。

编辑 1:

如果我将HttpServletRequest request 添加到控制器中方法的签名中,然后我添加一个类似log.debug("The query string is: '" + request.getQueryString() + "'"); 的日志语句,然后我会在日志中看到类似The query string is: '&amp;language=en&amp;ingredients=1%2F4+cup+ricotta%2C+yogurt' 的行(所以仍然是URL 编码)。

编辑 2:

另一方面,如果我将WebRequest request 添加到方法的签名中,日志为log.debug("The query string is: '" + request.getParameter("ingredients") + "'");,那么我在日志中得到一个字符串为The query string is: '1/4 cup ricotta, yogurt'(因此URL 被解码)。

我使用 Apache Tomcat 作为服务器。

我需要在 Spring/webapp 配置文件中添加/查看任何过滤器或其他内容吗?

编辑 3:

主要问题在于逗号的解释:

@ResponseBody
@RequestMapping(value="test", method=RequestMethod.GET)
public String renderTest(@RequestParam("test") String[] test) {
    return test.length + ": " + Arrays.toString(test);
    // /app/test?test=foo,bar => 2: [foo, bar]
    // /app/test?test=foo,bar&test=baz => 2: [foo,bar, baz]
}

可以防止这种行为吗?

【问题讨论】:

  • 尝试在您的操作方法中获取请求 QueryString。你如何运行它?我猜应用服务器可能正在为你解码 url。
  • 好点,我已经编辑了添加相关信息的问题。谢谢。
  • 看这里wiki.apache.org/tomcat/FAQ/CharacterEncoding。我会首先尝试解决这个问题。
  • 如果您只关注 Spring 在绑定到数组时将查询参数拆分为逗号这一事实,您可以简化此问题并实现SSCCE。查询编码/空格的事情似乎无关紧要。

标签: java arrays spring rest spring-mvc


【解决方案1】:

请求参数到你的方法参数的路径经过参数值提取然后参数值转换。现在发生的事情是:

提取

参数为extracted as a single String value。这可能是为了让简单的属性可以作为简单的字符串值传递给以后的值转换。

转化率

Spring 使用ConversionService 进行值转换。在其默认设置StringToArrayConverter is used 中,不幸的是,它将字符串作为逗号分隔列表处理。

做什么

您对 Spring 处理单值请求参数的方式感到非常困惑。所以我会手动进行绑定:

// Method annotations
public HttpEntity<CustomOutputObject> handlerMethod(WebRequest request) {
    String[] ingredients = request.getParameterValues("ingredients");
    // Do other stuff 
}

您还可以查看Spring guys have to say about this.. 和related SO question 的内容。

【讨论】:

  • 感谢您提供有关逗号的详细信息!我已经使用HttpServletRequestgetQueryString() 解决了它,然后用纯逗号, 作为分隔符拆分编码字符串,然后安全地解码数组的每个字符串(即使它包含逗号)。
【解决方案2】:

好吧,您可以注册一个custom conversion service(来自this SO answer),但这似乎需要做很多工作。 :) 如果是我,我会忽略方法签名中的 @RequestParam 声明,并使用传入的 request 对象解析值。

【讨论】:

    【解决方案3】:

    我建议您尝试以下格式:

    ingredients=egg&ingredients=milk&ingredients=butter
    

    &amp;ingredients 附加到末尾将处理数组只有一个值的情况。

    ingredients=egg&ingredients=milk&ingredients=butter&ingredients
    ingredients=milk,skimmed&ingredients   
    

    需要从数组中删除额外的条目,使用List&lt;String&gt; 会使这更容易。

    或者,如果您尝试使用 spring-data-jpa 实现 REST 控制器以通过管道直接进入数据库,您应该查看 spring-data-rest。这是example

    你基本上用 @RepositoryRestResource 注释你的存储库,其余的由 spring 完成 :)

    【讨论】:

    • 我遇到的问题是其中一个ingredients 参数里面可能有一个逗号,它们不像eggmilk 那样简单,它们像1/4 cup ricotta, yogurt 和需要被视为一种独特的成分(您不会将ricottayogurt 视为两种不同的成分)
    • 我做了一个快速测试应用程序,http://localhost:8080/?list=egg&amp;list=milk,%20skimmed&amp;list=butter 结果为{"egg", "milk, skimmed", "butter"}.. 你试过了吗?
    • 我在这样的场景中,我只有 1 个成分数组元素,至少与“milk, skimmed”一样复杂,预计将作为具有 1 个元素的数组处理,我正在处理 2 个元素,使用逗号, 作为分隔符。我没有成功尝试过这个:(
    • 当有ingredients的单个实例时,它看起来像被反序列化为csv,但是当你添加多个时,每个实例都成为数组中的一个值
    • 我看到了问题,如何在末尾添加一个空的&amp;ingredients 然后删除它? ingredients=milk,skimmed&amp;ingredients 然后在控制器中使用 List&lt;String&gt; ingredientsingredients.remove(ingredients.size()-1)
    【解决方案4】:

    来自here的解决方案

    public String get(WebRequest req) {
        String[] ingredients = req.getParameterValues("ingredients");
        for(String ingredient:ingredients ) {
            System.out.println(ingredient);
        }
        ...
    }
    

    这适用于您有一个包含逗号的单一成分的情况

    【讨论】:

      猜你喜欢
      • 2013-09-01
      • 2011-04-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-03
      • 1970-01-01
      • 2011-08-16
      • 1970-01-01
      相关资源
      最近更新 更多