Angular not fetching from Spring via RESTful API - java

I was having some problem when trying to call RESTful API from Angular to Spring. Here is my typescript class in Angular:
import { Injectable } from "#angular/core";
import { CATEGORIES } from "./mock-category";
import { Observable, of } from "rxjs";
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { Category } from "./category";
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
#Injectable({
providedIn: "root"
})
export class CategoryService {
constructor(private http: HttpClient) { }
private categoryUrl = '/api/category';
getCategories() {
return this.http.get<Category[]>(this.categoryUrl);
}
}
And my controller class in Java:
package controller;
import domain.Category;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import service.CategoryService;
import java.util.List;
#CrossOrigin(origins = "http://localhost:4200", methods = { RequestMethod.POST, RequestMethod.GET, RequestMethod.DELETE,
RequestMethod.PUT })
#RestController
#RequestMapping({"/api"})
public class CategoryController {
#Autowired
private CategoryService categoryService;
#GetMapping("/categories")
public List findAll(){
return categoryService.findAll();
}
}
I managed to start the maven already but when I try to fetch from Angular, I am getting this error message in console:
zone.js:3243 GET http://localhost:4200/api/category 404 (Not Found)
Any ideas? Thanks in advance!

That's because you are requesting on url http://localhost:4200/. But on 4200 port your angular app is running, not the backend. Backend will most probably be running on 8080 port, so you need to specify full URL of backend service. like:
private categoryUrl = 'http://localhost:8080/api/categories';
NOTE: I just assumed your backend is running on 8080, you need to add the port on which you are running your backend. Also change /category to /categories as you have specified in your Service

For your CORS issue you can try this :
#Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
#Override
public void addCorsMappings(CorsRegistry registry) {
// localhost:4200 (for dev) or YOUR_SERVER:8080 (for production)
registry.addMapping("/api/**").allowedOrigins("http://localhost:4200", "http://YOUR_SERVER:8080").allowCredentials(true);
}
}
And here are my headers for client side :
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
'withCredentials': 'true'
})
};

Try adding this as java Configuration file under SpringBootApp package:
#Configuration
#EnableWebSecurity
public class AppSecurity extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().cors().disable().httpBasic()
.and()
.authorizeRequests()
.antMatchers("/api/categories").permitAll()//Permits this api for all
.anyRequest().authenticated();
}
}

Drawback of Ans- https://stackoverflow.com/a/56555192/6582610 is you have to change in every ts service file and add domain in every URL
rather than you can create Interceptor for this
Use the new HttpClient Interceptor to manipulate your request.
Create a proper injectable that implements HttpInterceptor:
import {Injectable} from '#angular/core';
import {HttpEvent, HttpInterceptor, HttpHandler, HttpRequest} from '#angular/common/http';
import {Observable} from 'rxjs/Observable';
#Injectable()
export class APIInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const apiReq = req.clone({ url: `http://localhost:8080/${req.url}` });
return next.handle(apiReq);
}
}
The HttpInterceptor can clone the request and change it as you wish, in this case I added localhost path in your every api URL http://localhost:8080/.
Provide the AppModule with the following configurations:
provide: HTTP_INTERCEPTORS,
useClass: APIInterceptor,
multi: true,
}
]
Now all your requests will start with http://localhost:8080, you don't have to go to every file and change it.Also if you have multiple domain you can configure here with if- else condition.

Related

Cannot make request to springboot app because of CORS

I try to make a POST request to my springboot app hosted on heroku,
const email = 'x#x.com';
const password = 'x';
const data = JSON.stringify({
email,
password
});
fetch(
'https://culinareat-test.herokuapp.com/api/user/users/_login',
{
method: 'POST',
headers : {
'Accept' : 'application/json',
'Content-Type' : 'application/json'
},
body : data,
}
).then(res => {
console.log(res);
})
I got this as the response:
This is the controller on my springboot app:
#RestController
#AllArgsConstructor
public class UserController {
private final UserService userService;
#RequestMapping(method = RequestMethod.POST, value = "/user/users/_login")
public LoginResponse logUserIn(#RequestBody LoginRequest loginRequest, HttpServletResponse response, HttpServletRequest request){
return userService.logUserIn(loginRequest, response, request);
}
//another endpoints
}
I tried to do this from another articles i just read on stackoverflow:
#Configuration
public class AppConfig implements WebMvcConfigurer {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedMethods("*");
}
}
Still, it does nothing and i still cannot do the request.
Any recommendation what should i do? Is there anything i should add on the server-side? Thanks in advance!
You need to add annotation #CrossOrigin in your controller
#RestController
#CrossOrigin(origins = "*", maxAge = 3600)
#AllArgsConstructor
public class UserController {
private final UserService userService;
#RequestMapping(method = RequestMethod.POST, value = "/user/users/_login")
public LoginResponse logUserIn(#RequestBody LoginRequest loginRequest, HttpServletResponse response, HttpServletRequest request){
return userService.logUserIn(loginRequest, response, request);
}
//another endpoints
}
Keep in mind that cross-origin issues are raised by Browsers. By default, if you are running javascript on origin http://localhost, you cannot specify CORS for localhost on the back-end.
Browsers such as Chrome do not allow javascript running on origin http://localhost to share resources in a cross-origin manner. See answer https://stackoverflow.com/a/10892392/15262845.
The solution is simple. Resolve a domain, e.g., example.com to 127.0.0.1 in hosts file. Then request example.com:8080 or example.com to load your front-end application and post the request from example.com origin.
Besides, you can always debug CORS by navigating to Network tab and copy, paste these two following headers.
access-control-allow-credentials: true
access-control-allow-origin: http://example.com/

