4

I'm trying to POST a JSON object to my Spring MVC controller, but I only receive an Access-Control-Allow-Origin error.

My controller:

@RequestMapping(value= "/add", method = RequestMethod.POST, headers = {"content-type=application/json"})
public @ResponseBody Reponse addUser(Model model, @RequestBody @Valid @ModelAttribute("user") User user, BindingResult result) {
    if (result.hasErrors()) {
        Reponse error = new Reponse();
        // etc......
        return error;
    } else {
        return service.addUser(user);
    }
}

My Zepto POST:

this.addUser = function (valeur, callback) {
    $.ajax({
        type: 'POST',
        url: 'http://127.0.0.1:8080/AgenceVoyage/user/add',
        data: JSON.stringify({"mail" : "toto@toto.fr" , "password" : "titi"}),
        dataType: "json",
        contentType: "application/json",

        success: function(data) {
            if(data.reponse == "OK") {
                window.location = "main.html";
            } else {
                alert("PROBLEM");
            }
        },

        error: function(xhr, type) {
            alert("ERROR");
        }
    });
};

I tried with no stringify in POST request, with no headers in @RequestMapping.

My result:

OPTIONS http://127.0.0.1:8080/AgenceVoyage/user/add No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:9000' is therefore not allowed access. zepto.min.js:2 XMLHttpRequest cannot load

Lombric
  • 830
  • 2
  • 11
  • 23
  • Please check this answer. http://stackoverflow.com/questions/24098132/no-access-control-allow-origin-header-is-present-on-the-requested-resource/25707820#25707820 – Aalkhodiry Sep 07 '14 at 07:47

4 Answers4

8

I found the solution :

Firstly, create a new filter, which will set the header response :

    @Component
public class SimpleCORSFilter implements Filter {

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        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");
        chain.doFilter(req, res);
    }

    public void init(FilterConfig filterConfig) {}

    public void destroy() {}

}

After that, in your web.xml add those lines :

 <filter>
  <filter-name>cors</filter-name>
  <filter-class>MY.PACKAGE.SimpleCORSFilter</filter-class>
 </filter>

 <filter-mapping>
  <filter-name>cors</filter-name>
  <url-pattern>/*</url-pattern>
 </filter-mapping>

and that all !

Lombric
  • 830
  • 2
  • 11
  • 23
2

This is because the page is being loaded from a server in http://localhost:9000, and this page is trying to access via an ajax request a different domain http://127.0.0.1:8080/AgenceVoyage/user/add.

Notice that the different port number makes these two urls correspond to two different domains, even tough they are both localhost urls.

Because the two are considered different domains, the browser ajax Same Origin Policy kicks in and prevents the request from being executed due to security reasons (ajax requests against third party domains are not allowed).

See here for more details on the ajax Same Origin Policy, specially the origin determination policy section where it's mentioned that the port number is part of the origin.

Angular University
  • 42,341
  • 15
  • 74
  • 81
  • the simplest is to server the page from the same domain. using a frontend server like nginx or apache the WARs can still run on separate servers. JSONP is usually for public available data like maps, as its hard to secure http://security.stackexchange.com/questions/23438/security-risks-with-jsonp. CORS is the modern alternative http://enable-cors.org/ – Angular University Feb 19 '14 at 08:07
  • This is a part of the solution, you can adopt an alternative on your Spring server, I detailed it in the next response. – Lombric Feb 19 '14 at 12:17
0

If your application is configured programmatically, then you need next code in your WebApplicationInitializer implementation instead of 'xml':

public class WebAppInitializer implements WebApplicationInitializer {

  @Override
  public void onStartup(ServletContext servletContext) {
      ... // your context configuration like in Spring.io tutorial

     // Register filter to allow cross-domain requests
     registerServletFilter(servletContext, new SimpleCORSFilter());
  }

  ...

  protected FilterRegistration.Dynamic registerServletFilter(ServletContext servletContext, Filter filter) {
    String filterName = Conventions.getVariableName(filter);
    FilterRegistration.Dynamic registration = servletContext.addFilter(filterName, filter);
    registration.setAsyncSupported(true);
    registration.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType.class), false, "/*");
    return registration;
  }
}

if the filter is

@Component
public class SimpleCORSFilter implements Filter {

  public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
    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", "Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept");
    chain.doFilter(req, res);
  }

  public void init(FilterConfig filterConfig) {}

  public void destroy() {}

}
Alexander Volkov
  • 7,904
  • 1
  • 47
  • 44
0

Insert this annotation @CrossOrigin in class of controller

@CrossOrigin(origin = "http://domain2.com", maxAge = 3600)
@RestController
@RequestMapping("/account")
public class UserController {

}

Font: https://dzone.com/articles/cors-support-spring-framework