Tomcat 8, Ajax (jQuery), Jersey, REST API does not work, enable CORS

Asked

Viewed 641 times

3

I have been trying for a while to configure CORS to work with my Tomcat 8. But whenever I have to submit the POST request, I get the error message:

"Response to preflight request doesn’t pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested Resource. Origin 'http://rafael.etc' is therefore not allowed access."

web.xml:

 <display-name>apirestex</display-name>
    <servlet>

        <servlet-name>jersey-serlvet</servlet-name>
        <servlet-class>
            org.glassfish.jersey.servlet.ServletContainer
        </servlet-class>

        <init-param>
             <param-name>jersey.config.server.provider.packages</param-name>
             <param-value>br.com.resource.api</param-value>
        </init-param>

        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>jersey-serlvet</servlet-name>
        <url-pattern>/servlet</url-pattern>
    </servlet-mapping>

    <filter>

        <filter-name>CORS</filter-name>
        <filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>

        <init-param>
           <param-name>cors.allowOrigin</param-name>
           <param-value>/*</param-value>
        </init-param>

        <init-param>
           <param-name>cors.supportedHeaders</param-name>
           <param-value>*</param-value>
        </init-param>

    </filter>

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

pom.xml:

  <dependencies>

    <!-- https://mvnrepository.com/artifact/com.sun.jersey/jersey-server -->
    <dependency>
        <groupId>com.sun.jersey</groupId>
        <artifactId>jersey-server</artifactId>
        <version>1.9</version>
    </dependency>


    <!-- https://mvnrepository.com/artifact/javax.ws.rs/javax.ws.rs-api -->
    <dependency>
        <groupId>javax.ws.rs</groupId>
        <artifactId>javax.ws.rs-api</artifactId>
        <version>2.0</version>
    </dependency>

    <dependency>
       <groupId>com.google.code.gson</groupId>
       <artifactId>gson</artifactId>
       <version>2.8.0</version>
    </dependency>

    <dependency>
        <groupId>com.thetransactioncompany</groupId>
        <artifactId>java-property-utils</artifactId>
        <version>1.9.1</version>
    </dependency>

    <dependency>
        <groupId>com.thetransactioncompany</groupId>
        <artifactId>cors-filter</artifactId>
        <version>1.9.1</version>
    </dependency>

  </dependencies>

Resource:

import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;


@Path("/usuarios")
public class UsuarioResource {

    @Path("/save")
    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    public void save(String conteudo){

        //Usuario usuario  = Usuario.jsonToUser(conteudo);

        System.out.println(conteudo);

        /*return Response.status(200) //200
                .header("Access-Control-Allow-Origin", "*")
                .header("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS")
                .header("Access-Control-Allow-Headers", "X-Requested-With, Content-Type, X-Codingpedia,Authorization")
                .build();*/
    }

}

Ajax request:

$(document).ready(function (){


    $('#command').on('click', function(){

      var user = {
        name: $('#name').val(),
        secondName: $('#secondName').val(),
        sex: $('#sex').val(),
        acessLevel: $('#acessLevel').val(),
        email: $('#email').val(),
        telefone: $('#telefone').val(),
        login: $('#login').val(),
        password: $('#password').val()
      }

      $.ajax({

        url:'http://localhost:8282/apirestex/servlet/usuarios/save',
        data:user,
        contentType: "application/json; charset=utf-8",
        dataType: 'json',
        type:'POST',
        crossDomain: true,
        async:true,
        success: function() { console.log("Success!!!"); },
        error: function() { console.log('Failed!'); },
      });

    });
});

UPDATE 1

The class MyApplication:

import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.core.Application;

public class MyApplication extends Application {
  @Override
  public Set<Class<?>> getClasses() {
  final Set<Class<?>> classes = new HashSet<Class<?>>();

  // register resources and features
  classes.add(UsuarioResource.class);

  return classes;
  }
}

In the web.xml:

        <init-param>
            <param-name>javax.ws.rs.Application</param-name>
            <param-value>br.com.resource.api.MyApplication</param-value>
        </init-param>

UPDATE 2

Myapplication - I added the annotation "@Applicationpath("/")". I changed the Myapplication of package.

import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
import br.com.resource.api.UsuarioResource;


@ApplicationPath("/")
public class MyApplication extends Application {
	@Override
	public Set<Class<?>> getClasses() {
		final Set<Class<?>> classes = new HashSet<Class<?>>();
		
		// register resources and features
		classes.add(UsuarioResource.class);
		
		return classes;
	}
}

web xml.

<!-- Register JAX-RS Application, if needed. -->
	    <init-param>
	   		<param-name>javax.ws.rs.Application</param-name>
	        <param-value>br.com.app.api.MyApplication</param-value>
	    </init-param>
	    
	    <!-- Register resources and providers under my.package. -->
        <init-param>
            <param-name>jersey.config.server.provider.packages</param-name>
            <param-value>br.com.resource.api</param-value>
        </init-param>

Now the error returned is:

