8

I have application where whole frontend part is laying in resource. I would like to separate things apart. And have separate server for UI, provided by gulp, for example.

So that I assume that my server should return index.html for all requests that are rendered by client side.

Eg: I have 'user/:id' rout that is managing by angular routing and doesn't need server for anything. How can I configure so that server will not reload or redirect me to anywhere?

My security config is following(don't know if it responsible for such things):

public class Application extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.antMatcher("/**").authorizeRequests().antMatchers("/", "/login**", "/webjars/**", "/app/**", "/app.js")
                .permitAll().anyRequest().authenticated().and().exceptionHandling()
                .authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/")).and().logout()
                .logoutSuccessUrl("/").permitAll().and().csrf()
                .csrfTokenRepository(csrfTokenRepository()).and()
                .addFilterAfter(csrfHeaderFilter(), CsrfFilter.class)
                .addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class);
    } 
Rudziankoŭ
  • 10,681
  • 20
  • 92
  • 192

3 Answers3

25

For routing, according to this guide at Using "Natural" Routes (specifically here), you have to add a controller that does the following:

@Controller
public class RouteController {
    @RequestMapping(value = "/{path:[^\\.]*}")
    public String redirect() {
        return "forward:/";
    }
}

Then using Spring Boot, the index.html loads at /, and resources can be loaded; routes are handled by Angular.

EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428
  • 6
    This doesn't cover URLs with another forward slash though. E.G. localhost:8080/im-covered and localhost:8080/im/not/covered – james Jul 16 '17 at 09:26
  • 2
    @EpicPandaForce What exactly does `{[path:[^\\.]*}` mean? Where does it come from? I understand the regex, I don't understand how Spring Boot handles the "path" part. – Boyan Kushlev Nov 14 '17 at 15:13
  • @BobbyBrown unfortunately all I know is that I followed [this guide](https://spring.io/blog/2015/05/13/modularizing-the-client-angular-js-and-spring-security-part-vii) and it worked. – EpicPandaForce Nov 14 '17 at 15:20
  • @EpicPandaForce same... I was just trying to figure out what `path` was. I guess @RequestMapping takes an object in a JSON like format. – Boyan Kushlev Nov 14 '17 at 17:45
  • 1
    This is a [special syntax for](https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-requestmapping-uri-templates) [named path variables](https://stackoverflow.com/a/20527499/750510). `path:` means that the part of URL matched by this regex will be bound to the `@PathVariable` with the corresponding name. – madhead Nov 15 '17 at 15:47
  • I don't understand why `"/{path:[^\\.]*}"` is working because it should match the path `/`. I would have written `"/{path:[^\\.]+}"` – Olivier Boissé Apr 10 '21 at 15:18
6

EpicPandaForce has a great answer, and I wanted to expand on it. The following endpoint will allow matching on nested routes as well. If you wanted to have an admin section, you can configure it to return a different index.html.

@Controller
class PageController {

    @GetMapping("/**/{path:[^\\.]*}")
    fun forward(request: HttpServletRequest): String? {
        if(request.requestURI.startsWith("/admin")) {
            return "forward:/admin/index.html"
        }
        return "forward:/index.html"
    }
}

This RequestMapping (or @GetMapping) works by excluding any request that contains a period (i.e. "index.html").

Chris Gaskill
  • 61
  • 1
  • 2
  • 1
    Not working with spring boot 2.7.0 :( – kozla13 May 23 '22 at 11:24
  • @kozla13 With spring.mvc.pathMatch.matching-strategy=ANT_PATH_MATCHER you can change the default matching strategy that is now (in 2.7) defaulting to PATH_PATTERN_PARSER (this is the change between 2.7 and older ones) – Michal Maťovčík Feb 23 '23 at 11:24
-1

If you are using Angular with Spring Data Rest, I think that the most straightforward way to do it is using angular hash location strategy.

Just putting this in the providers array in your app module:

{ provide: LocationStrategy, useClass: HashLocationStrategy }

and, obviously, import it.

  • 2
    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 – Imtiaz Shakil Siddique Jun 18 '20 at 18:13