0

I'm getting the below CORS issue in my Angular 7 application when running locally.

Access to XMLHttpRequest at 'http://localhost:8080/OnlineOrganicMarket/api/changeorderstatus' from origin 'http://localhost:4200' 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.

I'm using Jersey RESTful Web Services framework at the backend. This happens only with header type application/json. It is working for application/x-www-form-urlencoded.

I think there is a problem with my CORSFilter class.

package com.oom.services.filter;

import java.io.IOException;
import java.util.Arrays;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

import javax.annotation.Priority;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.Priorities;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;

import org.glassfish.jersey.server.ExtendedUriInfo;

@Provider
@Priority(Priorities.HEADER_DECORATOR)
public class CORSFilter implements ContainerRequestFilter, ContainerResponseFilter {
 private static final String ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials";
 private static final String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers";
 private static final String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods";
 private static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin";
 private static final String ACCESS_CONTROL_REQUEST_HEADERS = "Access-Control-Request-Headers";
 private static final String AUTHORIZATION = "authorization";
 private static final String ORIGIN = "Origin";

 private static String extractAllowedMethods(final ExtendedUriInfo extendedUriInfo) {
  final Optional<Class<?>> optional = extendedUriInfo.getMatchedRuntimeResources().stream()
    .flatMap(r -> r.getResources().stream()).flatMap(r -> r.getHandlerClasses().stream())
    .filter(r -> r.getPackage().getName().startsWith("com.oom.services")).findFirst();

  if (optional.isPresent()) {
   return Arrays.stream(optional.get().getDeclaredMethods())//
     .flatMap(m -> Arrays.stream(m.getAnnotations()))//
     .map(a -> a.annotationType().getAnnotation(javax.ws.rs.HttpMethod.class))//
     .filter(Objects::nonNull)//
     .map(HttpMethod::value)//
     .distinct()//
     .collect(Collectors.joining(", "));
  }
  // Returning OPTIONS is a bit shady, as ACAM is about *real*, actual methods only.
  return "OPTIONS";
 }

 @Context
 private HttpServletRequest request;

 @Context
 private ExtendedUriInfo extendedUriInfo;

 @Override
 public void filter(final ContainerRequestContext requestContext) throws IOException {
  final String origin = requestContext.getHeaderString(ORIGIN);
  if (origin != null && "OPTIONS".equals(requestContext.getMethod())) {
   request.setAttribute(this.getClass().getName(), true);
   requestContext.abortWith(Response.ok("CORS OK, carry on.", MediaType.TEXT_PLAIN_TYPE).build());
  }
 }

 /**
  * @see https://www.w3.org/TR/cors/
  * @see https://jmchung.github.io/blog/2013/08/11/cross-domain-on-jersey-restful-web-services/
  * @see https://solutionsisee.wordpress.com/2016/06/30/adding-cors-support-in-jersey-server/
  */
 @Override
 public void filter(final ContainerRequestContext requestContext, final ContainerResponseContext responseContext)
   throws IOException {

  final MultivaluedMap<String, Object> responseHeaders = responseContext.getHeaders();
  final String origin = requestContext.getHeaderString(ORIGIN);

  if (origin != null) {
   // The presence of the Origin header marks a CORS request.
   responseHeaders.add(ACCESS_CONTROL_ALLOW_ORIGIN, origin);
   responseHeaders.add(ACCESS_CONTROL_ALLOW_METHODS, extractAllowedMethods(extendedUriInfo));
   responseHeaders.add(ACCESS_CONTROL_ALLOW_HEADERS, AUTHORIZATION + ", X-Requested-With, Content-Type");
   if (requestContext.getHeaderString(ACCESS_CONTROL_REQUEST_HEADERS) != null) {
    responseHeaders.add(ACCESS_CONTROL_ALLOW_CREDENTIALS, requestContext
      .getHeaderString(ACCESS_CONTROL_REQUEST_HEADERS).toLowerCase().contains(AUTHORIZATION));
   }
  }

  if (request.getAttribute(this.getClass().getName()) != null) {
   // We are in a CORS Preflight answer, fast tracked. The entity (== response body) is not
   // relevant.
  }
 }
}

My service part where I'm having issue

    @POST
    @Path("/changeorderstatus")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Response updateOrderStatus(OrderBean orderStatus) {

        JSONArray responseJson = new OrderDAO().updateOrderStatus(orderStatus);

        return Response.ok(responseJson, MediaType.APPLICATION_JSON).header("Access-Control-Allow-Origin", "*")
                .header("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS").build();
    }

I tried disabling CORS through some extensions and disabling it in the chrome browser. Nothing worked.

Soothran
  • 1,233
  • 4
  • 14
  • 1
    This will help, https://stackoverflow.com/questions/28065963/how-to-handle-cors-using-jax-rs-with-jersey – ROOT Jan 05 '20 at 06:17
  • Check out this one: https://stackoverflow.com/questions/58115578/spring-boot-cors-with-https-failing/59561352#59561352 – Imranmadbar Jan 05 '20 at 08:48
  • 1
    @mamounothman thanks for the reference. it actually did help. Thank you so much – Soothran Jan 10 '20 at 14:14

1 Answers1

1

As commented by @mamounothman, I tried out the below code from Paul Samsotha's answer

It started working when I added Content-type key in the Access-Control-Allow-Headers

Originally answered by Paul Samsotha

package com.oom.services.filter;

import java.io.IOException;
import java.util.Arrays;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

import javax.annotation.Priority;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.Priorities;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;

import org.glassfish.jersey.server.ExtendedUriInfo;


@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 static 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-type, Content-MD5, CSRF-Token");
        }

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

Still I'm not getting why the previous code I was using did not work with the header type Content-Type - application/json even though it had the header type Content-Type in the Access-Control-Allow-Header's list.

Soothran
  • 1,233
  • 4
  • 14