Java 带单页角度重定向的弹簧靴2

Java 带单页角度重定向的弹簧靴2,java,spring,angular,spring-mvc,spring-boot,Java,Spring,Angular,Spring Mvc,Spring Boot,我有一个带有Spring Boot的单页Angular应用程序。如下所示: src main java controller HomeController CustomerController OtherController webapp js/angular-files.js index.html Spring boot正确默认为webapp文件夹,并提供index.html文件 我希望做的是: 对于不是以/api

我有一个带有Spring Boot的单页Angular应用程序。如下所示:

src
  main
  java
    controller
       HomeController
       CustomerController
       OtherController
  webapp
    js/angular-files.js
    index.html
Spring boot正确默认为webapp文件夹,并提供index.html文件

我希望做的是:

  • 对于不是以
    /api
    开头的每个本地REST请求,覆盖并重定向到默认的webapp/index.html。我计划为spring控制器提供任何
    /api

  • 有没有一种方法可以给所有控制器加上API前缀,这样我就不必每次都写API了? e、 g

    @RequestMapping(“/api/home”)可以在@RequestMapping(“/home”)代码中编写速记

  • 我正在寻找每个API请求,例如1)使用API保留API并解析为正确的控制器并返回JSON,但是如果有人输入URL,如或,则它将服务于index.html页面并保留URL

    其他方法:尝试添加#ErrorViewResolver:

    对于整个应用程序,您可以在application.properties中添加上下文路径

    server.contextPath=/api

    之后,它将向每个请求的URL追加“/api”

    对于重定向

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addRedirectViewController("/", "/home");
        registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
        super.addViewControllers(registry);
    }
    
    将这组代码放在WebMVCConfig.java中,试试这个

    @SpringBootApplication
    @Controller
    class YourSpringBootApp { 
    
        // Match everything without a suffix (so not a static resource)
        @RequestMapping(value = "/**/{path:[^.]*}")       
        public String redirect() {
            // Forward to home page so that route is preserved.(i.e forward:/intex.html)
            return "forward:/";
        }
    }
    
    对于每个不以/api开头的本地REST请求,覆盖并重定向到默认的webapp/index.html。我计划为spring控制器提供任何东西/api

    2017年5月15日更新

    让我为其他读者重新表述一下你的疑问。(如果误解,请纠正我)

    背景
    使用Spring引导并从类路径提供静态资源

    要求
    所有
    404
    非api请求都应重定向到
    index.html

    非API-表示URL不以
    /API
    开头的请求
    API-404应该像往常一样抛出
    404

    样本响应
    /api/something
    -将抛出
    404

    /index.html
    -将服务器index.html
    /something
    -将重定向到
    index.html

    我的解决方案

    如果给定资源没有可用的处理程序,让SpringMVC抛出异常

    将以下内容添加到
    应用程序属性中

    spring.mvc.throw-exception-if-no-handler-found=true
    spring.resources.add-mappings=false
    
    添加
    ControllerAdvice
    ,如下所示

    @ControllerAdvice
    public class RedirectOnResourceNotFoundException {
    
        @ExceptionHandler(value = NoHandlerFoundException.class)
        public Object handleStaticResourceNotFound(final NoHandlerFoundException ex, HttpServletRequest req, RedirectAttributes redirectAttributes) {
            if (req.getRequestURI().startsWith("/api"))
                return this.getApiResourceNotFoundBody(ex, req);
            else {
                redirectAttributes.addFlashAttribute("errorMessage", "My Custom error message");
                return "redirect:/index.html";
            }
        }
    
        private ResponseEntity<String> getApiResourceNotFoundBody(NoHandlerFoundException ex, HttpServletRequest req) {
            return new ResponseEntity<>("Not Found !!", HttpStatus.NOT_FOUND);
        }
    }
    
    并扩展此
    BaseController
    ,并确保不使用
    @RequestMapping
    注释子类

    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class FirstTestController extends BaseController {
        @RequestMapping(path = "/something")
        public String sayHello() {
            return "Hello World !!";
        }
    
    }
    
    先前的答案

    如果请求路径未通过
    /api
    启动,您可以创建一个
    过滤器,该过滤器将重定向到
    /index.html


    好的,让我们从问题的简单部分开始:

    有没有一种方法可以给所有控制器加上api前缀,这样我就不必每次都写api了

    答案是肯定的,只需使用“全局”
    @RequestMapping
    注释标记控制器,例如:

    @RestController
    @RequestMapping("/api")
    public class ApiController{
    
       @RequestMapping("/hello") 
       public String hello(){
          return "hello simple controller";
       }
    
       @RequestMapping("/hello2") 
       public String hello2(){
          return "hello2 simple controller";
       }
    }
    
    在上面的示例中,您可以使用以下URL调用hello方法:
    /api/hello

    使用此URL的第二种方法:
    /api/hello2

    这就是为什么我不必用
    /api
    前缀标记每个方法。

    现在,到你问题中更复杂的部分:

    是如果请求不是以
    /api
    前缀开头,如何实现重定向?

    您可以通过返回重定向的HTTP状态代码(302)来实现这一点,毕竟angularJs“讲”了REST,因此您不能像以前那样强制从Java/Spring代码进行重定向

    然后返回一条状态代码为302的HTTP消息,在AngularJ上执行实际的重定向

    例如:

    @RestController
    @RequestMapping("/api")
    public class ApiController{
    
       @RequestMapping("/hello") 
       public String hello(){
          return "hello simple controller";
       }
    
       @RequestMapping("/hello2") 
       public String hello2(){
          return "hello2 simple controller";
       }
    }
    
    关于AngularJS:

    var headers = {'Content-Type':'application/json', 'Accept':'application/json'}
    
    var config = {
        method:'GET'
        url:'http://localhost:8080/hello',
        headers:headers
    };
    
    http(config).then(
        function onSuccess(response){
            if(response.status == 302){
                console.log("Redirect");
                $location("/")
            }
    }, function onError(response){
        console.log("An error occured while trying to open a new game room...");
    });
    
    @RestController
    @RequestMapping("/api")
    public class ApiController{
    
       @RequestMapping("/hello") 
       public ResponseEntity<String> hello(){
          HttpHeaders header = new HttpHeaders();
          header.add("Content-Type", "application/json");
          return new ResponseEntity<String>("", header, HttpStatus.FOUND);
       }
    }
    
    在春天:

    var headers = {'Content-Type':'application/json', 'Accept':'application/json'}
    
    var config = {
        method:'GET'
        url:'http://localhost:8080/hello',
        headers:headers
    };
    
    http(config).then(
        function onSuccess(response){
            if(response.status == 302){
                console.log("Redirect");
                $location("/")
            }
    }, function onError(response){
        console.log("An error occured while trying to open a new game room...");
    });
    
    @RestController
    @RequestMapping("/api")
    public class ApiController{
    
       @RequestMapping("/hello") 
       public ResponseEntity<String> hello(){
          HttpHeaders header = new HttpHeaders();
          header.add("Content-Type", "application/json");
          return new ResponseEntity<String>("", header, HttpStatus.FOUND);
       }
    }
    
    @RestController
    @请求映射(“/api”)
    公共类ApiController{
    @请求映射(“/hello”)
    公共响应你好{
    HttpHeaders header=新的HttpHeaders();
    添加(“内容类型”、“应用程序/json”);
    返回新的ResponseEntity(“,header,HttpStatus.FOUND);
    }
    }
    

    当然,您需要为您的项目定制它。

    您只需将
    index.html
    放到
    src/main/resources/static/

    参见示例:

    在我的
    package.josn
    中,我尝试将它复制到这个位置


    请参见PackageJSON:

    在@Configuration bean中,您可以添加一个ServletRegistrationBean,使spring服务器仅用于/api/*resquest,然后在控制器中,您无需添加它

    @Bean
    public ServletRegistrationBean dispatcherRegistration() {
        ServletRegistrationBean registration = new ServletRegistrationBean(
                dispatcherServlet());
        registration.addUrlMappings("/api/*");
        registration.setLoadOnStartup(1);
        registration.setName("mvc-dispatcher");
        return registration;
    }
    
    如果你厌倦了通过遵循这么多相互冲突的解决方案来解决这个问题,请看这里!! 经过数小时的努力,我终于找到了一个最小的纯spring boot+angular 6应用程序,它可以在非根页面刷新后始终重定向到index.html,同时维护所有
    REST API
    端点路径。没有
    @EnableWebMvc
    ,没有
    @ControllerAdvice
    ,没有更改
    应用程序。属性
    ,没有自定义
    ResourceHandlerRegistry
    修改,只是简单:

    非常重要的先决条件 您*必须*将
    ng build
    的输出包含在Spring的
    resources/static
    文件夹中。您可以通过
    maven资源插件
    实现这一点。在这里学习:

    代码 推理
    • 在构建时将ng build的输出包含到
      资源/static
      中,可以使spring视图重定向(
      “forward:/index.html”
      )成功。spring似乎无法重定向到resources文件夹之外的任何内容,因此如果您试图访问站点根目录下的页面,它将无法工作
    • 具有默认功能(即不添加
      @EnableWebMvc
      或更改
      应用程序.属性
      )navi
      @Controller
      @SpringBootApplication
      public class MyApp implements ErrorController {
      
          public static void main(String[] args) {
              SpringApplication.run(MyApp.class, args);
          }
      
          private static final String PATH = "/error";
      
          @RequestMapping(value = PATH)
          public String error() {
              return "forward:/index.html";
          }
      
          @Override
          public String getErrorPath() {
              return PATH;
          }
      }
      
      @Controller
      public class RedirectController {
          /*
           * Redirects all routes to FrontEnd except: '/', '/index.html', '/api', '/api/**'
           */
          @RequestMapping(value = "{_:^(?!index\\.html|api).*$}")
          public String redirectApi() {
              return "forward:/";
          }
      }
      
      @Component
      public class CustomErrorController extends BasicErrorController {
      
          public CustomErrorController(ErrorAttributes errorAttributes) {
              super(errorAttributes, new ErrorProperties());
          }
      
          @RequestMapping(produces = "text/html")
          @Override
          public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
              HttpStatus status = getStatus(request);
              if (status == HttpStatus.NOT_FOUND) {
                  return new ModelAndView("forward:/");
              } else {
                  return super.errorHtml(request, response);
              }
          }
      }
      
      @Component
      class ForwardErrorsToIndex : ErrorViewResolver {
         override fun resolveErrorView(request: HttpServletRequest?, 
                                    status: HttpStatus?, 
                                    model: MutableMap<String, Any>?): ModelAndView {
            return ModelAndView("forward:/index.html")
         }
      }
      
      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;
       
      import java.io.IOException;
       
      @Configuration
      public class MvcConfiguration 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");
                          }
                      });
          }
      }