GRAVE: Servlet.service() for Servlet [br.com.app.api.Myapplication] in context with path [/apirestex] threw Exception [Servlet Execution threw an Exception] with root cause java.lang.abstractmethooverthrow: javax.ws.rs.core.Uribuilder.Uri(Ljava/lang/String;)Ljavax/Ws/rs/core/Uribuilder; at javax.ws.rs.core.Uribuilder.fromUri(Uribuilder.java:119) at com.sun.jersey.spi.container.servlet.Servletcontainer.service(Servletcontainer.java:662) at javax.servlet.http.HttpServlet.service(Httpservlet.java:729) at org.apache.Catalina.core.Applicationfilterchain.internalDoFilter(Applicationfilterchain.java:292) at org.apache.Catalina.core.Applicationfilterchain.doFilter(Applicationfilterchain.java:207) at org.apache.Tomcat.websocket.server.WsFilter.doFilter(Wsfilter.java:52) at org.apache.Catalina.core.Applicationfilterchain.internalDoFilter(Applicationfilterchain.java:240) at org.apache.Catalina.core.Applicationfilterchain.doFilter(Applicationfilterchain.java:207) at br.com.web.api.Opencorsfilter.doFilter(Opencorsfilter.java:30) at org.apache.Catalina.core.Applicationfilterchain.internalDoFilter(Applicationfilterchain.java:240) at org.apache.Catalina.core.Applicationfilterchain.doFilter(Applicationfilterchain.java:207) at org.apache.Catalina.core.Standardwrappervalve.invoke(Standardwrappervalve.java:212) at org.apache.Catalina.core.Standardcontextvalve.invoke(Standardcontextvalve.java:106) at org.apache.Catalina.authenticator.Authenticatorbase.invoke(Authenticatorbase.java:502) at org.apache.Catalina.core.Standardhostvalve.invoke(Standardhostvalve.java:141) at org.apache.Catalina.valves.Errorreportvalve.invoke(Errorreportvalve.java:79) at org.apache.Catalina.valves.Abstractaccesslogvalve.invoke(Abstractaccesslogvalve.java:616) at org.apache.Catalina.core.Standardenginevalve.invoke(Standardenginevalve.java:88) at org.apache.Catalina.connector.Coyoteadapter.service(Coyoteadapter.java:528) at org.apache.Coyote.http11.Abstracthttp11processor.process(Abstracthttp11processor.java:1100) at org.apache.Coyote.Abstractprotocol$Abstractconnectionhandler.process(Abstractprotocol.java:687) at org.apache.Tomcat.util.net.Nioendpoint$Socketprocessor.doRun(Nioendpoint.java:1520) at org.apache.Tomcat.util.net.Nioendpoint$Socketprocessor.run(Nioendpoint.java:1476) at java.util.Concurrent.ThreadPoolExecutor.runWorker(Unknown Source) at java.util.Concurrent.Threadpoolexecutor$Worker.run(Unknown Source) at org.apache.Tomcat.util.threads.Taskthread$Wrappingrunnable.run(Taskthread.java:61) at java.lang.Thread.run(Unknown Source)

SOLVED

