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

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

Related

XMLHttpRequest at xxx from origin xxx has been blocked by CORS: No 'Access-Control-Allow-Origin' header

hi I am working on spring boot, angular 8, and mongodb. I am facing the error
Access to XMLHttpRequest at 'http://localhost:8080/employee/activeemployeesummary' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource
when i test same code on postman it work perfectly fine, however it don't work angular, and because chrome using the CORS policy.
My code:
package com.sani.springbootrestfulapi;
public class SpringBootMongoApplication extends SpringBootServletInitializer {
public static void main(String args[]) {
SpringApplication.run(SpringBootMongoApplication.class, args);
}
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS", "HEAD", "PATCH")
.allowedHeaders("Origin, X-Requested-With, Content-Type, Accept")
.allowedOrigins("http://localhost:4200");
}
};
}
}
below: employee controller code
package com.sani.springbootrestfulapi.controller;
#RestController
#RequestMapping("employee")
public class EmployeeController {
#Autowired
private EmployeeService empService;
#Autowired
private OrganizationService organizationService;
#PostMapping("/save")
public ResponseEntity<EmployeeEntity> save(#RequestBody EmployeeEntity emp) {
if (empService.findByrNumber(emp.getrNumber()))
return new ResponseEntity<EmployeeEntity>(HttpStatus.FOUND);
else {
organizationService.joinOrganization(emp);
return new ResponseEntity<EmployeeEntity>(HttpStatus.OK);
}
}
#PutMapping("/update") /* here we need to pass id, the spring will consider as update */
public ResponseEntity<EmployeeEntity> update(#RequestBody EmployeeEntity emp) {
EmployeeEntity employee = empService.getOne(emp.getId());
if (employee != null) {
organizationService.joinOrganization(emp);
return new ResponseEntity<EmployeeEntity>(HttpStatus.OK);
} else
return new ResponseEntity<EmployeeEntity>(HttpStatus.NOT_FOUND);
}
#GetMapping("/activeemployeesummary")
public List<EmployeeEntity> getActiveEmployeeSummary() {
List<EmployeeEntity> employee = new ArrayList<>();
empService.getActiveEmployeeSummary().forEach(employee::add);
return employee;
}
#GetMapping("/inactiveemployeesummary")
public List<EmployeeEntity> getInactiveEmplo`enter code here`yeeSummary() {
List<EmployeeEntity> employee = new ArrayList<>();
empService.getInactiveEmployeeSummary().forEach(employee:`enter code here`:add);
return employee;
}
}
Add this #Bean in your #Configuration or your main class.
#Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(
Arrays.asList("GET","POST","HEAD","DELETE","PUT","OPTIONS"));
configuration.setMaxAge(1l);
configuration.setAllowCredentials(true);
configuration.setAllowedHeaders(Arrays.asList("*"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
I think you just missed that header =>
Access-Control-Allow-Origin: *

Spring Swagger 2 not working when i have a custom HandlerInterceptorAdapter

Im using a spring boot 1.5.3 along side swagger 2.7.0 (can't upgrade because higher versions dont work on IE)
i got everything to work fine but when i add a custom HandlerInterceptorAdapter its not working giving me this error:
Cannot read property 'validatorUrl' of null springfox.js: 72
here is my swagger config
#Configuration
#EnableSwagger2
public class SwaggerConfig {
public Docket api() {
Docket docket = new Docket(DocumentationType.SWAGGER_2).select()
.apis(RequestHandlerSelectors
.basePackage("net.guides.springboot2.springboot2swagger2.controller"))
.paths(PathSelectors.regex("/.*"))
.build().apiInfo(apiEndPointsInfo());
docket.ignoredParameterTypes(HttpServletResponse.class, HttpServletRequest.class); // this didnt help
return docket;
}
private ApiInfo apiEndPointsInfo() {
return new ApiInfoBuilder().title("SWAT Rest API")
.description("SWAT Rest API documentation")
.contact(new Contact("xxx", "xxxx", "xxxxx"))
.license("Apache 2.0")
.licenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html")
.version("5.2.2")
.build();
}
}
and here is the config that gives me a hard time
#Configuration
#Slf4j
public class SwatStaticResourceConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
log.debug("Adding static folders to serve images");
// registry.addResourceHandler("/images/**").addResourceLocations("file:./images/");
//registry.addResourceHandler("/docs/**").addResourceLocations("file:./docs/");
registry.addResourceHandler("/images/**", "/docs/**").addResourceLocations("file:./images/", "file:./docs/");
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new SwatRequestInterceptor()); // if i disable this line than swagger is working fine
super.addInterceptors(registry);
}
}
in case its needed here is the interceptor class
#Slf4j
public class SwatRequestInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
Principal user = request.getUserPrincipal();
if (user == null) {
log.error("No user for request: " + request.getRequestURL().toString());
return false;
} else {
log.debug("Got the following request: " + request.getMethod().toUpperCase() + " " + request.getRequestURL().toString() + ", FROM: " + user.getName());
return true;
}
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
Principal user = request.getUserPrincipal();
log.debug("Completed the following request: " + request.getMethod().toUpperCase() + " " + request.getRequestURL().toString() + ", FROM: " + user.getName());
super.postHandle(request, response, handler, modelAndView);
}
}
I was getting same issue but Resolved it by making sure that WebMvcConfigurationSupport is extended only by a single class preferably where you are registering your interceptor.
Also, make sure to add the following for swagger config class:
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}

