Spring Boot Map所有请求,而不是Sping Boot 中的特定模式

col17t5w  于 2023-03-18  发布在  Spring
关注(0)|答案(5)|浏览(160)

我有VueJS单页应用程序内的Spring Boot 应用程序,我想使vue路由器处理所有的请求,而不是这些URL以/rest/**开头的请求。
因此我编写了正则表达式^(?!/rest/).*来匹配所有不以/rest/开头的内容,并尝试将请求Map为:

@Controller
public class MainController {

@RequestMapping(value = "/{path:^(?!/rest/).*}")
    public String forwardRequests() {
        return "forward:/";
    }
}

但没用我哪里做错了

编辑

我有剩余的控制器文件:

@RestController
@RequestMapping(path = "/rest/project")
public class ProjectController {

    @Autowired
    private ProjectService projectService;

    @GetMapping(value = "/{id}")
    public Project getProjectById(@PathVariable Long id) {
        return projectService.getProjectById(id);
    }
}

它返回包含项目详细信息的JSON。我有一些像/login/projekty这样的页面,所以我需要将它们转发到index.html,以便处理vue的路由。我知道我可以做这样的事情:

@Controller
public class MainController {

    @RequestMapping(value = {"/login", "/projekty"}, method = RequestMethod.GET)
    public String forwardRequests() {
        return "forward:/";
    }
}

它工作的很好,但是我不想显式的指定每个url,这就是为什么我尝试使用正则表达式,我想我用错了path变量,但是我不知道怎么用。
https://spring.io/guides/tutorials/spring-security-and-angular-js/截面Using "Natural" Routes

a7qyws3x

a7qyws3x1#

正如@JDB所说,AntPathMatcher会比较路径并找到最佳匹配,因此您不必担心端点,因为端点已经指定了路径,就像我的/rest API一样。
您可以添加以下内容:

@RequestMapping(value = "/{path:[^\\.]*}")
public String redirect() {
  return "forward:/";
}

并且您的所有请求(例如/login)将被正确地转发到index.html,在index.html中JavaScript可以处理它。
问题出在像/project/edit/33这样的URL上,它们与这个正则表达式不匹配,所以你会看到Whitelabel Error Page 404 Not Found

@RequestMapping(value = "/**/{path:[^\\.]*}")
public String redirect() {
  return "forward:/";
}

它工作正常,但如果启用了安全性,/ouath/token将返回:

{  
   "timestamp":"2018-02-05T09:13:28.104+0000",
   "status":405,
   "error":"Method Not Allowed",
   "exception":"org.springframework.web.HttpRequestMethodNotSupportedException",
   "message":"Request method 'POST' not supported",
   "path":"/oauth/token"
}

所以你必须排除oauth网址:

@RequestMapping(value = {"/{path:[^\\.]*}", "/**/{path:^(?!oauth).*}/{path:[^\\.]*}"}, method = RequestMethod.GET)
public String forward() {
    return "forward:/";
}

而且效果很好。
如果框架提供的其他端点(如/health)有问题,可以将正则表达式更改为/**/{path:^(?!oauth|health).*}/{path:[^\\.]*}
它看起来很糟糕,但它很有效。我相信有人会在这里发布一个更好更干净的解决方案。

p1tboqfb

p1tboqfb2#

我并不特别了解Spring,所以我只是根据使用其他框架的经验做出最好的猜测。
首先,既然已经在路径中包含了正斜杠,那么模式是否不应该排除它呢?

/{path:^(?!rest/).*}

如果这不起作用,那么我所能想到的就是AntPathMatcher不支持lookaheads。
这种设计的典型模式是同时实现/rest/*/*路由。在某些框架中,这仅仅是为了正确地排序它们。根据Spring MVC的文档,您可能需要尝试一些规则来使/rest/*路由“更具体”。
规则如下:
当多个模式匹配一个URL时,必须通过AntPathMatcher.getPatternComparator(字符串路径)比较它们以找到最佳匹配。
如果一个模式的URI变量和单个通配符计数较低,则该模式的特定性较低,单个通配符计数为1,双个通配符计数为2。如果得分相等,则选择较长的模式。如果得分和长度相同,则选择URI变量比通配符多的模式。
默认Map模式/不包含在评分中,始终排在最后。此外,前缀模式(如/public/)被认为不如其他没有双通配符的模式具体。
有关完整的细节,请参见AntPathMatcher中的AntPatternComparator。另外,请记住,所使用的PathMatcher实现可以自定义。
因此,根据我对这些规则和您的代码的解释,我认为沿着几种方法可以起作用:

// Your more specific patterns will take precedence
@RequestMapping(value = "/rest/**")
    public String handleRestRequests() {
        // forward this to your rest services
    }
}

// Your less specific patterns will only apply if the more specific ones don't match
@RequestMapping(value = "/**")
    public String forwardRequests() {
        // Everything else
        return "forward:/";
    }
}
l3zydbqr

l3zydbqr3#

可以添加缺省处理程序方法

@RequestMapping()
  String defaultHandler() { 
    return "This is a default method";
  }
4c8rllxm

4c8rllxm4#

再看一遍...字符串是/{path:^(?!/rest/).*}
存在第一个/,然后正则表达式也声明第一个/。此正则表达式将匹配/rest/*,因为它验证/(?!/rest/).*而不是(?!/rest/).*

旧答案错误,请参见@加多宝评论

/字符是正则表达式中的特殊字符,需要进行转义。

@Controller
public class MainController {

    @RequestMapping(value = "/{path:^(?!\\/rest\\/).*}")
    public String forwardRequests() {
        return "forward:/";
    }
}

由于\在Java中也是字符串的转义字符,因此需要两个转义字符。

r3i60tvu

r3i60tvu5#

对于我们想要排除的情况,尝试请求路径匹配通常是很麻烦的,我们可以使用ResourceResolver处理WebHistory重定向的特定情况。
我的案例是:

/admin/index.html => backoffice vuejs entry-point
/admin/css/**     => backoffice css files
/admin/js/**      => backoffice js files
/admin/**         => backoffice routes
/**               => API endpoints

后台静态文件由SpringBoot作为/admin下的静态资源提供。我需要将/admin/**资源(不是静态资源,而是webapp路由)作为index.html提供。对于该用例,我实现了一个简单的ResourceResolver

public class WebHistoryPathResourceResolver implements ResourceResolver {

    private final PathResourceResolver pathResourceResolver = new PathResourceResolver();
    private final String fallbackPath;

    public WebHistoryPathResourceResolver(String fallbackPath) {
        this.fallbackPath = fallbackPath;
    }

    @Override
    public Resource resolveResource(HttpServletRequest request, String requestPath, List<? extends Resource> locations, ResourceResolverChain chain) {
        Resource resource = pathResourceResolver.resolveResource(request, requestPath, locations, chain);
        if (resource == null) {
            return pathResourceResolver.resolveResource(request, fallbackPath, locations, chain);
        }

        return resource;
    }

    @Override
    public String resolveUrlPath(String resourcePath, List<? extends Resource> locations, ResourceResolverChain chain
    ) {
        return pathResourceResolver.resolveUrlPath(resourcePath, locations, chain);
    }
}

然后示例化处理程序,如下所示:

@Bean
public WebMvcConfigurer adminWebMvcConfigurer() {
    final String prefix = "/admin";

    return new WebMvcConfigurer() {
        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            registry
                .addResourceHandler(prefix + "/**")
                .addResourceLocations("classpath:" + prefix + "/")
                .resourceChain(true)
                .addResolver(new WebHistoryPathResourceResolver("index.html"));
        }

        @Override
        public void addViewControllers(ViewControllerRegistry registry) {
            registry.addViewController(prefix)
                .setViewName(prefix + "/index.html");
        }
    };
}

相关问题