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
Spring Boot with redirecting with single page angular2
提问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:
我想做的是:
For every local REST request notstarting with
/api
overwrite and redirect to default webapp/index.html. I plan to serve anything/api
to the spring controllers.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")
对于不是以
/api
覆盖和重定向到默认 webapp/index.html开头的每个本地 REST 请求。我计划为/api
弹簧控制器提供任何东西。有没有办法用 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-url或http:///localhost/some-other/123/url然后它将提供 index.html 页面并保留 URL。
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 404
non 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 404
as 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 ControllerAdvice
as 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 BaseController
and 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 BaseController
and 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 Filter
which redirects to /index.html
if 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" @RequestMapping
annotation, 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 /api
prefix.
这就是我不必用/api
前缀标记每个方法的方式。
Now, to the more complex part of your question:
现在,对于您问题的更复杂部分:
is how to achieve a redirect if the request doesn't start with /api
prefix?
如果请求不以/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.html
to src/main/resources/static/
所有你需要尝试的就是把index.html
到src/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.josn
I 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 API
endpoint paths. No @EnableWebMvc
, no @ControllerAdvice
, no changes to application.properties
, no custom ResourceHandlerRegistry
modifications, 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 build
into Spring's resources/static
folder. 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/static
at 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
@EnableWebMvc
or changes toapplication.properties
) navigating to/
automatically serves the index.html (iff it was included in theresources/static
folder) so no need to make changes there. - With default functionality (as stated above), any error encountered in a spring boot app routes to
/error
and implementingErrorController
overrides that behavior to - you guessed it - route toindex.html
which allowsAngular
to 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
评论
- Don't settle for the
HashLocationStrategy
to get over this problem as it is not recommended by Angular: https://angular.io/guide/router#which-strategy-is-best
- 不要满足
HashLocationStrategy
于克服这个问题,因为 Angular 不推荐它:https: //angular.io/guide/router#which-strategy-is-best
回答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")
}
}