1

I'm getting a Whitelabel Error Page (error 404) whenever I try to load any page on my Angular 9 app (aside from the root route) by putting the url on the browser and pressing enter or refreshing the page. Loading these pages using the page buttons (Angular router) works perfectly.

I tried many solutions from stackoverflow to fix this but none worked.

whitelabel error page

Backend Main Class

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class GroupsApplication {

    public static void main(String[] args) {
        SpringApplication.run(WarcraftgroupsApplication.class, args);
    }

}

Backend SecutiryConfigurer

import com.sampaiodias.groups.auth.filters.JwtRequestFilter;
import com.sampaiodias.groups.auth.services.MyUserDetailsService;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@EnableWebSecurity
public class SecurityConfigurer extends WebSecurityConfigurerAdapter {

    private final MyUserDetailsService myUserDetailsService;
    private final JwtRequestFilter jwtRequestFilter;

    public SecurityConfigurer(MyUserDetailsService myUserDetailsService, JwtRequestFilter jwtRequestFilter) {
        this.myUserDetailsService = myUserDetailsService;
        this.jwtRequestFilter = jwtRequestFilter;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(myUserDetailsService);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable().authorizeRequests().antMatchers("/auth", "/user", "/refresh", "/end-session").permitAll()
                .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

Frontend AppRoutingModule

import { GroupsComponent } from './groups/groups/groups.component';
import { RegisterComponent } from './auth/register/register.component';
import { LoginComponent } from './auth/login/login.component';
import { AppComponent } from './app.component';
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

const routes: Routes = [
  {
    path: '',
    component: AppComponent,
  },
  {
    path: 'login',
    component: LoginComponent,
  },
  {
    path: 'register',
    component: RegisterComponent,
  },
  {
    path: 'groups',
    component: GroupsComponent,
  },
  { path: '**', redirectTo: '/' },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
})
export class AppRoutingModule {}

Frontend proxy

const PROXY_CONFIG = [
  {
    context: ["/", "/user", "/group", "/character"],
    target: "http://localhost:8080",
    secure: false,
  },
];

module.exports = PROXY_CONFIG;

1 Answers1

1

You must forward to index.html for every single Angular route you have setup in your routing module, not just the root route. See Spring Boot with redirecting with single page angular2 for defining a single Controller to forward all requests to index.html.

Otherwise Spring tries to handle the url itself but since it doesn't have a @Controller request mapping for the url you entered (because you want Angular to handle it), it returns 404.

EDIT

OP is referring to ng serve and not a deployed environment. In that case, you'd have to create an ErrorController that redirects to the ng dev server - http://localhost:4200.

@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class AngularRedirectErrorController extends AbstractErrorController {

  private final ErrorAttributes errorAttributes;

  public AngularRedirectErrorController(ErrorAttributes errorAttributes) {
    super(errorAttributes);
    this.errorAttributes = errorAttributes;
  }

  @RequestMapping(produces = MediaType.TEXT_HTML_VALUE)
  public String redirectToAngular(ServletWebRequest request) {
    Map<String, Object> errorAttrs = errorAttributes.getErrorAttributes(request, false);
    String path = (String) errorAttrs.get("path");
    return "redirect:http://localhost:4200" + path;
  }

  @RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE)
  @ResponseBody
  public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
    HttpStatus status = getStatus(request);
    if (status == HttpStatus.NO_CONTENT) {
        return new ResponseEntity<>(status);
    }

    WebRequest webRequest = new ServletWebRequest(request);
    Map<String, Object> body = errorAttributes.getErrorAttributes(webRequest, false);
    return new ResponseEntity<>(body, status);
  }

  @Override
  public String getErrorPath() {
    return null;
  }

}
drumonii
  • 343
  • 5
  • 15
  • This didn't work for me, instead of the whitelabel page I get a completely white page. And if I'm understanding the reasoning behind this, why should I put the build of my Angular app on the Spring static folder when I'm getting this error while running my Angular app with ng serve? – Lucas Sampaio Dias Jun 05 '20 at 03:08
  • Oh you using ng serve! Since you didn't mention that, I thought you were referring to a deployed environment. See my updated answer. – drumonii Jun 07 '20 at 18:24
  • It does fix the whitelable issue, thanks! Is it possible to redirect to the page I'm trying to access instead of just the root route? For exemple, if I try to access http://localhost:4200/foo the code above redirects me to http://localhost:4200 – Lucas Sampaio Dias Jun 08 '20 at 02:24
  • The solution posted actually breaks my JWT auth too, giving the following error when the JWT token expires (instead of returning 403 or 401): core.js:6272 ERROR Error: Uncaught (in promise): HttpErrorResponse: {"headers":{"normalizedNames":{},"lazyUpdate":null},"status":200,"statusText":"OK","url":"http://localhost:4200/","ok":false,"name":"HttpErrorResponse","message":"Http failure during parsing for http://localhost:4200/","error":{"error":{},"text":" \n\n \n \n... – Lucas Sampaio Dias Jun 08 '20 at 03:17
  • Your code fixes the JWT part, thank you so much for that! However, it doesn't work when it comes to the redirect, the page still doesn't load. It calls the redirectToAngular method 20 times (returning the correct url + path) but the browser shows a ERR_TOO_MANY_REDIRECTS error. – Lucas Sampaio Dias Jun 11 '20 at 15:22
  • Hmm that's odd because I've tested it locally and works as expected. No ERR_TOO_MANY_REDIRECTS. Perhaps there is something else interacting with the redirect in your app that I'm not aware of. Sadly, I'm not sure how I can help. – drumonii Jun 12 '20 at 01:36