Springboot / Angular2 - 如何处理HTML5网址?

时间:2016-07-22 01:05:23

标签: spring angular spring-boot

我认为这是一个简单的问题,但我无法找到答案或至少在搜索中使用正确的字词。

我正在设置Angular2Springboot。默认情况下,Angular会使用localhost:8080\dashboardlocalhost:8080\dashboard\detail等路径。

如果可能的话,我想避免将路径用作哈希值。正如Angular documentation所述:

  

路由器的provideRouter功能将LocationStrategy设置为PathLocationStrategy,使其成为默认策略。如果我们愿意的话,我们可以在引导过程中使用覆盖切换到HashLocationStrategy。

然后......

  

几乎所有Angular 2项目都应该使用默认的HTML 5样式。它生成的URL更易于用户理解。它保留了稍后进行服务器端渲染的选项。

问题在于,当我尝试访问localhost:8080\dashboard时,Spring会寻找一些控制器映射到这条路径,但它不会有。

Whitelabel Error Page
There was an unexpected error (type=Not Found, status=404).
No message available

我最初认为我的所有服务都在localhost:8080\api以及localhost:8080\app下的所有静态服务。但是,如何告诉Spring忽略对此app路径的请求?

Angular2或Boot是否有更好的解决方案?

8 个答案:

答案 0 :(得分:42)

我有一个解决方案,您可以添加ViewController以从Spring启动向Angular转发请求。

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class ViewController {

@RequestMapping({ "/bikes", "/milages", "/gallery", "/tracks", "/tracks/{id:\\w+}", "/location", "/about", "/tests","/tests/new","/tests/**","/questions","/answers" })
   public String index() {
       return "forward:/index.html";
   }
}

在这里,我重定向了所有的angular2(" / bikes"," / milages"," / gallery"," / tracks" ," / tracks / {id:\ w +}"," / location"," / about"," / tests",& #34; / tests / new"," / tests / **"," / questions"," / answers")到我的SPA 您可以对preject做同样的事情,也可以将404错误页面重定向到索引页面作为进一步的步骤。 享受!

答案 1 :(得分:25)

在我的Spring Boot应用程序(版本1和版本2)中,我的静态资源位于一个地方:

src/main/resources/static

static是Spring Boot识别的文件夹,用于加载静态资源。

然后想法是定制Spring MVC配置。
更简单的方法是使用Spring Java配置。

我实施WebMvcConfigurer来覆盖addResourceHandlers()。 我将 ResourceHandler添加到当前ResourceHandlerRegistry
处理程序映射到每个请求,我指定classpath:/static/作为资源位置值(如果需要,您当然可以添加其他值)。
我添加了一个自定义PathResourceResolver匿名类来覆盖getResource(String resourcePath, Resource location) 返回资源的规则如下:如果资源存在且可读(因此它是文件),我将返回它。否则,默认情况下我会返回index.html页面。处理HTML 5网址的预期行为是什么。

Spring Boot 1.X应用程序:

扩展org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter方式。
该类是WebMvcConfigurer接口的适配器 使用空方法,允许子类只覆盖他们感兴趣的方法。

以下是完整代码:

import java.io.IOException;

import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.resource.PathResourceResolver;

@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {


    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {

    registry.addResourceHandler("/**/*")
        .addResourceLocations("classpath:/static/")
        .resourceChain(true)
        .addResolver(new PathResourceResolver() {
            @Override
            protected Resource getResource(String resourcePath,
                Resource location) throws IOException {
                  Resource requestedResource = location.createRelative(resourcePath);
                  return requestedResource.exists() && requestedResource.isReadable() ? requestedResource
                : new ClassPathResource("/static/index.html");
            }
        });
    }
}

Spring Boot 2.X应用程序:

org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter已被弃用 直接实现WebMvcConfigurer是现在的方式,因为它仍然是一个接口,但它现在有默认方法(由Java 8基线实现),并且可以直接实现而无需适配器。

以下是完整代码:

import java.io.IOException;

import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.resource.PathResourceResolver;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {

      registry.addResourceHandler("/**/*")
        .addResourceLocations("classpath:/static/")
        .resourceChain(true)
        .addResolver(new PathResourceResolver() {
            @Override
            protected Resource getResource(String resourcePath,
                Resource location) throws IOException {
                Resource requestedResource = location.createRelative(resourcePath);
                return requestedResource.exists() && requestedResource.isReadable() ? requestedResource
                : new ClassPathResource("/static/index.html");
            }
        });
    }
}

答案 2 :(得分:10)

