Reputation: 319
I'm trying to connect to my REST endpoint built using JAX-RS, but I get the response that the header 'Access-Control-Allow-Origin' is not present. Trying to GET a resource with Angular2.
What I've tried
I tried the solution presented in https://stackoverflow.com/a/30450944.
My response filter:
@Provider
@PreMatching
public class ResponseFilter implements ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
responseContext.getHeaders().add("Access-Control-Allow-Origin", "*");
responseContext.getHeaders().add("Access-Control-Allow-Headers", "origin, content-type, accept, authorization");
responseContext.getHeaders().add("Access-Control-Allow-Credentials", "true");
responseContext.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD");
}
}
And my web.xml servlet mapping:
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>com.api.ResponseFilter</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
Update 1
Updated the
responseContext.getHeaders().add(...);
to
responseContext.getHeaders.putSingle(...);
but I'm still getting the same error. While testing in postman, I am getting the correct headers back:
Access-Control-Allow-Credentials →true
Access-Control-Allow-Headers →origin, content-type, accept, authorization
Access-Control-Allow-Methods →GET, POST, PUT, DELETE, OPTIONS, HEAD
Access-Control-Allow-Origin →*
Update 2
In my JAXRSConfig, which extends Application, I've manually added all resources possibly necessary:
@ApplicationPath("api")
public class JAXRSConfig extends Application {
@Override
public Set<Class<?>> getClasses() {
Set<Class<?>> resources = new HashSet<>();
addRestResourceClasses(resources);
return resources;
}
private void addRestResourceClasses(Set<Class<?>> resources) {
resources.add(AdminResource.class);
resources.add(RequestFilter.class);
resources.add(ResponseFilter.class);
resources.add(TweetResource.class);
resources.add(UserResource.class);
}
}
Via the use of breakpoints I have noticed that my ResponseFilter does get invoked when I send a GET request via Postman, but it does not get invoked when I send a GET request via my angular application.
My angular code to send a request:
let headers = new Headers({'Authorization': authToken});
headers.append('Content-Type', 'application/json');
let options = new RequestOptions({ headers: headers});
return this.http.get(http://localhost:8080/api/object/user)
.map((res:Response) => res.json())
.catch((error:any) => Observable.throw(error.json().error || 'Server error'));
Update 3
I did realize I did not actually add the defined headers to the request; so I've updated my angular code to:
let authToken = "Basic " + btoa("henk:asdf");//temporary until I learn how to allow a user to log in, in angular
let headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('Authorization', authToken);
//let options = new RequestOptions({ headers: headers});
return this.http.get(this.myUrl, {headers: headers})
.map((res:Response) => res.json())
.catch((error:any) => Observable.throw(error.json().error || 'Server error'));
Update 4
I've since learned this could be due to the pre-flight check Chrome (and other browsers) perform, via a request-method called OPTIONS. I've added a request filter to my api as follows:
@Provider
@PreMatching
public class RequestFilter implements ContainerRequestFilter {
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
if (requestContext.getRequest().getMethod().equals("OPTIONS")) {
requestContext.abortWith(Response.status(Response.Status.OK).header("Access-Control-Allow-Origin", "http://localhost:4200").build());
}
}
}
But to no avail. With a break point on the if-statement I've seen that this method does not get called in a GET request from angular, but it does get called from a request from Postman.
I've also tried a different CORS filter according to https://stackoverflow.com/a/19902666/6795661. But this did not work either.
Update 5
As per peeskillet I've tried creating a class that implements javax.servlet.Filter, to add the required header(s) to a response:
public class ContainerResponseFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletResponse res = (HttpServletResponse) response;
res.addHeader("Access-Control-Allow-Origin", "http://localhost:4200");
res.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
res.addHeader("Access-Control-Allow-Headers", "Content-Type");
chain.doFilter(request, response);
}
@Override
public void destroy() {
}
}
With the corresponding mapping in my web.xml:
<filter>
<filter-name>corsfilter</filter-name>
<filter-class>com.api.ContainerResponseFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>corsfilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
And I've added ContainerResponseFilter as resource in my Application configuration. Unfortunately I am still getting the error that the 'Access-Control-Allow-Origin' header is not present.
Upvotes: 1
Views: 3654
Reputation: 319
As it turns out, the problem was with the authentication. My application server was trying to authenticate the request, but since a CORS check is done before the authentication I just got an unauthorized response, which did not include the required header.
Upvotes: 1