当前位置: 首页 > 知识库问答 >
问题:

SpringMVC:依赖于url扩展的不一致映射行为

吴哲
2023-03-14
@RestController
@RequestMapping(ThemeEndpoint.ENDPOINT_NAME)
public class ThemeEndpoint {

public static final String ENDPOINT_NAME = "/themes"; 

    @RequestMapping(value="/{id}/css/{assetName:.*}", method=RequestMethod.GET)
    public Asset getCssItem(
        @PathVariable("id") ThemeId id, 
        @PathVariable("assetName") String name) {
    CssThemeAsset themeAsset = themeService.getCssAsset(
                id, ThemeAssetId.fromString(name));
    Asset asset = new Asset();
    asset.name = themeAsset.getName();
    asset.text = themeAsset.getContent();
    return asset;
}
http://localhost:8080/app-url/rest/themes/ac18a080-a2f1-11e3-84f4-600308a0bd14/css/main.less

但当我将扩展名更改为.css时就会失败。

经过一些调试后,我确信如果使用类似的url的话,请求甚至不会被映射

http://localhost:8080/app-url/rest/themes/ac18a080-a2f1-11e3-84f4-600308a0bd14/css/main.css

对于高日志级别,我可以看到映射被Spring捕获:

org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping 
    - Mapped "{[/themes/{id}/css/{assetName:.*}],methods=[GET],params=[],headers=[],
                consumes=[],produces=[application/json],custom=[]}" 
      onto public xxx.endpoint.ThemeEndpoint$Asset
          xxx.endpoint.ThemeEndpoint.getCssItem(
              net.lacho.svc.themes.api.ThemeId,java.lang.String)   
Found 1 matching mapping(s) for [/themes/ac18a080-a2f1-11e3-84f4-600308a0bd14/css/main.less]
  : [{[/themes/{id}/css/{assetName:.*}],methods=[GET],params=[],headers=[],
        consumes=[],produces=[application/json],custom=[]}]
Looking up handler method for path /themes/ac18a080-a2f1-11e3-84f4-600308a0bd14/css/test.css
org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver - 
    Resolving exception from handler [null]:  
org.springframework.web.HttpMediaTypeNotAcceptableException: 
    Could not find acceptable representation
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
        http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    version="3.0"
    metadata-complete="false">

<welcome-file-list>
    <welcome-file>index</welcome-file>
</welcome-file-list>

</web-app>
package net.lacho.opcenter.bootstrap;


public class WebApplicationBootstrapper implements WebApplicationInitializer {


    @Override
    public void onStartup(ServletContext container) throws ServletException {

        AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
        rootContext.setConfigLocation(ApplicationConfig.class.getName());

        container.addListener(new ContextLoaderListener(rootContext));

        registerRestDispatcher(container);
        registerDefaultDispatcher(container);

        container.addFilter("CharacterEncodingFilter", UTF8EncodingFilter.class).addMappingForUrlPatterns(null,  true,  "/*");
        container.addFilter("headSupportFilter", HeadSupportFilter.class).addMappingForUrlPatterns(null,  true,  "/*");

        DelegatingFilterProxy shallowFrontendContextFilterProxy = new DelegatingFilterProxy("shallowFrontendContextProviderLocalFilter");
        shallowFrontendContextFilterProxy.setTargetFilterLifecycle(true);
        FilterRegistration.Dynamic shallowFrontendFilter = container.addFilter("ShallowFrontendContextFilter", shallowFrontendContextFilterProxy);
        shallowFrontendFilter.setInitParameter("ignoreNullClient", "true");
        shallowFrontendFilter.addMappingForUrlPatterns(null,  true,  "/*");

        container.addFilter("springSecurityFilterChain", new DelegatingFilterProxy("springSecurityFilterChain"))
            .addMappingForUrlPatterns(null,  true,  "/*");

        container.addFilter("FrontendContextFilter", new DelegatingFilterProxy("frontendContextProviderLocalFilter"))
            .addMappingForUrlPatterns(null,  true,  "/*");

        container.addFilter("hiddenHttpMethodFilter", new HiddenHttpMethodFilter()).addMappingForUrlPatterns(null,  true,  "/rest/*");;
    }