您可以通过提供自定义ErrorViewResolver将所有未找到的资源转发到您的主页面。您需要做的就是将其添加到@Configuration类:

@Bean
ErrorViewResolver supportPathBasedLocationStrategyWithoutHashes() {
    return new ErrorViewResolver() {
        @Override
        public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
            return status == HttpStatus.NOT_FOUND
                    ? new ModelAndView("index.html", Collections.<String, Object>emptyMap(), HttpStatus.OK)
                    : null;
        }
    };
}

答案 3 :(得分:6)

您可以使用以下内容转发未映射到Angular的所有内容:

@Controller
public class ForwardController {

    @RequestMapping(value = "/**/{[path:[^\\.]*}")
    public String redirect() {
        // Forward to home page so that route is preserved.
        return "forward:/";
    }
} 

来源:https://stackoverflow.com/a/44850886/3854385

我的用于angular的Spring Boot服务器也是一个网关服务器,对/api的API调用没有在角度页面前面的登录页面,你可以使用类似的东西。

import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;

/**
 * This sets up basic authentication for the microservice, it is here to prevent
 * massive screwups, many applications will require more secuity, some will require less
 */

@EnableOAuth2Sso
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                .logout().logoutSuccessUrl("/").and()
                .authorizeRequests()
                .antMatchers("/api/**").authenticated()
                .anyRequest().permitAll().and()
                .csrf()
                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
    }
}

答案 4 :(得分:1)

为了使它更简单,你可以直接实现ErrorPageRegistrar ..

@Component
public class ErrorPageConfig implements ErrorPageRegistrar {

    @Override
    public void registerErrorPages(ErrorPageRegistry registry) {
        registry.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/"));
    }

}

这会将请求转发给index.html。

@Controller
@RequestMapping("/")
public class MainPageController {

    @ResponseStatus(HttpStatus.OK)
    @RequestMapping({ "/" })
    public String forward() {
        return "forward:/";
    }
}

答案 5 :(得分:0)

以下是您需要遵循的三个步骤:

  1. 实现自己的TomcatEmbeddedServletContainerFactory bean并设置RewriteValve

      import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;  
      ...
      import org.apache.catalina.valves.rewrite.RewriteValve; 
      ... 
    
      @Bean TomcatEmbeddedServletContainerFactory servletContainerFactory() {
        TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
        factory.setPort(8080);
        factory.addContextValves(new RewriteValve());
        return factory;
      }
    
  2. 将rewrite.conf文件添加到应用程序的WEB-INF目录中,并指定重写规则。这是一个示例rewrite.conf内容,我在angular应用程序中使用它来利用angular的PathLocationStrategy(基本上我只是将所有内容重定向到index.html,因为我们只使用spring boot来提供静态Web内容,否则你需要在RewriteCond规则中过滤你的控制器:

      RewriteCond %{REQUEST_URI} !^.*\.(bmp|css|gif|htc|html?|ico|jpe?g|js|pdf|png|swf|txt|xml|svg|eot|woff|woff2|ttf|map)$
      RewriteRule ^(.*)$ /index.html [L]
    
  3. 从路由声明中删除useHash(或将其设置为false):

      RouterModule.forRoot(routes)
    
  4.       RouterModule.forRoot(routes, {useHash: false})
    

答案 6 :(得分:0)

使用index.html转发所有Angular路由。包括基础href。

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class ViewController {

@RequestMapping({ "jsa/customer","jsa/customer/{id}",})
   public String index() {
       return "forward:/index.html";
   }
}

在我的情况下,jsa是base href。

答案 7 :(得分:0)

我是用普通的旧filter来做的:

public class PathLocationStrategyFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {

        if(request instanceof HttpServletRequest) {
            HttpServletRequest servletRequest = (HttpServletRequest) request;

            String uri = servletRequest.getRequestURI();
            String contextPath = servletRequest.getContextPath();
            if(!uri.startsWith(contextPath + "/api") && 
                !uri.startsWith(contextPath + "/assets") &&
                !uri.equals(contextPath) &&
                // only forward if there's no file extension (exclude *.js, *.css etc)
                uri.matches("^([^.]+)$")) {

                RequestDispatcher dispatcher = request.getRequestDispatcher("/");
                dispatcher.forward(request, response);
                return;
            }
        }        

        chain.doFilter(request, response);
    }
}

然后在web.xml中输入

<web-app>
    <filter>
        <filter-name>PathLocationStrategyFilter</filter-name>
        <filter-class>mypackage.PathLocationStrategyFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>PathLocationStrategyFilter</filter-name>
        <url-pattern>*</url-pattern>
    </filter-mapping>
</web-app>