Java 使用单页 angular2 重定向的 Spring Boot

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/43913753/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 01:13:41  来源:igfitidea点击:

Spring Boot with redirecting with single page angular2

javaspringangularspring-mvcspring-boot

提问by Robbo_UK

I have a single page Angular app with Spring Boot. It looks like the following:

我有一个带有 Spring Boot 的单页 Angular 应用程序。它看起来像下面这样:

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

Spring boot correctly defaults to webapp folder and serves index.html file.

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

What I am looking to do is:

我想做的是:

  1. For every local REST request notstarting with /apioverwrite and redirect to default webapp/index.html. I plan to serve anything /apito the spring controllers.

  2. Is there a way to prefix all controllers with API so that I do not have to write API every time? e.g.

    @RequestMapping("/api/home") can write shorthand in code @RequestMapping("/home")

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

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

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

or

或者

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

I'm looking for every API request, e.g. 1) http://localhost:8080/api/homekeep API with API and resolve to correct controller and return JSON, however if someone enters a URL like http:///localhost/some-urlor http:///localhost/some-other/123/urlthen it will serve the index.html page and keep the URL.

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

enter image description here

在此处输入图片说明

Alternative ways to do it: try adding #ErrorViewResolver: Springboot/Angular2 - How to handle HTML5 urls?

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

采纳答案by ansh

For every local REST request not starting with /api overwrite and redirect to default webapp/index.html. I plan to serve anything /api to the spring controllers.

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

Update 15/05/2017

2017 年 5 月 15 日更新

Let me re-phrase your query for other readers. (Correct me, if misunderstood)

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

Background
Using Spring Boot and Serving static resources from classpath


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

Requirement
All 404non apirequests should be redirected to index.html.

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

NON API- means Requests in which URL doesn't start with /api.
API- 404 should throw 404as usual.

非 API- 表示 URL 不以/api.
API- 404 应该404像往常一样抛出。

Sample Response
/api/something- will throw 404
/index.html- will server index.html
/something- will redirect to index.html

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

My Solution

我的解决方案

Let the Spring MVC throw exceptions, if any handler is not available for the given resource.

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

Add following to application.properties

将以下内容添加到 application.properties

spring.mvc.throw-exception-if-no-handler-found=true
spring.resources.add-mappings=false

Add a ControllerAdviceas follows

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

You can customize the error message as you like.

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

Is there a way to prefix all controllers with api so that I do not have to write api every time.

有没有办法用 api 为所有控制器添加前缀,这样我就不必每次都编写 api。

For this, you can create a BaseControllerand set the RequestMapping path to /api

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

Example

例子

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

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

And extend this BaseControllerand make sure you do notannotate child class with @RequestMapping

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

}

Previous Answer

上一个答案

You can create a Filterwhich redirects to /index.htmlif request path doesn't startsWith /api.

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

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

回答by Anksss

For whole application, you can add context path in application.properties

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

server.contextPath=/api

server.contextPath=/api

It will append "/api" to every requested URL after http://localhost:8080/api/home

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

For Redirection,

对于重定向,

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

Put this bunch of code in WebMVCConfig.java

把这堆代码放在 WebMVCConfig.java 中

回答by Eduardo Eljaiek

Try this instead

试试这个

@SpringBootApplication
@RestController
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.
        return "forward:/";
    }
}

回答by Moshe Arad

Ok, let's start with the simple part of your question:

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

Is there a way to prefix all controllers with api so that I do not have to write api every time?

有没有办法用 api 为所有控制器添加前缀,这样我就不必每次都编写 api?

The answer is yes, just mark your controller with a "global" @RequestMappingannotation, for example:

答案是肯定的,只需用“全局”@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";
   }
}

In the example above you can invoke hello method with this URL: /api/hello

在上面的示例中,您可以使用以下 URL 调用 hello 方法: /api/hello

and the second method with this URL: /api/hello2

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

This is how I didn't have to mark each method with /apiprefix.

这就是我不必用/api前缀标记每个方法的方式。

Now, to the more complex part of your question:

现在,对于您问题的更复杂部分:

is how to achieve a redirect if the request doesn't start with /apiprefix?

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

You can do it by returning an HTTP status code (302) of Redirect, after all, angularJs "speaks" REST natively, thus you can't force a redirect from Java/Spring code like you use to.

