【问题标题】:Making Spring/Tomcat compatible with HTML5 pushState使 Spring/Tomcat 与 HTML5 pushState 兼容
【发布时间】:2016-12-07 05:38:55
【问题描述】:

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

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

【问题讨论】:

    标签: java html spring tomcat backbone.js


    【解决方案1】:

    我发现 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

    【讨论】:

      【解决方案2】:

      理论上,要通过 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 已经根据“produces”标头执行此操作)。

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

      【讨论】:

        【解决方案3】:

        我会为我的网址提供一个清晰的方案,并将前端与后端分开。

        一些建议:

        • 将所有以/server 开头的请求路由到后端,将所有其他请求路由到前端。
        • 设置两个不同的域,一个用于后端,一个用于前端。

        【讨论】:

          猜你喜欢
          • 2015-11-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-06-06
          • 1970-01-01
          • 2019-06-05
          • 1970-01-01
          • 2012-08-18
          相关资源
          最近更新 更多