Spring #CrossOrigin does not work with DELETE method - spring

Spring #CrossOrigin annotation does not work with DELETE methods.
Example code (in Groovy):
#CrossOrigin
#RestController
#RequestMapping('/rest')
class SpringController {
#RequestMapping(value = '/{fileName}', RequestMethod.DELETE)
void deleteFile(#PathVariable fileName) {
// logic
}
}
For this code I get the exception:
XMLHttpRequest cannot load http://localhost:8080/rest/filename.txt. No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:4200' is therefore not allowed
access. The response had HTTP status code 404.
Notes:
I tested it in Chrome 58 and Postman 4.10.7
According to https://spring.io/guides/gs/rest-service-cors/ by
default #CrossOrigin allows only GET, HEAD and POST cross-origin
requests. Although specifying #CrossOrigin(methods =
[RequestMethod.GET, RequestMethod.DELETE]) did not help
I omitted some code for brevity. Actual controller also has GET request by the same mapping, delete method has return type and produces JSON response, and other minor stuff that I don't think affects the issue.

#Configuration
public class AppConfig extends WebMvcConfigurerAdapter {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("your cross origin url")
.allowedOrigins("your cross origin host/url")
.allowedHeaders("Access-Control-Allow-Origin", "*")
.allowedHeaders("Access-Control-Allow-Headers", "Content-Type,x-requested-with").maxAge(20000)
.allowCredentials(false)
.allowedMethods("DELETE");
}
}
// in your controller
#RequestMapping(value = '/{fileName:.+}', RequestMethod.DELETE)
void deleteFile(#PathVariable fileName) {
// your custom logic
}

#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "PUT", "POST", "PATCH", "DELETE", "OPTIONS");
}
};
}

Related

in spring boot 2.3.3, facing issue regarding CORS. no spring security has been used

"has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource."
getting this message when api is being hit by angular portion, but proper response is obtained when end point is being hit from POSTMAN.
You will need to enable/configure CORS (Cross-Origin Resource Sharing) in your spring boot application -
Enabling CORS for the whole application (This is a global configuration)-
#Configuration
#EnableWebMvc
public class WebConfiguration extends WebMvcConfigurerAdapter {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
}
Additionally Spring allows you to control CORS configuration at Controller as well as at the RequestMapping level -
To enable CORS for the whole controller -
#CrossOrigin(origins = "http://your-client-domain.com", maxAge = 3600)
#RestController
#RequestMapping("/booking")
public class BookingController {
#RequestMapping("/{id}")
public Booking retrieve(#PathVariable Long id) {
// ...
}
#RequestMapping(method = RequestMethod.DELETE, path = "/{id}")
public void remove(#PathVariable Long id) {
// ...
}
}
To enable the CORS for specific path mapping (RequestMapping) -
#RestController
#RequestMapping("/booking")
public class BookingController {
#CrossOrigin(origins = "http://your-client-domain.com")
#RequestMapping("/{id}")
public Booking retrieve(#PathVariable Long id) {
// ...
}
#RequestMapping(method = RequestMethod.DELETE, path = "/{id}")
public void remove(#PathVariable Long id) {
// ...
}
}
You can also apply CORS configuration only for required mappings (paths), you can always configure it to the granular details -
#Configuration
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/v1/**")
.allowedOrigins("http://your-client-domain.com")
.allowedMethods("OPTIONS", "GET", "POST", "PUT", "DELETE")
.allowedHeaders("Some-Header-x", "Authorization")
.exposedHeaders("X-API-Limit")
.allowCredentials(false).maxAge(3600);
}
}
You can read more about enabling and configuring CORS in Spring Applications here

Request Post not allowed for controller, but get works

I have a spring-boot with a cors. I want set a static page controller,
I want return a static html page for same post/get request.
The get request works, but with the post request i have :
{
"timestamp": 1563296215801,
"status": 405,
"error": "Method Not Allowed",
"message": "Request method 'POST' not supported",
"path": "/test"
}
My code :
#Controller
#RequestMapping("/")
public class StaticPageController implements WebMvcConfigurer {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/test").setViewName(
"test");
}
#RequestMapping(value = "test", method = { RequestMethod.GET,
RequestMethod.POST })
public String test() {
return "test";
}
#Bean
public ViewResolver getViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("folder/");
resolver.setSuffix(".html");
return resolver;
}
#Override
public void configureDefaultServletHandling(
DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
With the post request, my breakpoint in 'return "test";' is fired. But after i have a 405 error...
Any ideas ?
You are facing the issue because along with configuring the GET endpoint for /test , you have also configured the ViewControllerRegistry to return the view test to /test url.
The ViewControllerRegistry is configured whenever you want to serve a static content without the need of a controller. So , try removing that and your controller method should work as expected because by default only GET is supported by ViewControllerRegistry.
Assists with the registration of simple automated controllers pre-configured with status code and/or a view. ViewControllerRegistry
registers view controller.
We need not to create actual controller class when using ViewControllerRegistry. It is used when we just need to map a URL
with a view. addViewController(String urlPath): It adds a view
controller for the given URL.
This method returns ViewControllerRegistration and using ViewControllerRegistration.setViewName() a corresponding view is
mapped. addRedirectViewController(String urlPath, String
redirectUrl): It maps a URL to another URL to redirect on it. It has
been introduced in spring 4.1.
addStatusController(String urlPath, HttpStatus statusCode): It maps a URL with given status code. It has also been introduced in spring
4.1.
Try using :
#Controller
#RequestMapping("/")
public class StaticPageController implements WebMvcConfigurer {
#RequestMapping(value = "test", method = { RequestMethod.GET,
RequestMethod.POST })
public String test() {
return "test";
}
#Bean
public ViewResolver getViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("folder/");
resolver.setSuffix(".html");
return resolver;
}
#Override
public void configureDefaultServletHandling(
DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}

spring REST RequestMethod how to Map a http LOCK and UNLOCK RequestMapping?

seems that this is the same as Custom HTTP Methods in Spring MVC
I need to implement a call with http method LOCK and UNLOCK.
currently spring's requestMethod only supports
public enum RequestMethod {
GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
}
how can I implement a #RestController method that is called if the spring app is called with LOCK and UNLOCK http directives?
You can do it using supported method ( GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE) call. Here is the way to call post method internally:
#Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
#Override
#Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
final RequestMappingHandlerAdapter requestMappingHandlerAdapter = super.requestMappingHandlerAdapter();
requestMappingHandlerAdapter.setSupportedMethods(
"LOCK", "GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "TRACE"
); //here supported method
return requestMappingHandlerAdapter;
}
#Bean
DispatcherServlet dispatcherServlet() {
return new CustomHttpMethods();
}
}
Custom method handler class. Here call post method internally:
public class CustomHttpMethods extends DispatcherServlet {
#Override
protected void service(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
if ("LOCK".equals(request.getMethod())) {
super.doPost(request, response);
} else {
super.service(request, response);
}
}
}
Now you do requestmapping below way:
#RequestMapping(value = "/custom")
ResponseEntity customHttpMethod(){
return ResponseEntity.ok().build();
}

Access-Control-Allow-Origin error with DELETE, while working fine with GET / POST

BackEnd is Spring, I'v configured CORS like this
#SpringBootApplication
public class App {
public static void main(String args[]){
SpringApplication.run(App.class, args);
}
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*");
}
};
}
}
Now I got following code in Controller
#PostMapping("/add")
public ProductDto addProduct(#Valid #RequestBody ProductDto productDto){
return productService.addProduct(productDto);
}
#RequestMapping(path="/remove/{id}", method=RequestMethod.DELETE)
#ResponseBody
public String removeProduct(#PathVariable Long id) {
return productService.removeProduct(id);
}
And from Angular 6 FrontEnd I'm calling those 2 endpoints
let httpHeaders = new HttpHeaders({
'Content-Type' : 'application/json',
});
let options = {
headers: httpHeaders
};
addProduct() {
const product = new Product();
product.name = this.productNameValue;
product.categoryName = this.categoryValue;
product.kcal = this.caloriesValue;
product.protein = this.proteinValue;
product.fat = this.fatValue;
product.carb = this.carbsValue;
this.http.post('http://localhost:8080/product/add', JSON.stringify(product), options).subscribe(data => this.populateProductTable());
}
removeProduct(x: any) {
const url = 'http://localhost:8080/product/remove/' + x.id;
this.http.delete(url, options).subscribe(data => console.log(data));
}
First one (and similar GET method) works fine, when I try to use DELETE, I got
Failed to load http://localhost:8080/product/remove/2: Response to
preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:4200' is therefore not allowed
access.
You need to add DELETE http-verb
For Spring Web MVC
#Configuration
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedMethods("HEAD", "GET", "PUT", "POST", "DELETE", "PATCH");
}
}
For Spring Boot:
#Configuration
public class MyConfiguration {
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedMethods("HEAD", "GET", "PUT", "POST", "DELETE", "PATCH");
}
};
}
}
To know how CORS works with spring, refer:
https://spring.io/blog/2015/06/08/cors-support-in-spring-framework#javaconfig
Spring security CORS Filter

Spring boot single page application - forward every request to index.html

I have a Spring Boot (v1.3.6) single page application (angular2) and i want to forward all request to the index.html.
A request to http://localhost:8080/index.html is working (200 and i get the index.html) but http://localhost:8080/home is not (404).
Runner.class
#SpringBootApplication
#ComponentScan({"packagea.packageb"})
#EnableAutoConfiguration
public class Runner {
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext run = SpringApplication.run(Runner.class, args);
}
}
WebAppConfig.class
#Configuration
#EnableScheduling
#EnableAsync
public class WebAppConfig extends WebMvcConfigurationSupport {
private static final int CACHE_PERIOD_ONE_YEAR = 31536000;
private static final int CACHE_PERIOD_NO_CACHE = 0;
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.setOrder(-1);
registry.addResourceHandler("/styles.css").addResourceLocations("/styles.css").setCachePeriod(CACHE_PERIOD_ONE_YEAR);
registry.addResourceHandler("/app/third-party/**").addResourceLocations("/node_modules/").setCachePeriod(CACHE_PERIOD_ONE_YEAR);
registry.addResourceHandler("/app/**").addResourceLocations("/app/").setCachePeriod(CACHE_PERIOD_NO_CACHE);
registry.addResourceHandler("/systemjs.config.js").addResourceLocations("/systemjs.config.js").setCachePeriod(CACHE_PERIOD_NO_CACHE);
registry.addResourceHandler("/**").addResourceLocations("/index.html").setCachePeriod(CACHE_PERIOD_NO_CACHE);
}
}
styles.css, /app/third-party/xyz/xyz.js,.. are working (200 and i get the correct file). Only /** to index.html is not working.
You can also add a forwarding controller like:
#Controller
public class ForwardingController {
#RequestMapping("/{path:[^\\.]+}/**")
public String forward() {
return "forward:/";
}
}
The first part {path:[^\\.]+} matches one or more of any character other than .. This makes sure request for a file.ext doesn't get handled by this RequestMapping. If you need to support sub-paths to also be forwarded, put /** outside of the {...}.
This one didn't work for me:
return "forward:/";
Thanks to Spring MVC #RestController and redirect I found a nicely working solution:
#RequestMapping(value = "/{[path:[^\\.]*}")
public void redirect(HttpServletResponse response) throws IOException {
response.sendRedirect("/");
}
Without looking at logs I'm not entirely sure why its not being mapped correctly, however if you want to map URLs to a view (HTML) then you will probably be better off using the viewController mechanism spring provides http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/mvc.html#mvc-config-view-controller. e.g.
#Configuration
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("home");
}
}
(taken from spring docs linked above - this is how you should map a url to a view rather than re-purposing the mapping for static resources.)
I'm not sure if there is any kind of suffix filtering for the resource mapping - e.g. I don't know how spring decides to map requests to the ResourceHttpRequestHandler - have you tried (just to confirm or deny) whether something like http://localhost:8080/home.html amps to anything?
It's also possible that the html mapping you have defined above is just being ignored and the index.html is just working because of Spring-Boot's default home page behaviour: https://github.com/spring-projects/spring-boot/blob/master/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ResourceProperties.java#L108
I had the same problem and the following worked for me. My html files are inside src/main/resources/static/app
The key was to remove #EnableWebMvc and add "classpath:/static/app/" to addResourceLocations! Hope this helps.
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
"classpath:/META-INF/resources/", "classpath:/resources/",
"classpath:/static/","classpath:/static/app/", "classpath:/public/" };
#Bean
public WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!registry.hasMappingForPattern("/webjars/**")) {
registry.addResourceHandler("/webjars/**").addResourceLocations(
"classpath:/META-INF/resources/webjars/");
}
if (!registry.hasMappingForPattern("/**")) {
registry.addResourceHandler("/**").addResourceLocations(
CLASSPATH_RESOURCE_LOCATIONS);
}
}
#Override
public void addViewControllers(ViewControllerRegistry registry) {
// forward requests to /admin and /user to their index.html
registry.addViewController("/portal").setViewName(
"forward:/app/index.html");
}
};
}

Resources