您可以通过返回重定向的 HTTP 状态代码 (302) 来实现,毕竟 angularJs 本地“说”REST,因此您不能像以前那样从 Java/Spring 代码强制重定向。

Then just return an HTTP message with the status code of 302, and on your angularJS do the actual redirection.

然后只返回一个状态码为 302 的 HTTP 消息,并在你的 angularJS 上进行实际的重定向。

For example:

例如:

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

On Spring:

春天:

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

of course, you'll need to custom it to your project.

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

回答by reflexdemon

All you need to try is put the index.htmlto src/main/resources/static/

所有你需要尝试的就是把index.htmlsrc/main/resources/static/

See Example:https://github.com/reflexdemon/shop/tree/master/src/main/resources/static

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

In my package.josnI try to copy it to this location.

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

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

参见 PackageJSON:https : //github.com/reflexdemon/shop/blob/master/package.json#L14

回答by Jane

In the @Configuration bean you can add a ServletRegistrationBean to make the spring server for the /api/* resquest only, then in the Controller you don't need to add it.

在@Configuration bean 中,您可以添加一个 ServletRegistrationBean 来为 /api/* resquest 制作 spring 服务器,然后在控制器中您不需要添加它。

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

回答by Hypothetical inthe Clavicle

If you're tired of trying to solve this problem by following so many conflicting solutions - look here!!

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

After hours upon hourstrying to follow all the scattered advice from dozens of stack overflow and blog posts, I've finally found the minimum PURE spring boot + angular 6 application to always redirect to index.html after a refresh on a non-root page WHILE maintaining all your REST APIendpoint paths. No @EnableWebMvc, no @ControllerAdvice, no changes to application.properties, no custom ResourceHandlerRegistrymodifications, just simplicity:

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

Very important pre-requisite

非常重要的先决条件

You *must*include the outputof ng buildinto Spring's resources/staticfolder. You can accomplish this via the maven-resources-plugin. Learn here: Copying multiple resource directories to independent target directories with maven

*必须*的输出包含ng build到 Spring 的resources/static文件夹中。您可以通过maven-resources-plugin. 在此处学习:使用 maven 将多个资源目录复制到独立的目标目录

Code

代码

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

Reasoning

推理

  • Including the output of ng-build into resources/staticat build time allows spring view redirects ("forward:/index.html") to succeed. It seems spring cannot redirect to anything outside of the resources folder so if you're trying to access pages at the root of the site, it won't work.
  • With default functionality (i.e. no additions of @EnableWebMvcor changes to application.properties) navigating to /automatically serves the index.html (iff it was included in the resources/staticfolder) so no need to make changes there.
  • With default functionality (as stated above), any error encountered in a spring boot app routes to /errorand implementing ErrorControlleroverrides that behavior to - you guessed it - route to index.htmlwhich allows Angularto take over the routing.
  • resources/static在构建时包含 ng-build 的输出允许 spring 视图重定向 ( "forward:/index.html") 成功。似乎 spring 无法重定向到资源文件夹之外的任何内容,因此如果您尝试访问站点根目录下的页面,它将无法正常工作。
  • 使用默认功能(即不添加@EnableWebMvc或更改application.properties)导航到/自动提供 index.html(如果它包含在resources/static文件夹中),因此无需在那里进行更改。
  • 使用默认功能(如上所述),spring boot 应用程序中遇到的任何错误都会路由到/error并实现ErrorController覆盖该行为 - 你猜对了 -index.html允许Angular接管路由的路由。

Remarks

评论

回答by Anton

@Controller
public class RedirectController {
    /*
     * Redirects all routes to FrontEnd except: '/', '/index.html', '/api', '/api/**'
     */
    @RequestMapping(value = "{_:^(?!index\.html|api).*$}")
    public String redirectApi() {
        return "forward:/";
    }
}

回答by jmformenti

The solution that works to me is to overwrite the BasicErrorControllerof Spring Boot:

对我有用的解决方案是覆盖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);
        }
    }
}

The method errorHtmlonly intercepts not found requests and is transparent for responses 404 (not found) from the api.

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

回答by Nick Hristov

Most reasonable solution, imho, for Spring Boot 2+(code is in Kotlin):

最合理的解决方案,恕我直言,适用于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")
   }
}