尽管使用 @RequestMapping("**") 在 Spring Boot 中提供静态内容

Posted

技术标签:

【中文标题】尽管使用 @RequestMapping("**") 在 Spring Boot 中提供静态内容【英文标题】:Serve static content in Spring Boot despite using @RequestMapping("**") 【发布时间】:2017-10-24 06:13:18 【问题描述】:

上下文

我目前正在从事一个教育项目。这意味着两个 Spring Boot REST 服务器。一个是实际的服务器,它会进行一些处理。

我感兴趣的是另一个。它是一个代理,它将所有呼叫重定向到第一个呼叫。这样当我调用http://localhost:8080/foo 时,我的代理服务器将依次调用http://localhost:8090/foo。而如果第一台服务器返回A,那么代理将返回"proxied": A, "someInformationAboutThisCall": B

我设法通过一些可能不优雅但功能正常的代码达到了这一点,我在下面给出了其中的摘录。这里的关键是我使用@RequestMapping("**") 来实现这一点。下一步是设计一个界面,让我的附加信息立即清晰易读,这基本上是这个项目的重点。如果我删除所有@RequestMapping("**"),它就可以正常工作。

问题

现在我的问题如下:使用了@RequestMapping("**"),我无法提供静态内容(调用被重定向到另一个不提供静态内容的 REST 服务器)。 我如何配置 Spring Boot/Spring MVC 以在映射请求时忽略作为静态内容可用的资源,或者使 PathResourceResolver 优先于我的控制器?` 或者我应该从另一个提供我的静态内容JVM/服务器?

提前感谢您的帮助!

感兴趣的编辑:在进行一些测试时,我发现如果我使用@RequestMapping("*"),静态内容提供的,但有一些限制。

/index.html 生成一个错误页面(更一般地,直接在 public 中的任何静态内容也是如此) /itf/index.html 有效(更普遍的是,public/itfpublic 的任何其他子目录中的任何文件) /itf not 工作:Spring Boot 似乎不知道其中的索引文件。我必须指定一个完整的 URI,直到我要显示的特定文件。

然而,这对我需要的 @RequestMapping("**") 根本不起作用。

暂定

我尝试使用WebMvcConfigurerAdapterHandlerInterceptorAdapter(在SO、SO again 和 Internet 上的许多其他地方找到),但无法再启动我的项目,因为 Spring boot 然后找不到 @ 987654341@bean(Spring Boot最近有变化吗?我用的是1.5.3.RELEASE版本)。

我也尝试了一些反匹配,但不仅不起作用,而且感觉非常非常脏(而且整个项目可能不是最优的,所以说了很多)。

好奇的代码示例

我的“代理”控制器

注意:您可以提出更好的方法来实现这一点在 cmets 中。请记住,尽管我总是对改进建议持开放态度,但这不是我的问题。

@RestController
public class ProxyController 

    @Value("$monitored.url.base") // "http://localhost:8090"
    private String redirectBase;


    @RequestMapping(value = "**", method = RequestMethod.POST, RequestMethod.PUT)
    public ProxiedResponse proxifyRequestsWithBody(HttpServletRequest request, @RequestHeader HttpHeaders headers, @RequestBody Object body) throws URISyntaxException 
        return proxifyRequest(request, headers, body);
    

    @RequestMapping(value = "**")
    public ProxiedResponse proxifyRequestsWithoutBody(HttpServletRequest request, @RequestHeader HttpHeaders headers) throws URISyntaxException 
        return proxifyRequest(request, headers, null);
    

    private ProxiedResponse proxifyRequest(HttpServletRequest request, @RequestHeader HttpHeaders headers, @RequestBody Object body) throws URISyntaxException 
        final RequestEntity<Object> requestEntity = convertToRequestEntity(request, headers, body);

        // call remote service
        final ResponseEntity<Object> proxied = restTemplate.exchange(requestEntity, Object.class);

        // Return service result + monitoring information
        final ProxiedResponse response = new ProxiedResponse();
        response.setProxied(proxied.getBody());
        // set additional information

        return response;
    

    // Won't work properly for POST yet
    private <T> RequestEntity<T> convertToRequestEntity(HttpServletRequest request, HttpHeaders headers, T body) throws URISyntaxException 
        // Build proxied URL
        final StringBuilder redirectUrl = new StringBuilder(redirectBase).append(request.getRequestURI());
        final String queryString = request.getQueryString();
        if (queryString != null) 
            redirectUrl.append("?").append(queryString);
        

        // TODO enhancement: transmit headers and request body to make this a real proxy
        final HttpMethod httpMethod = HttpMethod.valueOf(request.getMethod());
        return new RequestEntity<>(body, headers, httpMethod, new URI(redirectUrl.toString()));
    

我排除静态资源 URL 的肮脏尝试

@Configuration // adding @EnableWebMvc did not solve the problem
public class WebMvcConfiguration extends WebMvcConfigurerAdapter 

    private static class StaticResourcesHandlerInterceptor extends HandlerInterceptorAdapter 
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception 
            final String requestURI = request.getRequestURI();
            if (requestURI == null || "/".equals(requestURI) || "/index.html".equals(requestURI) || requestURI.startsWith("/assets")) 
                return super.preHandle(request, response, null);
            

            return super.preHandle(request, response, handler);
        
    

    @Autowired
    public void addInterceptors(InterceptorRegistry registry) 
        registry.addInterceptor(new StaticResourcesHandlerInterceptor()).addPathPatterns("/**");
    

【问题讨论】:

【参考方案1】:

您可以将路径拆分为一个通配符和一个必须匹配负前瞻正则表达式的命名路径变量。

@RequestMapping("/variable:(?!static).*/**")

然后,您可以使用@PathVariable String variable 作为控制器方法的参数来获取需要传递的变量的值。

(宁愿写评论,但我没有足够的声誉)

【讨论】:

您应该避免发布更适合作为 cmets 的答案,否则您可能会招来反对票或举报。【参考方案2】:

尝试将@EnableWebMvc 注解添加到您的配置中:

@Configuration
@EnableWebMvc
public class WebMvcConfiguration extends WebMvcConfigurerAdapter 

...


【讨论】:

忘了说。我试过了。我也尝试使用 WebMvcConfigurationSupport。

以上是关于尽管使用 @RequestMapping("**") 在 Spring Boot 中提供静态内容的主要内容,如果未能解决你的问题,请参考以下文章

Spring MVC 从jsp页面传值到Controller方法里

springmvc中@RequestMapping的使用

SpringMVC——RequestMapping

学习笔记——@RequestMapping注解位置注解属性;@RequestMapping支持Ant风格的路径

2.@RequestMapping

使用 @pathparam 和 @requestmapping 获取 null