Reputation: 4984
I am trying to send a request from my Webstorm application to my backend application, which both are at different ports, I am working with angularJS in the front end and java in backend. I have read up a bit about CORS Filters and I learned that in order to do Cross Origin Requests I need to implement these. However, after doing this my error, being
Failed to load resource: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:63343' is therefore not allowed access. http://localhost:8080/register?password=&username=
XMLHttpRequest cannot load http://localhost:8080/register?password=&username=. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:63343' is therefore not allowed access.
did not change at all which led me to believe I have done something wrong, here is the code from which I am sending the request:
var charmanderServices = angular.module('charmanderServices', ['ngResource']);
var hostAdress = "http://localhost:8080";
charmanderServices.factory("register", ["$resource",
function($resource){
console.log('in service');
return $resource(hostAdress + "/register", {}, {
'registerUser' : { method: 'POST', isArray: false,
params: {
username: '@username',
password: '@password'
}
},
headers : {'Content-Type' : 'application/x-www-form-urlencoded'}
});
}
]);
My corsFilter is written like this:
@Component
public class SimpleCORSFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
//This is not even printing
System.out.println("Cheers lads, I'm in the filter");
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "x-requested-with, X-Auth-Token, Content-Type");
chain.doFilter(req, res);
}
public void init(FilterConfig filterConfig) {}
public void destroy() {}
}
This is my web.xml:
<web-app version="3.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_1.xsd">
<display-name>Project</display-name>
<!-- Load Spring Contexts -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- CORS Filter -->
<filter>
<filter-name>cors</filter-name>
<filter-class>com.robin.filters.SimpleCORSFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>cors</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:/spring/applicationContext.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
This is the controller where I am catching the request:
@Controller
public class UserController {
@Autowired
private UserService userService;
@RequestMapping(value = "/register" , method= RequestMethod.POST, produces = "application/json")
@ResponseBody
public boolean register(@RequestParam(value = "username") String username, @RequestParam(value = "password") String password){
System.out.println("Im in register hurray");
return userService.register(username, password);
}
}
Update: I have tried implementing the filter as a OncePerRequestFilter, still doesn't work. Is anyone able to help me further here?
Update#2: Also tried this one, http://software.dzhuvinov.com/cors-filter-installation.html, no luck
Update#3: This was my output in the console, I can see that the response did not add any headers:
Request URL:http://localhost:8080/register?password=g&username=g
Request Method:OPTIONS
Status Code:200 OK
Request Headersview source
Accept:*/*
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8,nl;q=0.6
Access-Control-Request-Headers:accept, content-type
Access-Control-Request-Method:POST
Connection:keep-alive
Host:localhost:8080
Origin:http://localhost:63343
Referer:http://localhost:63343/Project/index.html?uName=g&uPassword=g&uPasswordConfirm=g
User-Agent:Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.152 Safari/537.36
Query String Parametersview sourceview URL encoded
password:g
username:g
Response Headersview source
Allow:GET, HEAD, POST, PUT, DELETE, OPTIONS
Content-Length:0
Date:Fri, 04 Apr 2014 09:50:35 GMT
Server:Apache-Coyote/1.1
Update#4: Annotated the filter with @WebFilter instead of @Component, didn't help
Update#5: Here is my applicationContext.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:component-scan base-package="com.robin"/>
<mvc:annotation-driven/>
<!-- Hibernate Session Factory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="packagesToScan">
<array>
<value>com.robin.model</value>
</array>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">create</prop>
</props>
</property>
<property name="annotatedClasses">
<list>
<value>com.robin.model.User</value>
</list>
</property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<!--Driver for mysqldb -->
<import resource="mysql-context.xml"/>
</beans>
I also added the code from my controller and register.html file here:
charmanderControllers.controller('registerController', ['$scope', 'register',
function($scope, register){
$scope.username = '';
$scope.password = '';
$scope.register = function () {
register.registerUser({'username': $scope.username, 'password': $scope.password}).$promise.then(function(data){
switch(data.response){
case true:
//succes
$scope.registered = true;
$scope.userExists = false;
break;
case false:
//user exists
$scope.registered = false;
$scope.userExists = true;
break;
}
console.log(data.response);
})
};
$scope.checkValidRegister = function (invalid) {
console.log(invalid);
console.log($scope.passwordConfirm);
console.log($scope.password);
console.log($scope.username);
if (invalid || $scope.password != $scope.passwordConfirm) {
console.log("I shouldnt be here");
$scope.validation = true;
if ($scope.password != $scope.passwordConfirm) {
$scope.passwordError = true;
}
} else {
register();
}
};
}]);
register.html
<h1>Register now!</h1>
<form method="post" class="register" novalidate>
<p>
<label for="email">E-mail:</label>
<input type="text" name="login" id="email" placeholder="E-mail address..." required ng-model="username">
</p>
<p>
<label for="password">Password:</label>
<input type="password" name="password" id="password" placeholder="Password..."
required ng-model="password">
</p>
<p>
<label for="confirm_password">Confirm password: </label>
<input type="password" name="confirm_password" id="confirm_password" placeholder="Confirm password..."
required ng-model="passwordConfirm">
<span ng-show="passwordError">Passwords do not match!</span>
</p>
<p class="register_submit">
<button type="submit" class="register-button" ng-click="checkValidRegister()">Register</button>
</p>
</form>
Upvotes: 32
Views: 82227
Reputation: 7998
Your code and configuration looks good in general and I was able to run it on local environment. Please remove @Component annotation from your SimpleCORSFilter
, because you use it as a plain Servlet Filter and it doesn't need to be a part of Spring context.
UPD. Tomcat 7 has its own CORS filter implementation. You can check documentation and source code for more details. I have modified the headers to reflect its default configuration, should work as expected now.
Since you are already using Spring and if you are using Spring 4.2 or higher, you dont need CorsFilter
, but can simply annotate your controller method with CrossOrigin
. Here is the excellent article on this, worth a read.
Please also check nice resource which describes CORS configuration for different platforms.
Here is my working example using plain Filter implementation:
Controller:
@Controller
public class UserController {
private static Logger log = Logger.getAnonymousLogger();
@RequestMapping(
value = "/register",
method = RequestMethod.POST,
consumes = "application/x-www-form-urlencoded")
@ResponseBody
public String register(@RequestParam(value = "user") String username,
@RequestParam(value = "password") String password) {
log.info(username + " " + password);
return "true";
}
}
Filter:
public class CorsFilter implements Filter {
private static final Logger log = Logger.getAnonymousLogger();
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
log.info("Adding Access Control Response Headers");
HttpServletResponse response = (HttpServletResponse) servletResponse;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, HEAD, OPTIONS");
response.setHeader("Access-Control-Allow-Headers", "Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers");
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}
Filter mapping in web.xml:
<filter>
<filter-name>cors</filter-name>
<filter-class>com.udalmik.filter.CorsFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>cors</filter-name>
<url-pattern>/register</url-pattern>
</filter-mapping>
JS to perform request from separate web app (JQuery):
$(document).ready(function() {
$('#buttonId').click(function() {
$.ajax({
type: "POST",
url: "http://localhost:8080/register",
success : function(data){
console.log(data);
},
data : {
user : '[email protected]',
password : 'password'
}
});
}
}
Upvotes: 35