簡體   English   中英

Spring 使用單頁 angular2 重定向引導

[英]Spring Boot with redirecting with single page angular2

我有一個帶有 Spring 引導的單頁 Angular 應用程序。 它如下所示:

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

Spring 引導正確默認為 webapp 文件夾並提供 index.html 文件。

我想做的是:

  1. 對於每個/api開頭的本地 REST 請求覆蓋並重定向到默認 webapp/index.html。 我計划為 spring 控制器提供任何/api服務。

  2. 有沒有辦法給所有控制器加上 API 前綴,這樣我就不必每次都寫 API 了? 例如

    @RequestMapping("/api/home") 可以在代碼中寫速記 @RequestMapping("/home")

或者

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

I'm looking for every API request, eg 1) http://localhost:8080/api/home keep API with API and resolve to correct controller and return JSON, however if someone enters a URL like http:///localhost/ some-urlhttp:///localhost/some-other/123/url然后它將提供 index.html 頁面並保留 URL。

在此處輸入圖像描述

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

如果您厭倦了通過遵循這么多相互矛盾的解決方案來解決這個問題 - 看這里!!

經過幾個小時的努力,我試圖遵循來自數十篇堆棧溢出和博客文章的所有分散的建議,我終於找到了最小的 PURE spring boot + angular 6 應用程序,在非根頁面上刷新后總是重定向到 index.html同時維護所有REST API端點路徑。 沒有@EnableWebMvc ,沒有@ControllerAdvice ,沒有改變application.properties ,沒有自定義ResourceHandlerRegistry修改,只是簡單:

非常重要的先決條件

*必須*ng build的輸出包含到 Spring 的resources/static文件夾中。 您可以通過maven-resources-plugin完成此操作。 在此處學習: 使用 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接管路由。

評論

  • 不要滿足於HashLocationStrategy來解決這個問題,因為 Angular 不推薦它: https : HashLocationStrategy

對於每個不以 /api 開頭的本地 REST 請求,覆蓋並重定向到默認的 webapp/index.html。 我計划為 spring 控制器提供任何/api。

2017 年 5 月 15 日更新

讓我重新表述您對其他讀者的查詢。 如果誤解了,請糾正我

背景
使用 Spring Boot 和從類路徑提供靜態資源

要求
所有404非 api請求都應重定向到index.html

非 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 !!";
    }

}

上一個答案

如果請求路徑不是 startsWith /api您可以創建一個重定向到/index.htmlFilter

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

試試這個

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

對我有用的解決方案是覆蓋 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(未找到)是透明的。

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

在這個線程上為時已晚,但認為它可能會幫助某人

嘗試了很多解決方案,但這看起來很簡單,對我來說很棒

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 : //keeprising.in/java/springboot/make-spring-boot-surrender-routing-control-to-angular/

對於整個應用程序,您可以在 application.properties 中添加上下文路徑

server.contextPath=/api

它將在http://localhost:8080/api/home之后的每個請求的 URL 附加“/api”

對於重定向,

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

把這堆代碼放在 WebMVCConfig.java 中

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

我不知道為什么,但是如果不添加更多代碼,根 url "/" 將無法解析。 這就是我最終的結果。

import java.io.IOException;
import java.util.concurrent.TimeUnit;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.http.CacheControl;
import org.springframework.web.context.request.RequestContextListener;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.resource.PathResourceResolver;

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

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

好的,讓我們從您問題的簡單部分開始:

有沒有辦法用 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);
   }
}

當然,您需要根據您的項目對其進行自定義。

您需要嘗試的只是將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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM