1
votes

I have downloaded Jersey Java Rest Api example from https://www.logicbig.com/tutorials/java-ee-tutorial/jax-rs/post-example.html

When I run this project using 'Run on server', it is accepting the following Rest api commands from Postman and perfectly working fine:

GET http://localhost:9900/jaxrs-post-example/rest/customers

POST http://localhost:9900/jaxrs-post-example/rest/customers
{
  "firstName":"David",
  "lastName":"Parker",
  "school":"CSI",
  "standard":"4",
  "rollNumber":85
}

When I send same command from ReactJS, the server is showing the following error in ReactJS.

Access to fetch at 'http://localhost:9900/jaxrs-post-example/rest/customers' from origin 'http://localhost:3000' 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. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

To fix this issue, I have added the following in RestServer.java (Based on How to handle CORS using JAX-RS with Jersey)

@Provider
@PreMatching
public class CorsFilter implements ContainerRequestFilter, ContainerResponseFilter {

    /**
     * Method for ContainerRequestFilter.
     */
    @Override
    public void filter(ContainerRequestContext request) throws IOException {

        // If it's a preflight request, we abort the request with
        // a 200 status, and the CORS headers are added in the
        // response filter method below.
        if (isPreflightRequest(request)) {
            request.abortWith(Response.ok().build());
            return;
        }
    }

    /**
     * A preflight request is an OPTIONS request
     * with an Origin header.
     */
    private boolean isPreflightRequest(ContainerRequestContext request) {
        return request.getHeaderString("Origin") != null
                && request.getMethod().equalsIgnoreCase("OPTIONS");
    }

    /**
     * Method for ContainerResponseFilter.
     */
    @Override
    public void filter(ContainerRequestContext request, ContainerResponseContext response)
            throws IOException {

        // if there is no Origin header, then it is not a
        // cross origin request. We don't do anything.
        if (request.getHeaderString("Origin") == null) {
            return;
        }

        // If it is a preflight request, then we add all
        // the CORS headers here.
        if (isPreflightRequest(request)) {
            response.getHeaders().add("Access-Control-Allow-Credentials", "true");
            response.getHeaders().add("Access-Control-Allow-Methods",
                "GET, POST, PUT, DELETE, OPTIONS, HEAD");
            response.getHeaders().add("Access-Control-Allow-Headers",
                // Whatever other non-standard/safe headers (see list above) 
                // you want the client to be able to send to the server,
                // put it in this list. And remove the ones you don't want.
                "X-Requested-With, Authorization, " +
                "Accept-Version, Content-MD5, CSRF-Token, Content-Type");
        }

        // Cross origin requests can be either simple requests
        // or preflight request. We need to add this header
        // to both type of requests. Only preflight requests
        // need the previously added headers.
        response.getHeaders().add("Access-Control-Allow-Origin", "*");
    }
}

Also, I have added the following in web.xml

    <web-app>
  <display-name>JAX-RS Web Application</display-name>
  <servlet>
    <servlet-name>jersey-json-example-serlvet</servlet-name>
    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
    <init-param>
        <param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
        <param-value>com.javacodegeeks.rest.jersey.server.CORSFilter</param-value>
    </init-param>
    <init-param>
        <param-name>com.sun.jersey.config.property.packages</param-name>
        <param-value>com.javacodegeeks.rest.jersey.server</param-value>
    </init-param>
    <init-param>
        <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
        <param-value>true</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>jersey-json-example-serlvet</servlet-name>
    <url-pattern>/rest/*</url-pattern>
  </servlet-mapping>
</web-app>

When I tried Flask+Python with CORS, the command from ReactJS is perfectly working.

ReactJS change: fetch('http://localhost:5000/jaxrs-post-example/rest/customers',

Flask+Python code:

app = Flask(__name__)
app.config['SECRET_KEY'] = 'xyz'
app.config['CORS_HEADERS'] = 'Content-Type'
#Allow specific origin
cors = CORS(app, resources={r"/jaxrs-post-example/*": {"origins": ["http://localhost:3000"]}})

@app.route('/jaxrs-post-example/rest/customers', methods=['GET', 'POST'])
def getStudent():
    print("You are getting getStudent request")
    content = request.json
    id = content['id']
    name = content['name']
    address = content['address']
    phoneNumber = content['phoneNumber']
    print("id = ", id)
    print("name = ", name)
    print("address = ", address)
    print("phoneNumber = ", phoneNumber)
    json_object = JFY({"result": "getStudent is called"})
    print("json_results = ", json_object)
    print("response for getStudent = ", json_object.get_json())
    return json_object

The output is perfect

127.0.0.1 - - [08/Sep/2020 21:00:40] "OPTIONS /jaxrs-post-example/rest/customers HTTP/1.1" 200 -
127.0.0.1 - - [08/Sep/2020 21:00:41] "POST /jaxrs-post-example/rest/customers HTTP/1.1" 200 -
You are getting getStudent request
id =  2152
name =  David
address =  12,new street
phoneNumber =  8765489011
json_results =  <Response 39 bytes [200 OK]>
response for getStudent =  {'result': 'getStudent is called'}

Questions:

  1. How to achieve CORS in Jersey Java (similar to Flask+Python)?
  2. Please provide a solution for this issue
2
You're registering the filter wrong. Show your whole web.xmlPaul Samsotha
whole web.xml is updatedAnbunathan
You're using Jersey 1.x. That filter you have is for 2.x Your configuration is correct, but the filter is wrong. You need to use the 1.x filter. It is slightly different. You will see an example in the link.Paul Samsotha
>>You will see an example in the link. – ??? Any link is missing?Anbunathan
Your link.......Paul Samsotha

2 Answers

1
votes

Solution is found

Based on the video: Enable CORS in a JAX-RS Project https://www.youtube.com/watch?v=CDEeOWKza2Q

The steps are:

Right click any package > New > Filter

    Java package > com.logicbig.example

    Class Name > CorsFilter > Next

    Double click /CorsFilter > Pattern > /rest/* > Ok > Next

    Ensure > Interface = javax.servlet.Filter > Finish

Observe new class(CorsFilter) is created

Remove constructor

Add the following under doFilter


public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletResponse resp = (HttpServletResponse) response;
        resp.addHeader("Access-Control-Allow-Origin", "http://localhost:3000");
        resp.addHeader("Access-Control-Allow-Headers", "*");
        resp.addHeader("Access-Control-Allow-Methods", "*");
        chain.doFilter(request, response);
    }

Build path > External jar > Add > C://Tomcat/lib/servlet-api.jar

Observe: All errors are removed

Re-Start Tomcat

Run on server

Observe: CORS error from ReactJS is removed

-2
votes

You have to enable cross origin resource sharing from the service you are trying to access. please check the related link here. This very nicely explains how to achieve it in JAXRS with Jersey.