Using Azure AD premium custom roles with spring security for role based access

I have created a demo spring boot application where i want to use AD authentication and authorization using AD and spring security.Looking at Azure docs i did the following
package com.myapp.contactdb.contactfinder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
#RequestMapping("/directory")
public interface Directory {
#Autowired
#PreAuthorize("hasRole('Users')")
#GetMapping("/contact/{mobile}")
public String getContact(#PathVariable("mobile") Long mobile);
#Autowired
#GetMapping("/contact/data")
public String getData();
}
which is the rest API entry point.
I created groups and users in it in the respective Azure AD.And used that group as specified in azure docs like this
package com.myapp.contactdb;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.oauth2.client.oidc.userinfo.OidcUserRequest;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import com.microsoft.azure.spring.autoconfigure.aad.AADAppRoleStatelessAuthenticationFilter;
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.oauth2Login()
.userInfoEndpoint()
.oidcUserService(oidcUserService);
}
}
and app properties as
spring.main.banner-mode=off
# create and drop tables and sequences, loads import.sql
#spring.jpa.hibernate.ddl-auto=create-drop
# MySql settings
spring.datasource.url=jdbc:mysql://localhost:3306/xxxx
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL8Dialect
# HikariCP settings
# spring.datasource.hikari.*
spring.datasource.hikari.connection-timeout=60000
spring.datasource.hikari.maximum-pool-size=5
# azure.activedirectory.tenant-id
azure.activedirectory.tenant-id = xxxx
azure.activedirectory.client-id = xxxx
# spring.security.oauth2.client.registration.azure.client-id
spring.security.oauth2.client.registration.azure.client-id = xxxxxxx
# spring.security.oauth2.client.registration.azure.client-secret
spring.security.oauth2.client.registration.azure.client-secret = xxxxxxxx
azure.activedirectory.active-directory-groups = Users
However i require to authorize using custom roles.I have added an azure premium AD free trial and created a role viz., "Operator".
However problem is what property do i use to depict that in the app.props file and how to get the role to get reflected in the #Preauthorize(hasRole('Operator')).
Any idea or anything that i may have not seen?
#Jim,
So finally i went with this by modifying the WebSecurityConfig class from above in the question
package com.xxx.contactdb;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;
import org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority;
import net.minidev.json.JSONArray;
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().and().oauth2Login().userInfoEndpoint()
.oidcUserService(this.oidcUserService());
}
/**
* Replaces the granted authorities value received in token with the roles value
* in token received from the app roles attribute defined in manifest and
* creates a new OIDCUser with updated mappedAuthorities
*
* #return oidcUser
*/
private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {
final OidcUserService delegate = new OidcUserService();
return (userRequest) -> {
Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
// Delegate to the default implementation for loading a user
OidcUser oidcUser = delegate.loadUser(userRequest);
oidcUser.getAuthorities().forEach(authority -> {
if (OidcUserAuthority.class.isInstance(authority)) {
OidcUserAuthority oidcUserAuthority = (OidcUserAuthority) authority;
Map<String, Object> userInfo = oidcUserAuthority.getAttributes();
JSONArray roles = null;
if (userInfo.containsKey("roles")) {
try {
roles = (JSONArray) userInfo.get("roles");
roles.forEach(s -> {
mappedAuthorities.add(new SimpleGrantedAuthority("ROLE_" + (String) s));
});
} catch (Exception e) {
// Replace this with logger during implementation
e.printStackTrace();
}
}
}
});
oidcUser = new DefaultOidcUser(mappedAuthorities, oidcUser.getIdToken(), oidcUser.getUserInfo());
return oidcUser;
};
}
}
I did this change for spring boot version 2.3.1 Release which uses Azure 2.3.1 and spring security version 5.3.3.
Mentoned this because for Spring boot version 2.1.13 we could use UserAuthoritiesMapping as the authorities would have a OIDCUserService type mapping which the latest one doesnt.
However if one uses DB to populate the roles to the Granted Authorities then they can still go with this option and not the OidcUser option.This is working as of now.
If you want to project your application with app role, please refer to the following steps
The example is used app role auth to project web api
Register web API application and configure API scope
Define app role in your Azure AD web api application. Please add the following content in your application manifest
"appRoles": [
{
"allowedMemberTypes": [
"User"
],
"displayName": "Admin",
"id": "2fa848d0-8054-4e11-8c73-7af5f1171001",
"isEnabled": true,
"description": "Full admin access",
"value": "Admin"
},
{
"allowedMemberTypes": [
"User"
],
"displayName": "User",
"id": "f8ed78b5-fabc-488e-968b-baa48a570001",
"isEnabled": true,
"description": "Normal user access",
"value": "User"
}
],
Assign these roles for the user
Register client application in Azure AD and configure API permissions
Enable implicit flow in client application
Configure API application
a. sdk
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-active-directory-spring-boot-starter</artifactId>
<version>2.3.1</version>
</dependency>
b. application.properties
azure.activedirectory.session-stateless=true
azure.activedirectory.client-id=xxxxxx-your-client-id-xxxxxx
azure.activedirectory.appIdUri=xxxxxx-your-appIDUri-xxxxxx
c. WebSecurityConfig class
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AADAppRoleStatelessAuthenticationFilter aadAuthFilter;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER);
http.authorizeRequests()
.antMatchers("/", "/index.html", "/public").permitAll()
.anyRequest().authenticated();
http.addFilterBefore(aadAuthFilter, UsernamePasswordAuthenticationFilter.class);
}
}
d. Controller
#RestController
public class HelloController {
#GetMapping("/public")
#ResponseBody
public String publicMethod() {
return "public endpoint response";
}
#GetMapping("/authorized")
#ResponseBody
#PreAuthorize("hasAnyRole('User','Admin')")
public String onlyAuthorizedUsers() {
return "authorized endpoint response";
}
#GetMapping("/admin/demo")
#PreAuthorize("hasRole('Admin')")
#ResponseBody
public String onlyForAdmins() {
return "admin endpoint";
}
}
Test. I use a single page to do a test
For more details, please refer to here and here

