1

我有一个使用 Backbone.js 客户端路由和 pushState 的单页 Web 应用程序。为了让它工作,我必须告诉我的服务器(Java、Spring 3、Tomcat)哪些 URL 应该在服务器上解析(实际的 JSP 视图、API 请求),哪些应该简单地发送到索引页面由客户处理。目前,我正在使用 InternalResourceViewResolver 来简单地提供与 URL 请求名称匹配的 JSP 视图。由于客户端 URL 在服务器上没有视图,因此服务器返回 404。

向 Spring(或 Tomcat)指定一些特定 URL(我的客户端路由)应该全部解析为 index.jsp,而其他任何东西都应该通过 InternalResourceViewResolver 指定的最佳方法是什么?

4

3 回答 3

3

我发现 Spring MVC 3 添加了一个标签,它正是我需要的mvc:view-controller标签。这为我完成了:

<mvc:view-controller path="/" view-name="index" />
<mvc:view-controller path="/admin" view-name="index" />
<mvc:view-controller path="/volume" view-name="index" />

http://static.springsource.org/spring/docs/3.0.x/reference/mvc.html

于 2012-11-12T16:18:56.050 回答
1

理论上,要通过 history.pushState 处理导航,您需要返回 index.html 以获取未处理的资源。如果您查看现代 Web 框架的官方文档,它通常是基于 404 状态来实现的。

在春季,您应该按顺序处理资源:

  • 路径映射的 REST 控制器
  • 应用静态资源
  • 其他人的 index.html

为此,您至少有 4 种可能的解决方案。

使用 EmbeddedServletContainerCustomizer 和自定义 404 处理程序

@Controller
static class SpaController {
    @RequestMapping("resourceNotFound")
    public String handle() {
        return "forward:/index.html";
    }
}

@Bean
public EmbeddedServletContainerCustomizer containerCustomizer() {
    return container -> container.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/resourceNotFound"));
}

使用自定义默认请求映射处理程序

@Autowired
private RequestMappingHandlerMapping requestMappingHandlerMapping;

static class SpaWithHistoryPushStateHandler {

}

static class SpaWithHistoryPushStateHandlerAdapter implements HandlerAdapter {

    @Override
    public boolean supports(final Object handler) {
        return handler instanceof SpaWithHistoryPushStateHandler;
    }

    @Override
    public ModelAndView handle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) throws Exception {
        response.getOutputStream().println("default index.html");
        return null;
    }

    @Override
    public long getLastModified(final HttpServletRequest request, final Object handler) {
        return -1;
    }
}

@Bean
public SpaWithHistoryPushStateHandlerAdapter spaWithHistoryPushStateHandlerAdapter() {
    return new SpaWithHistoryPushStateHandlerAdapter();
}

@PostConstruct
public void setupDefaultHandler() {
    requestMappingHandlerMapping.setDefaultHandler(new SpaWithHistoryPushStateHandler());
}

使用自定义 ResourceResolver

@Autowired
private ResourceProperties resourceProperties;

@Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/**")
            .addResourceLocations(resourceProperties.getStaticLocations())
            .setCachePeriod(resourceProperties.getCachePeriod())
            .resourceChain(resourceProperties.getChain().isCache())
            .addResolver(new PathResourceResolver() {
                @Override
                public Resource resolveResource(final HttpServletRequest request, final String requestPath, final List<? extends Resource> locations, final ResourceResolverChain chain) {
                    final Resource resource = super.resolveResource(request, requestPath, locations, chain);
                    if (resource != null) {
                        return resource;
                    } else {
                        return super.resolveResource(request, "/index.html", locations, chain);
                    }
                }
            });
}

使用自定义 ErrorViewResolver

@Bean
public ErrorViewResolver customErrorViewResolver() {
    final ModelAndView redirectToIndexHtml = new ModelAndView("forward:/index.html", Collections.emptyMap(), HttpStatus.OK);
    return (request, status, model) -> status == HttpStatus.NOT_FOUND ? redirectToIndexHtml : null;
}

概括

第四个选项看起来最简单,但一如既往地取决于您的需要。您可能还希望仅在请求需要 text/html 时才限制返回 index.html(BasicErrorController 已经基于“生成”标头执行此操作)。

我希望其中一个选项对您的情况有所帮助。

于 2016-12-06T21:38:55.853 回答
-1

我会给我的网址一个明确的方案,并将前端与后端分开。

一些建议:

  • 将所有请求路由/server到后端,将所有其他请求路由到前端。
  • 设置两个不同的域,一个用于后端,一个用于前端。
于 2012-11-09T00:17:36.530 回答