Spring #CrossOrigin does not work with DELETE method

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");
}
};
}

How to dynamically set #Bean at #Configuration in #Service?

I use spring cloud & feign client with my app.And I want to set the param 'accept-language' to headers when call feign clients.
I found the similar questions at [Using #Headers with dynamic values in Feign client + Spring Cloud (Brixton RC2)
Ask]1,but I don't know how to set header param.Here is my code:
I set MyDefaultFeignConfig at app.java
#EnableFeignClients(basePackages = {defaultConfiguration = MyDefaultFeignConfig.class)
And MyDefaultFeignConfig.java :
#Configuration
public class MyDefaultFeignConfig {
private String requestLanguage = "zh";
#Bean
RequestInterceptor feignRequestInterceptor() {
return new RequestInterceptor() {
#Override
public void apply(RequestTemplate template) {
template.header("accept-language", requestLanguage);
}
};
}
//doesn't work
public static void updateBean(String requestLanguage) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyDefaultFeignConfig.class);
try {
System.out.println(applicationContext.getBean("feignRequestInterceptor"));
} catch (NoSuchBeanDefinitionException e) {
System.out.println("Bean not found");
}
BeanDefinitionRegistry beanFactory = (BeanDefinitionRegistry) applicationContext.getBeanFactory();
beanFactory.registerBeanDefinition("feignRequestInterceptor",
BeanDefinitionBuilder.genericBeanDefinition(String.class)
.addConstructorArgValue(new RequestInterceptor() {
#Override
public void apply(RequestTemplate template) {
template.header("accept-language", requestLanguage);
}
})
.getBeanDefinition()
);
}
}
My Gateway controller is :
#Autowired
private LeaseOrderRemoteService leaseOrderRemoteService;
#RequestMapping(value = "/Discovery/order/unifiyInit", method = RequestMethod.GET)
public Message unifiyOrderInit(#RequestHeader("accept-language") String language) {
MyDefaultFeignConfig.updateBean(language);
return leaseOrderRemoteService.unifiyOrderInit();
}
My feign clients Controller is:
public Message unifiyOrderInit(#RequestHeader("accept-language") String language) {
//...
}
And I can only get the value of "accept-language" as MyDefaultFeignConfig config the first time set #Bean.How can I set the value of "accept-language" from Gateway to feign client.Please help me,thinks! Any suggestions are grateful and best regards!

Controller component issue with Spring and SockJs

I have a problem with a configuration of a WebSocket using Spring and SockJs.
I have configured all my app like Spring Documentation, the connection seems to be fine, but when I send a message the Controller is never involved and it doesn't work.
This is the Configuration component:
#Configuration
#EnableWebMvc
#EnableWebSocketMessageBroker
public class MyWebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer implements WebSocketConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/mySocket").withSockJS().setInterceptors(new MySocketHandshakeInterceptor());;
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/app").enableSimpleBroker("/topic");
}
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
}
}
This is the HandshakeInterceptor implementation:
public class MySocketHandshakeInterceptor implements HandshakeInterceptor {
#Override
public void afterHandshake(ServerHttpRequest paramServerHttpRequest, ServerHttpResponse paramServerHttpResponse, WebSocketHandler paramWebSocketHandler, Exception paramException) {
// TODO Auto-generated method stub
System.out.println("MySocketHandshakeInterceptor afterHandshake");
}
#Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> paramMap)
throws Exception {
System.out.println("MySocketHandshakeInterceptor beforeHandshake");
return true;
}
}
This is the Controller component:
#Controller
public class MySocketController {
#MessageMapping("/testsocket")
#SendTo("/topic/testresponse")
public MessageOut test(MessageIn message) throws Exception {
System.out.println("MySocketController start");
System.out.println("MySocketController test "+ message);
return new MessageOut("test OK");
}
}
These are MessageIn and MessageOut:
public class MessageIn {
private String test;
/**
* #return the test
*/
public String getTest() {
return test;
}
}
public class MessageOut {
private String result;
public MessageOut(String result) {
super();
this.result = result;
}
/**
* #return the test
*/
public String getResult() {
return result;
}
/**
* #param result the result to set
*/
public void setResult(String result) {
this.result = result;
}
}
Finally, this is the client side (javascript):
var socketSession = {};
connectToMySocket();
function connectToVideoSocket() {
socketSession.socket = new SockJS("/mySocket");
socketSession.socket.stomp = Stomp.over(socketSession.socket);
socketSession.socket.stomp.debug = null;
socketSession.socket.stomp.connect({}, function () {
socketSession.socket.stomp.subscribe("/topic/testresponse", function (data) {
console.log(data);
});
});
}
This is the command that I launch to test the socket:
socketSession.socket.stomp.send("app/testsocket", {}, JSON.stringify({'test': 'test'}));
In the system out console I can only see the two rows:
MySocketHandshakeInterceptor beforeHandshake
MySocketHandshakeInterceptor afterHandshake
The interceptor works fine, but I don't see any print by the Controller component.
What's wrong?
Thanks.
I resolved by myself.
It was a problem of the client-side.
The correct script is:
var mySocket = undefined;
var stompClient = undefined;
connectToVideoSocket();
function connectToVideoSocket() {
mySocket = new SockJS('/mySocket');
stompClient = Stomp.over(mySocket);
stompClient.debug = null;
stompClient.connect({}, function(frame) {
stompClient.subscribe('/topic/testresponse', function(data){
console.log(JSON.parse(data.body).result);
});
});
}
In this way it works fine.

Resources