Getting No 'Access-Control-Allow-Origin' header is present on the requested resource. error

I am using Angular in the frontend and Java Spring in the backend but I am getting the error: No 'Access-Control-Allow-Origin' header is present on the requested resource. while I am sure CORS is enabled. This is my configuration file:
package com.aon04.backend.configuration;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
#Configuration
#EnableWebMvc
public class CorsConfiguration implements WebMvcConfigurer
{
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
}
And it works I can do every other request without a problem but this PUT request in my service gives me the error:
updateExamById(id, exam: Examen): Observable<Examen> {
return this.http.put<Examen>(APIURL + '/update/' + id, {file: exam.file, name: exam.naam});
}
This is my server side of it:
#PutMapping("/update/{id}")
public Exam UpdateExam(#RequestParam("file") MultipartFile file, #RequestParam("name") String name, #PathVariable("id") int id)
{
Exam newExam = new Exam();
newExam.setId(id);
newExam.setSkelet(file.getOriginalFilename());
newExam.setNaam(name);
newExam.setCreatedAt(LocalDateTime.now());
Exam exam2 = ExamFactory.update(newExam);
examRepository.save(exam2);
storageService.store(file);
return newExam;
}
No need to add this configuration
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
Just add like this in your Controller class at class level or method level what u want
#CrossOrigin(origins = "http://localhost:4200")

Spring security login not working, no JSESSIONID cookie returned and redirect fails

I'm trying to write an application with separated Backend (written using Spring Boot, and Spring Security for login) and Frontend (ReactJS). Right now I'm struggling with accessing a secured endpoint after a successful login.
What I want to achieve: Make a GET to secured endpoint e.g. '/books/all'. If user not logged in, return 401. If 401 received on front-end, make a POST to '/login'. Then I want to have a successful login and to be able to make a successful GET to '/books/all'.
What doesn't work: The last part. I'm making a POST to '/login' and receive 200 GET. Then I make a second call to '/books/all' and receive GET 401. Also I no longer receive JSESSIONID cookie which worries me.
My question: how to fix this behavior? I believe it's connected to JSESSIONID (server doesn't send information about user being successfully logged in?).
On front-end I'm using axios.
axios.get('http://localhost:8080/rest/book/anna/all')
.then(response => {
console.log('response rebuild');
console.log(response);
if (response.status === 401 && response.request.responseURL === 'http://localhost:8080/login') {
axios.post('http://localhost:8080/login', 'username=c&password=d')
.then(response => {
console.log('response 2');
console.log(response);
})
.catch(error => {
console.log('error');
console.log(error);
})
}
})
.catch(error => {
console.log('error 2');
console.log(error);
axios.post('http://localhost:8080/login', 'username=c&password=d')
.then(response => {
console.log('response 2');
console.log(response);
axios.get('http://localhost:8080/rest/book/anna/all')
.then(response => {
console.log('response 3');
console.log(response);
})
.catch(error => {
console.log('error 3');
console.log(error);
})
})
.catch(error => {
console.log('error');
console.log(error);
})
});
Please mind that I'm aware that this code it's low quality; it's just temporary to check if redirection after login is working.
SecurityConfig.java
package com.shareabook.security;
import com.shareabook.repository.UsersRepository;
import com.shareabook.service.CustomUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
#EnableJpaRepositories(basePackageClasses = UsersRepository.class)
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetailsService userDetailsService;
#Autowired
private RESTAuthenticationEntryPoint restAuthenticationEntryPoint;
#Autowired
private RESTAuthenticationSuccessHandler restAuthenticationSuccessHandler;
#Autowired
private RESTAuthenticationFailureHandler restAuthenticationFailureHandler;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(getPasswordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf().disable()
.authorizeRequests()
.antMatchers("**/anna/**").authenticated()
.anyRequest().permitAll();
http.exceptionHandling().authenticationEntryPoint(restAuthenticationEntryPoint);
http.formLogin().successHandler(restAuthenticationSuccessHandler);
http.formLogin().failureHandler(restAuthenticationFailureHandler);
// .and()
// .formLogin().permitAll();
http
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessUrl("/rest/author/all");
}
private PasswordEncoder getPasswordEncoder() {
return new PasswordEncoder() {
#Override
public String encode(CharSequence charSequence) {
return charSequence.toString();
}
#Override
public boolean matches(CharSequence charSequence, String s) {
return true;
}
};
}
}
RESTAuthenticationEntryPoint.java
#Component
public class RESTAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
throws IOException, ServletException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
}
RESTAuthenticationFailureHandler.java
#Component
public class RESTAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
#Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException,
ServletException {
super.onAuthenticationFailure(request, response, exception);
}
}
RESTAuthenticationSuccessfulHandler.java
#Component
public class RESTAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
// clearAuthenticationAttributes(request);
HttpSession session = request.getSession();
session.setAttribute("username", "c");
response.setStatus(HttpServletResponse.SC_OK);
}
}
BooksController.java
#RestController
#RequestMapping("/rest/book")
public class BookController {
#CrossOrigin(origins = "http://localhost:8888")
#PreAuthorize("hasAnyRole('ROLE_ADMIN')")
#RequestMapping(value = "/anna/all", method = RequestMethod.GET)
public List<String> securedHello() {
List<String> word = new ArrayList<>();
word.add("all");
System.out.print(word);
return word;
}
}
You have enabled forms authentication on your application. At the moment you send the post to login page Spring authenticates your request and by default caches the authnetication on user session.
Later you can send requests to server bound to the same session without additional authentication information. But you need to provide session information on your request. Usually this is done by supplying the JSESSIONID cookie on your next requests. Browser does this for you automatically but only after page reload. If you stay of the same page only cookies loaded for that page initially will be sent back to server.
For SPA applications I would suggest using token-based authentication instead of forms. You will have first to login and receive a token in response. Next you will have to supply Authorization header with each your request providing token as authentication information (usually in form Bearer <token>)
Late answer, but may help someone.
Try to add header "Access-Control-Allow-Credentials" with value "true" to your server responses. And in request config of axios calls set option withCredentials to true as well.

