【问题标题】:Spring-boot controller error when url contains braces character当 url 包含大括号字符时 Spring-boot 控制器错误
【发布时间】:2018-12-04 11:25:27
【问题描述】:

我有一个控制器,它以GET 接收请求,带有两个参数ep

@GetMapping("")
public String getIframe(
    @RequestParam(value = "p", required = false) String p,
    @RequestParam(value = "e", required = false) String e
){
    System.out.println("Hi");
}

在某些情况下,ep 值可以包含 {} 字符。 它在 spring-boot 1 中运行良好,但是在将模块更新到 spring-boot 2 后它开始引发异常:

java.net.URISyntaxException: Illegal character in query at index 47: http://127.0.0.1:8080/shorteners/click/RikBV?e={CLICKID}&p={PUBID}
  at java.net.URI$Parser.fail(URI.java:2848)
  at java.net.URI$Parser.checkChars(URI.java:3021)
  at java.net.URI$Parser.parseHierarchical(URI.java:3111)
  at java.net.URI$Parser.parse(URI.java:3053)
  at java.net.URI.<init>(URI.java:588)
  at java.net.URI.create(URI.java:850)
  ... 45 common frames omitted
Wrapped by: java.lang.IllegalArgumentException: Illegal character in query at index 47: http://127.0.0.1:8080/shorteners/click/RikBV?e={CLICKID}&p={PUBID}
  at java.net.URI.create(URI.java:852)
  at fr.hamid.testApp.app.configurations.HeadersFilter.doFilter(HeadersFilter.java:43) [26 skipped]
  at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803) [9 skipped]
  at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
  at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790)
  at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1459)
  at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
  at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [2 skipped]
 [1 skipped]

另外,我的过滤器类如下:

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class HeadersFilter implements Filter {

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) res;
        HttpServletRequest request = (HttpServletRequest) req;
        String referer = request.getHeader("referer") ;
        if (referer != null && new UrlValidator().isValid(referer)) {
            URL url = new URL(referer);
            response.setHeader("Access-Control-Allow-Origin", url.getProtocol() + "://" + url.getHost());
        }
        else {
            response.setHeader("Access-Control-Allow-Origin", "*");
        }
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE");
        response.setHeader("Access-Control-Allow-Headers", "x-auth-token, x-requested-with, content-type, accept, " +
                "origin, referer, Authorization, customer-user-id, device-os, t-user-id, t-network-cache-capacity," +
                "t-network-bandwidth, device-client-date, dev-platform, sdk-version, sdk-type, app-package-name," +
                "device-imei, developer-key, device-model, device-os-version, secret-token, t-network-type");

        response.setHeader("Access-Control-Expose-Headers", "x-requested-with");
        response.setHeader("Access-Control-Allow-Credentials", "true");

        if (!"OPTIONS".equals(request.getMethod())) {
            //Exception raises here!
            chain.doFilter(req, res);
        }

        response.setHeader("X-Frame-Options", "*");
    }

    public void init(FilterConfig filterConfig) {
    }

    public void destroy() {
    }

}

当我使用wget 命令时,它运行良好,但是当我使用浏览器调用 url 时出现异常!

有没有办法处理这些类型的网址?

注意:由于客户正在使用这些 url,我无法通过与其他人更改 {} 字符来解决此问题。所以,处理这个服务器端很重要!

非常感谢:)

【问题讨论】:

  • 能发一些代码吗?
  • 请发布您的控制器代码/
  • 您不能使用 PathVariable 代替吗?哪个可以解决您的问题?
  • @JaiDixit 这只是一个简单的控制器。但是,我更新了我的问题并添加了方法。
  • @VelNaga 不幸的是,这个 url 模式已被大量客户使用,我暂时无法更改。

标签: java spring spring-boot


【解决方案1】:

由于 spring boot 2.2.0 可以配置 Tomcat,因此 URL 中允许使用特殊字符。

只需在application.properties 中总结您想要允许的那些

在你的情况下

server.tomcat.relaxed-query-chars= {,}

所有可配置的字符都是 `{}[] >、^>|

application.yml

server:
  tomcat:
    relaxed-query-chars: ['{','}']

【讨论】:

    【解决方案2】:

    查询字符串中带有 {} 的 url 被 tomcat 拒绝,因为它们不是有效的 url 字符。

    wget 之所以有效,是因为它在通过 {=%7B, }=%7D 发送之前对查询字符串进行 urlencode。 @RequestParam 注释会导致 url 解码,因此您只需在变量中看到 { 和 }。如果您查看实际的查询字符串,您会看到 %7B 和 %7D。

    您可以将 tomcat 配置为更轻松地接受哪些字符,但如果可以的话,最好让客户端进行 urlencode。

    Tomcat docs 查找 RelaxQueryChars,这是允许您告诉 tomcat 接受查询字符串中的 {} 的选项。

    已经有一些关于如何设置此属性的答案,例如Setting a tomcat property using spring boot application properties

    import org.apache.catalina.connector.Connector;
    import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;
    import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
    import org.springframework.boot.web.server.WebServerFactoryCustomizer;
    import org.springframework.stereotype.Component;
    
    @Component
    public class TomcatWebServerCustomizer
            implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
    
        @Override
        public void customize(TomcatServletWebServerFactory factory) {
            factory.addConnectorCustomizers(new TomcatConnectorCustomizer() {
                @Override
                public void customize(Connector connector) {
                    connector.setAttribute("relaxedQueryChars", "{}");
                }
            });
        }
    }
    

    【讨论】:

      【解决方案3】:

      假设您希望大括号出现在您的参数字符串中。 您必须在浏览器中使用的 URL 中使用 HTML URL 编码, 参考here

      所以您在浏览器中的 URL 将如下所示:

      http://127.0.0.1:8080/shorteners/click/RikBV?e=%7BCLICKID%7D&p=%7BPUBID%7D

      使用下面的代码进行测试和打印:

      @GetMapping("/urlParamTest")
      public void getIframe(@RequestParam(value = "p", required = false) String p,
              @RequestParam(value = "e", required = false) String e) {
          System.out.println("Hi " + p + " , " + e);
      }
      

      控制台输出:

      Hi {PUBID} , {CLICKID}
      

      希望这会有所帮助!

      【讨论】:

      • 您似乎没有阅读有关问题的Note和cmets!
      • 他们发帖时我可能正在打字。无论如何它的作品。
      • @Code_Is_Law 谢谢你的回答。但这里的主要问题是我现在不能要求所有客户更改 URL。我应该找到一种方法来处理一段时间的大括号。
      【解决方案4】:

      最后我在spring boot中无法解决问题,我有两个选择:

      1- 将 Spring-boot 降级到最新版本。

      2-在Nginx中添加if语句并对请求参数进行编码。

      虽然第二种解决方案效率不高,并且可能会减慢服务请求的速度,但我选择它的原因有两个:

      首先,让 Grafana 监控我的服务对我来说很重要,而旧的 Spring Boot 版本是不可能的。

      其次,对响应时间的影响可以忽略不计,我认为这是值得的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-04-13
        • 2017-08-04
        • 1970-01-01
        • 1970-01-01
        • 2015-10-01
        • 1970-01-01
        • 2015-06-16
        • 1970-01-01
        相关资源
        最近更新 更多