    public void registerRestDispatcher(ServletContext container) {
        AnnotationConfigWebApplicationContext restDispatcherContext = new AnnotationConfigWebApplicationContext();
        restDispatcherContext.register(RestCommonsMvcConfig.class);

        ServletRegistration.Dynamic restDispatcher = container.addServlet("rest-dispatcher", new DispatcherServlet(restDispatcherContext));
        restDispatcher.setLoadOnStartup(1);
        restDispatcher.addMapping("/rest/*");
    }

    public void registerDefaultDispatcher(ServletContext container) {
        AnnotationConfigWebApplicationContext dispatcherContext = new AnnotationConfigWebApplicationContext();
        dispatcherContext.register(MvcConfig.class);

        ServletRegistration.Dynamic dispatcher = container.addServlet("backend-dispatcher", new DispatcherServlet(dispatcherContext));
        dispatcher.setLoadOnStartup(1);
        dispatcher.addMapping("/*", "/index");
    }

}

MVC-Config:

package net.lacho.opcenter.bootstrap;



@Configuration
@EnableWebMvc
@ComponentScan(basePackages = {"net.lacho.opcenter.ui"} )
public class MvcConfig extends WebMvcConfigurerAdapter {

    ... many lines removed, containing interceptors and velocity-config ...

@Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry
            .addResourceHandler("/_r/_s/**")
            .addResourceLocations("classpath:/static-resources/")
            .setCachePeriod(365 * 86400);
        registry
            .addResourceHandler("/_r/_d/**")
            .addResourceLocations("classpath:/static-uncached-resources/");
    }

    @Override
    public void configureDefaultServletHandling(
            DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

有人知道吗?

共有1个答案

薄腾
2023-03-14

不是虫子,是功能...

正如@axtavt和@rhinds所设想的,内容类型中有什么东西在乱搞。浏览器发送正确的accept:application/json,但spring忽略了这一点,而是使用url的扩展名(aarrgh)。从文档中:

16.16.4配置内容协商

You can configure how Spring MVC determines the requested media types from the client for
request mapping as well as for content negotiation purposes. The available options are to 
check the file extension in the request URI, the "Accept" header, a request parameter, as 
well as to fall back on a default content type. By default, file extension in the request 
URI is checked first and the "Accept" header is checked next.
@Configuration
@EnableWebMvc
public class RestCommonsMvcConfig extends WebMvcConfigurerAdapter {

    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer.favorPathExtension(false);
    }

}
    null
 类似资料:
  • http://some-amazon-link:8080/the-war-name/login 现在我正在纠结如何在Spring MVC项目中添加“/the-war-name/”以及在哪里添加“/the-war-name/”。 它在 null

  • Hi这似乎适用于添加额外的方法,但不适用于在现有方法上添加新的注释。假设我们有以下课程:

  • 问题内容: 大多数教程都建议使用默认的JSF配置,类似于以下web.xml: 使用此配置,仅当对应的URL以文件扩展名.jsf结尾(例如,http://localhost/welcome.jsf)时,Faces Servlet才能找到我的Web应用程序中的相应 .xhtml文件。是否可以配置web.xml,以便使用相同的 .xhtml文件将不以.jsf结尾的URL也处理为JSF页面? 换句话说,我

  • 因此,当我将对象转换为JSON字符串时: dob以以下方式写入JSON字符串: “dob”:{“年份”:1964,“月份”:“二月”,“纪年”:{“日历类型”:“ISO8601”,“ID”:“ISO”},“月份值”:2,“DayofMonth”:13,“DayofWeek”:“星期四”,“Era”:“CE”,“DayofYear”:44,“LeapYear”:true 所以,我不知道我是如何做到这

  • 这类似于JPA映射带有继承的视图和表,但由于接受的答案不能让我满意,所以我决定问自己的问题。 我有一个基于类,它包含所有实体的公共字段 我想像现在一样读/写,但我不知道如何映射,以便JPA查询可以使用它。我用做了一些努力,但没有成功。如果我将中的所有字段复制到中,那么我的应用程序将按预期运行,但这并不适合我。如何映射视图以便使用继承?

  • 我试图构建一个不和谐的应用程序,但由于某些原因,它无法访问JDA。错误如下: 编辑:这是我的POM: