So I am trying to load the data from a REST source into my Angular 6 app using http: HttpClient
from '@angular/common/http'
. Calling the app in the browser using ng serve --open
though doesn't do the job. I assume CORS to be the problem here. I guess I either have to set the server or the client headers with Access-Control-Allow-Origin
or something, but I have already tried multiple ways without any success in making this simple REST call work. So what follows below is what I coded.
Calling the Angular app in the browser responds the following error:
Failed to load http://localhost:8080/mysite-backend/rest/report/single/d83badf3:
Response to preflight request doesn't pass access control check:
No 'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:4200' is therefore not allowed
access.
Calling the same URL (http://localhost:8080/mysite-backend/rest/report/single/d83badf3
) within the Chrome browser works perfectly though:
{"id":"d83badf3","language":"en","categoryId":"a5","title":"Simcity","created":1527723183880,"modified":1527723183880}
Within Angular 6 I use the following service class that I generated with ng generate service tour
. Within that I created the method getTour(id: string)
which does non more than call REST at the URL and return the retrieved JSON-string as a Tour object:
import { Injectable } from '@angular/core';
import { HttpHeaders, HttpClient } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { Tour } from './tour';
import { catchError, tap } from 'rxjs/operators';
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json'
})
};
@Injectable({
providedIn: 'root'
})
export class TourService {
// URL to the web api.
private tourUrl = 'http://localhost:8080/mysite-backend/rest/report';
constructor(
private http: HttpClient
) { }
/**
*
* @param id: string GET tour report by id. Will 404 if id not found.
*/
getTour(id: string): Observable<Tour> {
httpOptions.headers.append('Access-Control-Allow-Origin', 'http://localhost:8080');
httpOptions.headers.append('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
httpOptions.headers.append('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
httpOptions.headers.append('Access-Control-Allow-Credentials', 'true');
const url = `${this.tourUrl}/single/${id}`;
console.log("XXX URL GET " + url)
return this.http.get<Tour>(url, httpOptions).pipe(
tap(_ => this.log(`fetched tour id=${id}`)),
catchError(this.handleError<Tour>(`getHero id=${id}`))
);
}
private handleError<T> (operation = 'operation', result?: T) {
return (error, any): Observable<T> => {
console.error(error);
this.log(`${operation} failed: ${error.message}`);
return of(result as T);
};
}
private log(message: string) {
console.log("Log message: " + message);
}
}
This is the Tour
object:
export class Tour {
id: string;
language: string;
categoryId: string;
created: Date;
modified: Date;
title: string;
content: string;
}
I also added HttpClientModule
from '@angular/common/http'
to the imports
array and the providers
array within app.module.ts
.
The RESTful WebService is built in Java. Here I have the getReport(@PathParam("reportId") String reportId)
method, that gets an Report
object and returns it within a Reponse
:
import java.util.List;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
/**
* Describes the RESTful access for reports.
*/
@Path("/report")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class ReportResource {
@Inject
private Logger logger;
@GET
@Path("/single/{reportId}")
public Response getReport(@PathParam("reportId") String reportId) {
//return Mock.getReport(reportId);
return Response.ok() // 200
.entity(Mock.getReport(reportId))
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT")
.allow("OPTIONS").build();
}
...
}
What must I do to make a successful call from the Angular 6 client to the Java RESTful WebService?
Since the header is currently set to allow access only from https://yoursite.com , the browser will block access to the resource and you will see an error in your console. Now, to fix this, change the headers to this: res. setHeader("Access-Control-Allow-Origin", "*");
If the server is under your control, add the origin of the requesting site to the set of domains permitted access by adding it to the Access-Control-Allow-Origin header's value. You can also configure a site to allow any site to access it by using the * wildcard. You should only use this for public APIs.
There Are Two Approaches to Getting It Right. Use a reverse proxy server or WSGI server(such as Nginx or Apache) to proxy requests to your resource and handle the OPTIONS method in the proxy. Add support for handling the OPTIONS method in the resource's code.
The access-control-allow-origin plugin essentially turns off the browser's same-origin policy. For every request, it will add the Access-Control-Allow-Origin: * header to the response. It tricks the browser, and overrides the CORS header that the server has in place with the open wildcard value.
The Issue is not related to angular itself, but the the web server you are using.
The angular http client request always have a preflight request with type options before hitting the actual request.
you might need to add OPTIONS request method to this line
.header("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT")
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With