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
Related
I have a Spring repository
#RepositoryRestResource
public interface MongoIntegrationTokenRepository extends MongoRepository<IntegrationToken, String>, CrudRepository<IntegrationToken, String> {}
I've added the validation configuration to add validation support and my entity has the validation annotations:
#Configuration
class MyRestMvcConfiguration implements RepositoryRestConfigurer {
private final LocalValidatorFactoryBean localValidatorFactoryBean;
#Autowired
public MyRestMvcConfiguration(LocalValidatorFactoryBean localValidatorFactoryBean) {
this.localValidatorFactoryBean = localValidatorFactoryBean;
}
#Override
public void configureValidatingRepositoryEventListener(
ValidatingRepositoryEventListener validatingRepositoryEventListener) {
validatingRepositoryEventListener.addValidator("beforeCreate", localValidatorFactoryBean);
validatingRepositoryEventListener.addValidator("beforeSave", localValidatorFactoryBean);
validatingRepositoryEventListener.addValidator("beforeSave", localValidatorFactoryBean);
}
}
When running the create operation, if there are any validation errors, the entity creation fails but the JSON response doesn't contain any errors details.
A POST to my endpoint with invalid data simply returns:
{
"message": "Server Error",
"details": [
"Validation failed"
]
}
I'd like to return the validation errors in the RFC7807 format. This should be possible via Spring HATEOAS or by this popular library by Zalando https://github.com/zalando/problem-spring-web but I'm unsure how they need to be wired in or which approach should be used.
I found this lone example on Github. https://github.com/marciovmartins/problem-spring-web-starter-expanded/blob/aed5825c958fad93f4aaad022689958926cf3b4a/src/main/kotlin/com/github/marciovmartins/problem/spring/web/expanded/ProblemExceptionHandler.kt and rewrote it in Java. This seems to do it.
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.data.rest.core.RepositoryConstraintViolationException;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.NativeWebRequest;
import org.zalando.problem.Problem;
import org.zalando.problem.ThrowableProblem;
import org.zalando.problem.spring.web.advice.ProblemHandling;
import org.zalando.problem.violations.Violation;
#ControllerAdvice
public class ProblemControllerAdvice implements ProblemHandling {
#ExceptionHandler
public ResponseEntity<Problem> handleRepositoryConstraintViolationException(RepositoryConstraintViolationException exception, NativeWebRequest request) {
List<Violation> violationList = exception.getErrors().getAllErrors()
.stream()
.map(objectError -> {
if (objectError instanceof FieldError) {
return new Violation(((FieldError) objectError).getField(), objectError.getDefaultMessage());
}
return new Violation(null, objectError.getDefaultMessage());
})
.collect(Collectors.toList());
ThrowableProblem problem = Problem.builder()
.withTitle("Constraint Violation")
.withStatus(defaultConstraintViolationStatus())
.with("violations", violationList)
.build();
return create(problem, request);
}
}
I'm creating simple controller server for spring reactive project. While setting redirection to another location, I have found an error when calling http://localhost:8080/:
There was an unexpected error (type=Internal Server Error, status=500).
ModelAttributeMethodArgumentResolver does not support multi-value reactive type wrapper: interface reactor.netty.http.server.HttpServerResponse
java.lang.IllegalStateException: ModelAttributeMethodArgumentResolver does not support multi-value reactive type wrapper: interface reactor.netty.http.server.HttpServerResponse
at org.springframework.util.Assert.state(Assert.java:94)
at org.springframework.web.reactive.result.method.annotation.ModelAttributeMethodArgumentResolver.resolveArgument(ModelAttributeMethodArgumentResolver.java:112)
at org.springframework.web.reactive.result.method.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:123)
at org.springframework.web.reactive.result.method.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:190)
at org.springframework.web.reactive.result.method.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:133)
at org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerAdapter.lambda$handle$1(RequestMappingHandlerAdapter.java:200)
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:44)
...
This is the controller code:
import reactor.core.publisher.Mono;
import reactor.netty.http.server.HttpServerResponse;
#RestController
public class BaseController {
#GetMapping("/")
public Mono<Void> indexController(HttpServerResponse response) {
return response.sendRedirect("/api/v1");
}
// ...
}
I expected it to be redirected from localhost:8080/ to localhost:8080/api/v1. But I've got the above exception.
Redirecting with Controller MVC good-old approach:
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
import java.net.URI;
#RestController
public class Controller {
#GetMapping("/")
public Mono<Void> indexController(ServerHttpResponse response) {
response.setStatusCode(HttpStatus.PERMANENT_REDIRECT);
response.getHeaders().setLocation(URI.create("/api/v1"));
return response.setComplete();
}
}
If someone would prefer functional Router/Handler approach might investigate: How to redirect a request in spring webflux?.
#Bean
RouterFunction<ServerResponse> routerFunction() {
return route(GET("/"), req ->
ServerResponse.temporaryRedirect(URI.create("/api/v1"))
.build());
}
When using #RestController a ResponseEntity can work as well (here implemented using kotlin coroutines) :
#RestController
class SomeController() {
suspend fun someMethod() : ResponseEntity<Unit> {
return ResponseEntity
.status(HttpStatus.TEMPORARY_REDIRECT)
.location(URI.create("/api/v1"))
.build()
}
}
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.
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")
i'm new in Spring + MVC.
i've found a script and i could run some part of this script.
this script configuring spring mvc with no xml, inside java side.
i put all the jars into WEB-INF/lib.
ControllerConfiguration .java
package org.java.springmvc.bootstrap;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "org.java.springmvc.controller")
public class ControllerConfiguration {
#Bean
public InternalResourceViewResolver configureInternalResourceViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
}
WebAppInitializer.java
package org.java.springmvc.bootstrap;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration.Dynamic;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
public class WebAppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(final ServletContext servletContext) throws ServletException {
final AnnotationConfigWebApplicationContext root = new AnnotationConfigWebApplicationContext();
root.setServletContext(servletContext);
root.scan("org.java.springmvc.bootstrap");
root.refresh();
final Dynamic servlet = servletContext.addServlet("spring", new DispatcherServlet(root));
servlet.setLoadOnStartup(1);
servlet.addMapping("/*");
}
}
HomeController.java
package org.java.springmvc.controller;
import java.io.IOException;
import java.io.Writer;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
#Controller
public class HomeController {
#RequestMapping(value = "/")
public void home(final Writer writer)
throws IOException {
writer.append("<h2>Welcome, XML Free Spring MVC!</h2>");
}
#RequestMapping(value = "/giris")
public void giris(final Writer writer)
throws IOException {
writer.append("Giris");
}
}
FilmController.java
package org.java.springmvc.controller;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.java.springmvc.model.Film;
import org.java.springmvc.model.Film.FilmTurleri;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
#Controller
#RequestMapping("/film")
public class FilmController {
#RequestMapping(value = "filmler")
public void filmler(final Writer writer)
throws IOException {
writer.append("<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=ISO-8859-9\"><title>...Filmler...</title>");
writer.append("<script type=\"text/javascript\" src=\"/js/touch/sencha-touch-all.js\"></script>");
writer.append("<script type=\"text/javascript\" src=\"/js/film/filmler.js\"></script>");
writer.append("</head><body></body></html>");
}
#RequestMapping (value = "/filmleriGetir", method = RequestMethod.GET)
public #ResponseBody Map<String, List<Film>> FilmleriGetir() {
List<Film> movies = new ArrayList<Film>();
// For testing...
movies.add(new Film(0, "Birinci Film", "Birinci Yönetmen", 2015, FilmTurleri.Aksiyon));
movies.add(new Film(0, "İkinci Film", "İkinci Yönetmen", 2015, FilmTurleri.Komedi));
movies.add(new Film(0, "Üçüncü Film", "Üçüncü Yönetmen", 2015, FilmTurleri.Aile));
Map<String, List<Film>> resp = new HashMap<String, List<Film>>();
resp.put("filmListesi", movies);
return resp;
}
}
Film.java
package org.java.springmvc.model;
public class Film {
public int Id;
public String FilmAdi, Yonetmen;
public int CikisTarihi;
public FilmTurleri Turu;
public enum FilmTurleri {
Aksiyon, Komedi, Aile, Korku, Savas;
}
public Film(){
}
public Film(int id, String title, String director, int yearOfRelease, FilmTurleri tur)
{
super();
this.Id = id;
this.FilmAdi = title;
this.Yonetmen = director;
this.CikisTarihi = yearOfRelease;
this.Turu = tur;
}
//getter, settings method
}
i have two questions:
if i write "http://localhost:8080/SpringMVC/", the page displays.
But if i write "http://localhost:8080/SpringMVC/movies/index" i get this warning:
"WARNING: No mapping found for HTTP request with URI [/SpringMVC/WEB-INF/views/index.jsp] in DispatcherServlet with name 'spring'"
if i add a JSP page(Giris.jsp) under WebContent, i cannot display this page. must all page has a mapping? how can i display simple jsp page?
WARNING: No mapping found for HTTP request with URI [/SpringMVC/Giris.jsp] in DispatcherServlet with name 'spring'
EDIT:
i changed a little.
My project structure like this:
i get this error:
Failed to load resource:
http://localhost:8080/js/film/filmler.js
http://localhost:8080/js/touch/sencha-touch-all.js
i thought a logic like that:
- there will be a jsp file including "*.js" files. (filmler.jsp)
- there are some methods returning json object in those *.js files. (FilmleriGetir method)
any advice for this logic?
Regards.
In MovieController.java, you need to add '/'
:
#RequestMapping("/movies")
You are using servlet.addMapping("/*"); which means your org.springframework.web.servlet.DispatcherServlet i.e Spring will intercept every request that comes to your application. Now, you don't have any RequestMapping for Giris.jsp in any controller, so Spring is throwing error as: No mapping found for HTTP request with URI [/SpringMVC/Giris.jsp]
In order to show Giris.jsp page, your need to:
A] Add entry in/ make new controller with RequestMapping for 'Giris.jsp', and set view as 'Giris'
eg:
#Controller
public class MyController {
#RequestMapping(value = "/Giris.jsp")
public void home(final Writer writer)
throws IOException {
return 'Giris';
}
}
You would be better of using RequestMapping as /giris instead of /Giris.jsp, as it exposes that underlying technology is JSP.
B] place Giris.jsp file under /WEB-INF/views/ folder.
Understand how InternalResourceViewResolver works. Taking reference of your ControllerConfiguration, When a view name is returned for controller as 'Giris', InternalResourceViewResolver adds prefix and suffix as defined by you, and that page is shown. So, in case of view name 'Giris', page '/WEB-INF/views/'+ 'Giris' + '.jsp' will be rendered.
According to java naming convention, JSP (file) name should always begin with a lower-case letter. So use giris.jsp instead of Giris.jsp
EDIT(For modified question):
Failed to load resource: http://localhost:8080/js/film/filmler.js
Understand that, as DispatcherServlet is mapped to /*, every request that comes to your web-app, is handled by DispatcherServlet i.e Spring.
Whenever you application comes across url http://localhost:8080/js/film/filmler.js, it knows that DispatcherServlet will handle that url. DispatcherServlet checks if there is any RequestMapping for the url(in controller).
Now, when you add url
http://localhost:8080/js/film/filmler.js
there is no RequestMapping that would handle such kind of url, so you are getting a url.
For loading resources such as js files or image files, use mvc:resources.
eg:
For js files:
Put all your js files in directory /WEB-INF/js/.
Add mvc:resource mapping for js files in you configuration:
<mvc:resources mapping="/js/**" location="/WEB-INF/js/" />
Now you you will be able to access your js files. If Spring comes across url such as /js/film/filmler.js, it will know know from mvc:resource mapping, for where to look for that file.
Goof mvc:resource for tutorials.