使用单页angular2进行重定向的Spring启动

时间:2017-05-11 11:12:07

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

我有一个带弹簧靴的单页角应用程序。它看起来像以下

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

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

我期待的是

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

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

e.g。

@RequestMapping("/api/home") can write shorthand in code  @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等网址,那么它会提供index.html页面并保留网址。

enter image description here

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

10 个答案:

答案 0 :(得分:15)

  

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

更新15/05/2017

让我重新对其他读者的查询进行短语。 (纠正我,如果被误解

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

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

NON API - 表示网址不以/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,您可以创建/index.html重定向到/api

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

答案 1 :(得分:6)

如果您厌倦了尝试遵循许多相互矛盾的解决方案来解决此问题,请看这里!

几个小时后,试图遵循数十个堆栈溢出和博客文章中的所有分散建议,我终于找到了最小的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 view重定向("forward:/index.html")成功。看来spring无法重定向到resources文件夹之外的任何内容,因此,如果您尝试访问网站根目录下的页面,它将无法正常工作。
  • 使用默认功能(即未添加@EnableWebMvc或未更改application.properties),导航到/会自动提供index.html(如果它包含在resources/static中文件夹),因此无需在其中进行更改。
  • 使用默认功能(如上所述),在春季启动应用中遇到的任何错误都会路由到/error并实现ErrorController,从而将该行为覆盖到-您猜到了-路由到index.html允许Angular接管路由。

备注

答案 2 :(得分:1)

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

答案 3 :(得分:0)

对于整个应用程序,您可以在application.properties

中添加上下文路径

server.contextPath = / API

它会在http://localhost:8080/api/home

之后为每个请求的网址添加“/ api”

对于重定向,

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

将这堆代码放在WebMVCConfig.java

答案 4 :(得分:0)

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

答案 5 :(得分:0)

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

答案 6 :(得分:0)

对于 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")
   }
}

答案 7 :(得分:0)

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

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

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/

答案 8 :(得分:-1)

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

  

有没有办法用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

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

这就是我没有用/api前缀标记每种方法的方式。

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

如果请求不以/api前缀开头,

是如何实现重定向的?

你可以通过返回Redirect的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);
   }
}

当然,您需要将其自定义为您的项目。

答案 9 :(得分:-1)

您需要尝试的是index.htmlsrc/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