Whitelabel Error Page after refresh

I have Spring Boot Application (backend) and for the Frontend I am using the Angular 2 Single Page Application.
Whenever I navigate to a route for example: localhost:8080/getAccounts and do after the navigation a refresh I get the Whitelabel Error Page. If I am at the root localhost:8080 I works fine. The problem only occurs in the sub links.
Returning (use the return/back button) to the previous page also works fine. Just the refresh.
I also can not call direct the link: localhost:8080/getAccounts. First I have to go to Home (localhost:8080) an call the page throug sub navigation bar.
Does anybody had the same problem? What excalty I do have to change. My Code:
Main.ts
import {bootstrap} from '#angular/platform-browser-dynamic';
import {AppComponent} from './components/app.component';
import {HTTP_PROVIDERS};
import {enableProdMode} from '#angular/core';
enableProdMode();
bootstrap(AppComponent, [HTTP_PROVIDERS]);
app.comonent:
import { Component, OnInit } from '#angular/core';
import { Http } from '#angular/http';
import { RouteConfig, ROUTER_DIRECTIVES, ROUTER_PROVIDERS } from '#angular/router-deprecated';
import { HomeComponent } from './home.component';
import { UserSearchComponent} from './webUserProfiles.component';
import { UserDetailViewComponent} from './webUserProfileView.component';
import { HTTPService } from '../service/http.service';
#Component({
selector: 'app-content',
templateUrl: './app/templates/app.component.html',
directives: [ROUTER_DIRECTIVES, AccessErrorComponent],
providers: [
ROUTER_PROVIDERS,
HTTPService
]
})
#RouteConfig([
{
path: '/',
name: 'HomeComponent,
useAsDefault: true
},
{
path: '/user',
name: 'UserSearch',
component: UserSearchComponent,
},
{
path: '/user/:id',
name: 'UserDetailView',
component: UserDetailViewComponent,
}
])
export class AppComponent implements OnInit {
constructor (
) { }
}
}
Thanks in advance
After some researches, i found this pretty good answer from Thierry Templier
With the default strategy (HTML5 history API) of routing, you need a server configuration to redirect all your paths to your HTML entry point file. With the hashbang approach it's not necessary... If you want to switch to this approach, simply use the following code:
import { bootstrap } from "angular2/platform/browser";
import { provide } from "angular2/core";
import {
ROUTER_PROVIDERS, LocationStrategy, HashLocationStrategy
} from "angular2/router";
bootstrap(MainApp, [
ROUTER_PROVIDERS,
provide(LocationStrategy, {useClass:HashLocationStrategy});
]);
You could have a look at these questions about this issue:
When I refresh my website I get a 404. This is with Angular2 and firebase
PathLocationStrategy vs HashLocationStrategy in web apps
Is Angular 2's Router broken when using HTML5 routes?
I had a similar issue WhiteLabel Error message on my Angular SPA whenever I did a refresh.
If you don't want to change the app URL (which will happen if you use HashLocation Strategy), you could add a new controller to handle the White label Error mapping in your Spring Boot app.
The fix was to create a controller that implements ErrorController and return a ModelAndView object that forwards to /
#CrossOrigin
#RestController
public class IndexController implements ErrorController {
private static final String PATH = "/error";
#RequestMapping(value = PATH)
public ModelAndView saveLeadQuery() {
return new ModelAndView("forward:/");
}
#Override
public String getErrorPath() {
return PATH;
}
}
If you don't want to use the HashLocationStrategy, you can add the following controller in your project :
#Controller
public class UiController {
#GetMapping("/")
public String welcome() {
return "index.html";
}
// Match everything without a suffix (so not a static resource)
#GetMapping(value = {
"/{path:[^.]*}",
"/{path:[^.]*}/{path:[^.]*}",
"/{path:[^.]*}/{path:[^.]*}/{path:[^.]*}",
"/{path:[^.]*}/{path:[^.]*}/{path:[^.]*}/{path:[^.]*}",
"/{path:[^.]*}/{path:[^.]*}/{path:[^.]*}/{path:[^.]*}/{path:[^.]*}"
// add more if required ...
})
public String redirect() {
// Forward to home page so that route is preserved.
return "forward:/";
}
}
Have one better way...
You can implement WebMvcConfigurer and add the view controller generic paths.
follow the example:
package net.mypackage.config;
import org.springframework.context.annotation.Configuration; import
org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import
org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import
org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
#Configuration public class MvcConfig implements WebMvcConfigurer {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
String viewName = "forward:/";
registry.addViewController("/{spring:\\w+}")
.setViewName(viewName);
registry.addViewController("/**/{spring:\\w+}")
.setViewName(viewName);
registry.addViewController("/{spring:\\w+}/**{spring:?!(\\.js|\\.css)$}")
.setViewName(viewName);
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
}
}
About the macther: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/util/AntPathMatcher.html

Categories

Resources