【问题标题】:Spring Boot with redirecting with single page angular2带有单页 angular2 重定向的 Spring Boot
【发布时间】:2023-03-13 02:05:01
【问题描述】:

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

src
  main
  java
    controller
       HomeController
       CustomerController
       OtherController
  webapp
    js/angular-files.js
    index.html

Spring boot 正确默认为 webapp 文件夹并提供 index.html 文件。

我想做的是:

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

  2. 有没有办法为所有控制器添加 API 前缀,这样我就不必每次都编写 API? 例如

    @RequestMapping("/api/home") 可以在代码中写简写@RequestMapping("/home")

@RequestMapping("/api/other-controller/:id") can write shorthand  @RequestMapping("/other-controller/:id")

我正在寻找每个 API 请求,例如1) http://localhost:8080/api/home 保留 API 和 API 并解析正确的控制器并返回 JSON,但是如果有人输入像 http:///localhost/some-urlhttp:///localhost/some-other/123/url 这样的 URL,那么它将提供 index.html 页面并保留 URL。

其他方法:尝试添加#ErrorViewResolver: Springboot/Angular2 - How to handle HTML5 urls?

【问题讨论】:

  • 您可以尝试创建一个自定义注释,其中将包含 @RequestMapping("/api") 并将其应用于您的 api 控制器。然后在特定网址的方法上使用@RequestMapping
  • 这个问题可能还有一个额外的要求:index.html 可能会引用 js 和 css 文件,虽然不是“index.html”,但不应作为 /api/* 处理* 请求

标签: java spring angular spring-mvc spring-boot


【解决方案1】:

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

server.contextPath=/api

它会将“/api”附加到http://localhost:8080/api/home之后的每个请求的URL

对于重定向,

@Override
public void addViewControllers(ViewControllerRegistry registry) {
    registry.addRedirectViewController("/", "/home");
    registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
    super.addViewControllers(registry);
}

把这一堆代码放到WebMVCConfig.java中

【讨论】:

  • 感谢您的反馈。它解决了一半的问题......我仍然想要任何不是 api 的东西来重定向和加载 index.html。
  • 我编辑了重定向的答案,希望它对你有用
  • 不确定该示例是否不包括 api 并让它们继续使用控制器?我已经更新了问题以进一步解释。
【解决方案2】:

试试这个

@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:/";
    }
}

【讨论】:

  • 正则表达式 "/{[path:[^\\.]*}" 匹配什么?.. 看起来它匹配任何内容并将其转发到 / ...这怎么会不捕获/api 请求?
  • 匹配所有没有后缀的东西(所以不是静态资源)
  • @Robbo_UK 的回答:约定:所有不包含句点(并且尚未明确映射)的路径都是 Angular 路由,并且应该转发到主页。来源spring.io/blog/2015/05/13/…
  • 我想我做错了什么。现在我得到的只是浏览器中的“forward:/”^^
  • @displayname 那是因为您使用的是@RestController 注释,因此“forward:/”被认为是响应正文。尝试改用@Controller
【解决方案3】:

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

2017 年 5 月 15 日更新

让我为其他读者重新表述您的查询。 (如果有误解,请纠正我

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

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

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

示例响应
/api/something - 将抛出404
/index.html - 将服务器index.html
/something - 将重定向到index.html

我的解决方案

如果给定资源没有任何处理程序可用,则让 Spring MVC 抛出异常。

添加关注application.properties

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);
    }
}

您可以根据需要自定义错误消息。

有没有办法给所有控制器加上api前缀,这样我就不用每次都写api了。

为此,您可以创建一个BaseController 并将RequestMapping 路径设置为/api

示例

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping("/api")
public abstract class BaseController {}

并扩展此 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 !!";
    }

}

上一个答案

你可以创建一个Filter,如果请求路径没有以/api开始,则重定向到/index.html

// CODE REMOVED. Check Edit History If you want.

【讨论】:

  • 这看起来接近我正在寻找的东西。但是它已经停止在 webapp 中提供静态内容了?返回 ERR_TOO_MANY_REDIRECTS
  • 重定向过多可能是由于您的Spring Security 配置。可以分享 TRACE 日志吗?
  • 我禁用了 spring 安全性。我在 doFilter 方法上添加了日志记录,并且它一直返回到自身。在我看来,它已经禁用了一些解析为 /webapp 的默认 Spring Boot 行为/index.html .. 它卡在一个循环中。
  • 哦!我的错。对于您的工作,您甚至不需要过滤器。基本上,您希望将所有 404 重定向到 index.html。其余请求将由 Spring 资源处理映射器管理。如果有人点击/api/1 - 对应的它将与/api/** 匹配,并调用相应的控制器。另一方面,如果有人点击/xxx,但它在您的静态资源中不可用,则 servlet 将抛出 404。
  • 差不多但不完全。 Spring 抱怨找不到映射到“/index.html”
【解决方案4】:

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

有没有办法给所有的控制器加上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 消息,然后在你的 angularJS 上进行实际的重定向。

例如:

在 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);
   }
}

当然,您需要根据您的项目对其进行自定义。

【讨论】:

    【解决方案5】:

    您需要尝试的只是将index.html 放入src/main/resources/static/

    参见示例: https://github.com/reflexdemon/shop/tree/master/src/main/resources/static

    在我的package.josn 中,我尝试将其复制到此位置。

    查看 PackageJSON: https://github.com/reflexdemon/shop/blob/master/package.json#L14

    【讨论】:

      【解决方案6】:

      在@Configuration bean中你可以添加一个ServletRegistrationBean来为/api/* resquest创建spring服务器,然后在Controller中你不需要添加它。

      @Bean
      public ServletRegistrationBean dispatcherRegistration() {
          ServletRegistrationBean registration = new ServletRegistrationBean(
                  dispatcherServlet());
          registration.addUrlMappings("/api/*");
          registration.setLoadOnStartup(1);
          registration.setName("mvc-dispatcher");
          return registration;
      }
      

      【讨论】:

        【解决方案7】:

        如果您厌倦了通过遵循这么多相互冲突的解决方案来解决这个问题 - 看这里!!

        几小时后几小时后尝试遵循来自数十篇堆栈溢出和博客文章的所有分散建议,我终于找到了始终重定向到 index.html 的最小 PURE spring boot + angular 6 应用程序。在非根页面上刷新后的 html,同时维护所有 REST API 端点路径。没有@EnableWebMvc,没有@ControllerAdvice,没有更改application.properties,没有自定义ResourceHandlerRegistry 修改,只是简单:

        非常重要的先决条件

        *必须*ng build的输出包含到Spring的resources/static文件夹中。您可以通过maven-resources-plugin 完成此操作。在这里学习:Copying multiple resource directories to independent target directories with maven

        代码

        @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;
            }
        }
        

        推理

        • 在构建时将 ng-build 的输出包含到 resources/static 中可以让 spring 视图重定向 ("forward:/index.html") 成功。似乎 spring 无法重定向到资源文件夹之外的任何内容,因此如果您尝试访问站点根目录下的页面,它将无法正常工作。
        • 使用默认功能(即没有添加@EnableWebMvc 或更改application.properties)导航到/ 会自动提供index.html(如果它包含在resources/static 文件夹中),因此无需进行更改在那里。
        • 使用默认功能(如上所述),在 Spring Boot 应用程序中遇到的任何错误都会路由到 /error 并实现 ErrorController 会覆盖该行为 - 你猜对了 - 路由到 index.html 允许 Angular 到接管路由。

        备注

        【讨论】:

        • 还要注意这需要用@Controller注解。 @RestController 将不起作用。
        • 你是上帝吗?哇...我花了这么多时间!!非常感谢兄弟!
        • 只是他的仆人 :-) 很高兴答案对您有所帮助。
        • @DanOrtega 您可以使用@Controller 并在您想要响应正文的方法中包含@ResponseBody?因为@RestController 显然只是将@Controller@ResponseBody 添加到我在网上阅读的课程中。 :)
        • 如果您查看控制台,您会注意到每个页面请求都有一个错误响应代码,解决方案是将@ResponseStatus(HttpStatus.OK) 添加到error() 方法。保重,兄弟。 :)
        【解决方案8】:
        @Controller
        public class RedirectController {
            /*
             * Redirects all routes to FrontEnd except: '/', '/index.html', '/api', '/api/**'
             */
            @RequestMapping(value = "{_:^(?!index\\.html|api).*$}")
            public String redirectApi() {
                return "forward:/";
            }
        }
        

        【讨论】:

          【解决方案9】:

          对我有用的解决方案是覆盖 Spring Boot 的 BasicErrorController

          @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);
                  }
              }
          }
          

          errorHtml 方法只拦截未找到的请求,对于来自 api 的响应 404(未找到)是透明的。

          【讨论】:

            【解决方案10】:

            最合理的解决方案,恕我直言,对于 Spring Boot 2+(代码在 Kotlin 中):

            @Component
            class ForwardErrorsToIndex : ErrorViewResolver {
               override fun resolveErrorView(request: HttpServletRequest?, 
                                          status: HttpStatus?, 
                                          model: MutableMap<String, Any>?): ModelAndView {
                  return ModelAndView("forward:/index.html")
               }
            }
            

            【讨论】:

              【解决方案11】:

              在这个线程上为时已晚,但认为它可能对某人有所帮助

              尝试了很多解决方案,但这对我来说看起来很简单而且很棒

              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");
                                  }
                              });
                  }
              }
              

              致谢:https://keepgrowing.in/java/springboot/make-spring-boot-surrender-routing-control-to-angular/

              【讨论】:

                猜你喜欢
                • 2017-07-22
                • 1970-01-01
                • 2018-10-24
                • 2016-09-12
                • 2018-03-11
                • 2021-01-05
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多