The problem that caused the internal server error (500) was that the version of Jersey (1.9) I was using was not compatible with JAX-RS 2.0.(https://stackoverflow.com/questions/30176811/abstractmethoderror-using-uribuilder-on-jax-rs) The version has been amended.

Myapplication

import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.core.Application;
import br.com.resource.api.UsuarioResource;

public class MyApplication extends Application {
	@Override
	public Set<Class<?>> getClasses() {
		final Set<Class<?>> classes = new HashSet<Class<?>>();
		
		// register resources and features
		classes.add(UsuarioResource.class);
		
		return classes;
	}
}

Corsfilter - Adaptation of the Opencorsfilter class to work with Jersey 2

import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.ext.Provider;

@Provider
public class CORSFilter implements ContainerResponseFilter {

    @Override
    public void filter(ContainerRequestContext creq, ContainerResponseContext cres) {

        cres.getHeaders().add("Access-Control-Allow-Origin", "*");
        cres.getHeaders().add("Access-Control-Allow-Headers", "Authentication, Content-Type, X-Requested-With, X-Codingpedia");
        cres.getHeaders().add("Access-Control-Allow-Headers", "origin, content-type, accept, authorization");
        cres.getHeaders().add("Access-Control-Allow-Credentials", "true");
        cres.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD");
        cres.getHeaders().add("Access-Control-Max-Age", "1209600");

    }

}

User resource

import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;


@Path("/usuarios")
public class UsuarioResource {

	@Path("/save")
	@POST
	@Consumes(MediaType.APPLICATION_JSON)
	public Response save(String conteudo){
		
		System.out.println(conteudo);
		
	    return Response.status(200)
	            .build();
	}
	
}

pom.xml - dependences

<dependency>
    <groupId>org.glassfish.jersey.containers</groupId>
    <artifactId>jersey-container-servlet</artifactId>
    <version>2.25.1</version>
</dependency>


<!-- https://mvnrepository.com/artifact/javax.ws.rs/javax.ws.rs-api -->
<dependency>
    <groupId>javax.ws.rs</groupId>
    <artifactId>javax.ws.rs-api</artifactId>
    <version>2.0</version>
</dependency>

<dependency>
   <groupId>com.google.code.gson</groupId>
   <artifactId>gson</artifactId>
   <version>2.8.0</version>
</dependency>

web.xml - final

  	<display-name>apirestex</display-name>
  	
    <servlet>
        <servlet-name>br.com.app.api.MyApplication</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
 
        <!-- Register JAX-RS Application, if needed. -->
        <init-param>
            <param-name>javax.ws.rs.Application</param-name>
            <param-value>br.com.app.api.MyApplication</param-value>
        </init-param>
 
        <!-- Register resources and providers under my.package. -->
        <init-param>
            <param-name>jersey.config.server.provider.packages</param-name>
            <param-value>br.com.resource.api</param-value>
        </init-param>
 
        <!-- Register my custom provider (not needed if it's in my.package) AND LoggingFilter. -->
        <init-param>
            <param-name>jersey.config.server.provider.classnames</param-name>
            <param-value>br.com.web.api.CORSFilter</param-value>
        </init-param>

        <load-on-startup>1</load-on-startup>
    </servlet>
 
    <servlet-mapping>
        <servlet-name>br.com.app.api.MyApplication</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>

Ajax request

$.ajax({

		url:'http://localhost:8282/apirestex/servlet/usuarios/save',
		data: JSON.stringify(user),
		type:'POST',
		crossDomain: true,
    contentType: 'application/json; charset=utf-8',
		dataType: 'json',
		async:true,
		success: function() { console.log("Success"); },
		error: function() { console.log();('Failed!'); },
	  });

1 answer

1


Try adding this class to your project:

@WebFilter(urlPatterns = "/*") // Coloque só os padrões que você quer abrir o CORS.
public class OpenCorsFilter implements Filter {

    public OpenCorsFilter() {
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void destroy() {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        authorizeCrossDomain((HttpServletResponse) response);
        chain.doFilter(request, response);
    }

    public static void authorizeCrossDomain(HttpServletResponse resp) {
        resp.setHeader("Access-Control-Allow-Origin", "*");
        resp.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, HEAD");
        resp.setHeader("Access-Control-Allow-Headers", "Authentication, Content-Type, X-Requested-With, X-Codingpedia");
        resp.setHeader("Access-Control-Max-Age", "86400");
    }
}

Supposedly, nothing would need to be added to the web.xml because the annotation @WebFilter should automatically realize this. But if this does not happen, add this to web.xml:

<filter>
    <filter-name>OpenCorsFilter</filter-name>
    <filter-class>com.example.OpenCorsFilter</filter-class> <!-- Use o pacote adequado. -->
</filter>
<filter-mapping>
    <filter-name>OpenCorsFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

Remove this from your web.xml:

<filter>

    <filter-name>CORS</filter-name>
    <filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>

    <init-param>
       <param-name>cors.allowOrigin</param-name>
       <param-value>/*</param-value>
    </init-param>

    <init-param>
       <param-name>cors.supportedHeaders</param-name>
       <param-value>*</param-value>
    </init-param>

</filter>

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

And remove that from your pom.xml:

<dependency>
    <groupId>com.thetransactioncompany</groupId>
    <artifactId>cors-filter</artifactId>
    <version>1.9.1</version>
</dependency>
  • I made the suggested changes and had a breakthrough. Before my request stopped in OPTIONS that is sent before the POST, now it passes this part. But I get a 404 as a response and the System.out.println I put in my Source does not run. I even changed my Source so that it sends one javax.ws.rs.core.Response.status(200). build() instead of void or String, but I kept getting 404. Which leads me to believe that my Source is actually not being accessed. Did something wrong in URI or Resource?

  • @Rafaeldias If you put the project having the root in http://localhost:8282/apirestex, should work. However, the CORS error message speaks of a different URL: http://rafael.etc

  • Good afternoon @Victor Stafusa. Thanks for your help. Yes, I put it in the root. Being "apirestex" the project name, "Servlet" the <Servlet-name> configured in web.xml, "users" the resource path, and "save" the method name within the resource. I think there might be something related to me registering the resource in some way to be recognized by Jersey.

  • @Rafaeldias You have in your project some class that inherits from javax.ws.rs.core.Application? Also, if you try to open the URL in the browser http://localhost:8282/apirestex directly, what happens? If a 4XX error occurs, it means the server is running. If you give "Can’t connect" or something like that, then the server is running in the wrong place.

  • @Rafaeldias After your question update, it still doesn’t work?

  • EDIT @Victorsatafusa Another advance. After a few more modifications (I will update) now returns me an error 500. It seems the problem is that the version of Jersey (1.9) I am using is not compatible with JAX_RS 2.0. I will change the version. http://stackoverflow.com/questions/30176811/abstractmethoderror-using-uribuilder-on-jax-rs

Show 1 more comment

Browser other questions tagged

You are not signed in. Login or sign up in order to post.