【问题标题】:How to handle requests that includes forward slashes (/)?如何处理包含正斜杠 (/) 的请求?
【发布时间】:2015-10-03 22:36:59
【问题描述】:

我需要处理如下请求:

www.example.com/show/abcd/efg?name=alex&family=moore   (does not work)
www.example.com/show/abcdefg?name=alex&family=moore   (works)
www.example.com/show/abcd-efg?name=alex&family=moore   (works)

它应该接受位于www.example.com/show/? 之间的值中的任何类型的字符。请注意,将位于其中的值将是单个值而不是操作的名称。

例如:/show/abcd/efg/show/lkikf?name=Jack,其中第一个请求应该将用户重定向到页面 abcd/efg(因为那是一个名称),第二个请求应该将用户重定向到页面 lkikf 以及值参数名称。

我有以下控制器来处理它,但问题是当我在地址中有 / 时控制器无法处理它。

@RequestMapping(value = "/{mystring:.*}", method = RequestMethod.GET)
public String handleReqShow(
            @PathVariable String mystring,
            @RequestParam(required = false) String name,
            @RequestParam(required = false) String family, Model model)     {

我使用了以下不起作用的正则表达式。

 /^[ A-Za-z0-9_@./#&+-]*$/

【问题讨论】:

  • 你的春季版本是什么? Jira SPR-11101 是关于影响包含在 3.2.8 和 4.0.2 中修复的正斜杠的 URL 的问题 - 不确定是否相关...

标签: java regex spring spring-mvc


【解决方案1】:

我做的另一种方法是:

@RequestMapping(value = "test_handler/**", method = RequestMethod.GET)

...您的测试处理程序可以是“/test_hanlder/a/b/c”,您将使用以下机制获得整个值。

requestedUri = (String) 
request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);

【讨论】:

  • 我最喜欢这个解决方案,因为它不需要您知道 URL 中的斜杠数量。
  • request的参数类型是什么?
  • javax.servlet.http.HttpServletRequest
【解决方案2】:

您必须创建两种方法,一种具有@RequestMapping(value = { "/{string:.+}" }) 注释,另一种具有@RequestMapping(value = { "/{string:.+}", "/{string:.+}/{mystring:.+}" }),然后在每个方法中采取相应措施,因为您不能有可选的path variables.

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
@RequestMapping("/show")
public class HelloController {

    @RequestMapping(value = { "/{string:.+}" })
    public String handleReqShow(@PathVariable String string,
            @RequestParam(required = false) String name,
            @RequestParam(required = false) String family, Model model) {
        System.out.println(string);
        model.addAttribute("message", "I am called!");
        return "hello";
    }

    @RequestMapping(value = { "/{string:.+}", "/{string:.+}/{mystring:.+}" })
    public String whatever(@PathVariable String string,
            @PathVariable String mystring,
            @RequestParam(required = false) String name,
            @RequestParam(required = false) String family, Model model) {
        System.out.println(string);
        System.out.println(mystring);
        model.addAttribute("message", "I am called!");
        return "hello";
    }
}

【讨论】:

  • 那么如何捕捉mystring的值呢?
  • 它不起作用。似乎是因为您在 requestMapping 的开头有 /{string:.+} 请求将被捕获,因此不会到达第二个请求处理程序。
  • 这个 www.example.com/show/abcd/efg?name=alex&family=moore 你的解决方案丢弃 abcd/ 部分
  • 奇怪,是的,我注意到了,你注意到 /abcd/efg?name=alex&family=moore 就在 show/ 之后吗?我的意思是它应该将 /abcd/efg 放在一个变量中并捕获姓名和家庭的值。
  • @Anand 问题是它丢弃了传递的值,因为它使用“/”作为分隔符而不是提供的值的一部分。例如,如果您传递“abcd/efg”,则不会将其视为单个值;因此,很难知道第二个变量中的值是第一部分值的一部分还是一个动作的名称。例如: /show/abcd/efg 和 /show/jack/edit 第一个请求应该将用户发送到与“abcd/efg”相关的页面(因为那是一个名称),第二个请求应该将用户重定向到编辑页面“jack”(因为 edit 是一个动作的名称)
【解决方案3】:

第一个不起作用,因为您正在尝试处理一个全新的 URL,该 URL 实际上并未映射您的控制器。

www.example.com/show/abcd/efg?name=alex&family=moore   (does not work)

上述 URL 的正确映射可能类似于以下代码。

@RequestMapping(value = {"/{mystring:.*}" , "/{mystring:.*}/{mystring2:.*}"}, method = RequestMethod.GET)
public String handleReqShow(
        @PathVariable String mystring,
        @PathVariable String mystring2,
        @RequestParam(required = false) String name,
        @RequestParam(required = false) String family, Model model)     {

当我的一个控制器用于处理多种类型的请求时,我尝试过类似的概念。

【讨论】:

  • @Jack 是的,它可以处理.. 但是你最好运行,因为我从未尝试过使用 @PathVariable,我用硬编码的 URL 而不是这个。
  • 它不起作用。当我使用 system.out 打印 mystring 值时,我通过了 abcs-fdfrfr-(dfdf)-dfgdf/f-sdff223 bu 它打印的 f-sdff223 我认为它已被第一个选项 (/{mystring:.*}
【解决方案4】:

您可以使用 %2f:http://www.example.com/show/abcd%2fefg?name=alex&family=moore 在 UI 上对斜线进行编码。 现在你应该配置 Spring 来处理斜线。简单配置示例:

@RestController
public class TestController {

    @GetMapping("{testId:.+}")
    public String test(@PathVariable String testId) {
        return testId;
    }


    @GetMapping("{testId:.+}/test/{messageId}")
    public String test2(@PathVariable String testId, @PathVariable String messageId) {
        return testId + " " + messageId;
    }

    //Only if using Spring Security
    @Configuration
    public static class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
        @Bean
        public HttpFirewall allowUrlEncodedSlashHttpFirewall() {
            DefaultHttpFirewall firewall = new DefaultHttpFirewall();
            firewall.setAllowUrlEncodedSlash(true);
            return firewall;
        }
        @Override
        public void configure(WebSecurity web) throws Exception {
            web.httpFirewall(allowUrlEncodedSlashHttpFirewall());
        }
    }


    @Configuration
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public static class SpringMvcConfig extends WebMvcConfigurerAdapter {
        @Override
        public void configurePathMatch(PathMatchConfigurer configurer) {
            UrlPathHelper urlPathHelper = new UrlPathHelper();
            urlPathHelper.setUrlDecode(false);
            configurer.setUrlPathHelper(urlPathHelper);
        }
    }

}

【讨论】:

  • 如果在 Tomcat 上,您的 catalina.properties 文件中可能还需要 org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true
  • 不使用 Spring Security 能做什么?
【解决方案5】:

默认的 Spring MVC 路径映射器使用/ 作为路径变量的分隔符,无论如何。

处理此请求的正确方法是编写一个自定义路径映射器,这将更改特定处理程序方法的此逻辑并将其他处理程序方法委托为默认值。

但是,如果您知道值中可能的最大斜杠数,您实际上可以编写一个接受可选路径变量的处理程序,而不是在方法本身中,从路径变量部分组装值,这是一个示例最多可以使用一个斜线,您可以轻松地将其扩展到三个或四个

@RequestMapping(value = {"/{part1}", "/{part1}/{part2}"}, method = RequestMethod.GET)
public String handleReqShow(
        @PathVariable Map<String, String> pathVariables,
        @RequestParam(required = false) String name,
        @RequestParam(required = false) String family, Model model) {
    String yourValue = "";
    if (pathVariables.containsKey("part1")) {
        String part = pathVariables.get("part1");
        yourValue += " " + part;
    }
    if (pathVariables.containsKey("part2")) {
        String part = pathVariables.get("part2");
        yourValue += " /" + part;
    }
    // do your stuff

}

您可以捕获地图内的所有路径变量,地图@PathVariable Map&lt;String, String&gt; pathVariables,但缺点是映射的静态部分必须枚举所有可能的变化

【讨论】:

  • 您能否详细说明“编写自定义路径映射器,这将改变特定处理程序方法的逻辑”。 1)我需要为我的自定义路径映射器实现什么接口? 2)我如何注释方法来告诉它使用我的自定义路径映射器?
【解决方案6】:

您可以定义规则来避免这种情况

<filter>
    <filter-name>UrlRewriteFilter</filter-name>
    <filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>UrlRewriteFilter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
</filter-mapping>

rules.xml 将此添加到您的 WEB-INF

<urlrewrite>
    <rule>
       <from>^/(10\..*)$</from> <!-- tweak this rule to meet your needs -->
       <to>/Show?temp=$1</to>
    </rule>
</urlrewrite>

【讨论】:

    【解决方案7】:

    尝试转义正斜杠。 正则表达式:/^[ A-Za-z0-9_@.\/#&amp;+-]*$/

    【讨论】:

    • 这确实必须是最简单的方法,然后在服务器上取消正则表达式。如果您有未知数量的斜线,上述答案确实无法应对。由于您无法在其余规范中指定未知数量,因此这确实是唯一的出路。
    猜你喜欢
    • 2017-10-16
    • 2012-12-19
    • 1970-01-01
    • 2012-06-28
    • 2015-07-01
    • 1970-01-01
    • 2018-01-10
    • 2018-02-10
    • 2012-01-17
    相关资源
    最近更新 更多