Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 6 accessing REST failing with Access-Control-Allow-Origin

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?

like image 439
Socrates Avatar asked May 31 '18 00:05

Socrates


People also ask

How do I fix Access-Control allow origin?

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

How do I fix CORS header Access-Control allow Origin missing?

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.

How do you resolve no Access-Control allow Origin header is present on the requested resource?

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.

What is Cors policy no Access-Control allow origin?

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.


1 Answers

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")
like image 114
Ihab Salem Avatar answered Oct 27 '22 02:10

Ihab Salem