Abrar Hossain
Abrar Hossain

Reputation: 2702

Angular CORS request blocked

I am trying to connect my Angular app with a simple REST server on express. The server only sends json data in reply to request. To add CORS support, I used the cors module from npm. On the Angular app, I added HttpHeaders following the instructions from this question: Angular CORS request blocked.

Here is my code in express where I set up the cors Options: `

// async CORS setup delegation
function corsOptsDelegator(req, cb) {
    let opts = {},
        origin = req.header('Origin');
    if(imports.allowedOrigins.indexOf(origin) === 1) opts.origin = true;
    else opts.origin = false;
    opts.optionsSuccessStatus = 200;
    opts.methods = ['POST', 'GET']; // allowed methods
    opts.credentials = true; // for passing/setting cookie 
    opts.allowedHeaders = ['Content-Type', 'Accept', 'Access-Control-Allow-Origin']; // restrict headers 
    opts.exposedHeaders = ['Accept', 'Content-Type']; // for exposing custom headers to clients; use for hash
    cb(null, opts);
}
`

Here's how I added it to the global get handler:

`
app.get('/', cors(corsOptsDelegator), (res, req, nxt) => {
    // only for adding cors on all requests
    nxt();
});
`

Here's how I set up the Angular service:

`

export class ContentGetterService {

  private root: string;
  private corsHeaders: HttpHeaders;
  //private contents: string;

  constructor(private http: HttpClient) {
    this.root = 'http://localhost:8888';
    this.corsHeaders = new HttpHeaders({
      'Content-Type': 'application/json',
      'Accept': 'application/json',
      'Access-Control-Allow-Origin': 'http://localhost:4200'
    });
    //this.contents = '';
   }

  getContent(subs: Array<string>): Observable<IContent> {
    return (() => {
      return this.http.get<IContent>( (() => {
        let r = this.root;
        subs.forEach((s, i, a) => {
          if(i === a.length-1) {
            r += s;
          }
          else {
            if(s !== '/') {
              r += s;
            }
          }
        });
        return r;
      })(), {
        headers: this.corsHeaders
      });

    })();
  }
  }

Browser Warning: Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:8888/. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).

Thanks.

Upvotes: 26

Views: 186901

Answers (7)

sathira lamal
sathira lamal

Reputation: 87

To solve this issue, you can create a proxy for your Angular app. Go to your Angular project directory and under the src folder, create a file named proxy.config.json.

On proxy.config.json

{
  "/api/*": {
    "target": "http://localhost:3010",
    "secure": false,
    "logLevel": "debug"
  }
}

http://localhost:3010 is the backend service.

Then in the package.json file, under the scripts add this line "start": "ng serve --proxy-config proxy.config.json", his will set up a proxy from your app to your backend service.

Now, when you make any API calls, you don’t need to include this part: http://localhost:3010 For example, instead of calling http://localhost:3010/api/myapp, now you can use /api/myapp

Also before test ,restart the serve again.

Upvotes: 1

Lecram
Lecram

Reputation: 102

If you're working with Angular CLI, NPM, Spring Data JPA and Tomcat 9, I finally found a solution to communicate between a frontend webapp (Angular) and backend webapp (Spring Data JPA) running on an application server (Tomcat).

In development environment:

The annotation @CrossOrigin("localhost:4200") in your Spring DAO repository is just fine if you running ng serve on default port 4200.

@CrossOrigin("http://localhost:4200")
public interface ConfigRepository extends JpaRepository<Config, String> {
    
}

In production environment:

If your application is running on a productive server, you have to modify your web.xml and add a (e.g.) build-in CORS filter to allow access to other resources as the origin.

<!-- ==================== CORS Filter Definition ====================== -->

<filter>
    <filter-name>CorsFilter</filter-name>
    <filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
    <init-param>
        <param-name>cors.allowed.origins</param-name>
        <param-value>http://localhost:8080</param-value>
    </init-param>
    <init-param>
        <param-name>cors.allowed.headers</param-name>
        <param-value>Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers,Access-Control-Allow-Origin</param-value>
    </init-param>
    <init-param>
        <param-name>cors.exposed.headers</param-name>
        <param-value>Access-Control-Allow-Origin</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>CorsFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

The difference to the template from Tomcat 9 CORS Filter is to add Access-Control-Allow-Origin to cors.allowed.header and set an actual allowed origin in cors.allowed.origins e.g. http://localhost:8080.

So, there is no need to specify explicit headers to an HTTP request. Thanks to @Neutrino.

Upvotes: 2

Abrar Hossain
Abrar Hossain

Reputation: 2702

Firstly, the problem on the client was due to the behavior of Firefox opting in to handle pre-flight CORS. Instead of using GET method,OPTIONS method was used. As it is evident from my code, I do not have any handler for handling OPTIONS. After some googling, I came across this post on github: Express CORS middleware. Using this and making the following modifications to my client request headers, I was able to properly get it up and running. Here's the code:

CORS Middleware:

function myCors(req, res, nxt) {
    res.header('Access-Control-Allow-Origin', 'http://localhost:4200');
    res.header('Access-Control-Allow-Methods', 'GET,PUT,OPTIONS');
    res.header('Access-Control-Allow-Headers', 'Access-Control-Allow-Origin, Content-Type, Accept, Accept-Language, Origin, User-Agent');
    if(req.method === 'OPTIONS') {
        res.sendStatus(204);
    }
    else {
        nxt();
    }
}`

Mounting it to the app instance: app.use(myCors);

On the Angular client side service:

this.corsHeaders = new HttpHeaders({
  'Content-Type': 'application/json',
});
// ... adding it to the request
getContent(subs: Array<string>): Observable<IContent> {
return (() => {
  return this.http.get<IContent>( (() => {
    let r = this.root;
    subs.forEach((s, i, a) => {
      if(i === a.length-1) {
        r += s;
      }
      else {
        if(s !== '/') {
          r += s;
        }
      }
    });
    return r;
  })(), {
    headers: this.corsHeaders
  });
 
})();
}

Thanks everyone for their time and efforts.

Edit

Thanks to @neutrino for pointing out that adding the Access-Control-* headers on the request makes no sense (and has no effect).

Upvotes: 8

TheUnreal
TheUnreal

Reputation: 24472

In case you are using angular-cli, you can use a proxy:

proxy.conf.json:

{
  "/api": {
    "target": "http://localhost:8888",
    "secure": false
  }
}

Will redirect all requests with /api prefix to localhost:8888 without any cors issue.

Offical docs: https://angular.io/guide/build#proxying-to-a-backend-server

Upvotes: 19

Chandan Kumar
Chandan Kumar

Reputation: 889

Use this code and manage yourself according to your structure.

app.use(
    cors({ 
        origin: http://localhost:4200, 
        methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
        allowedHeaders: [
            'Content-Type', 
            'Authorization', 
            'Origin', 
            'x-access-token', 
            'XSRF-TOKEN'
        ], 
        preflightContinue: false 
    })
);

app.get('/', (res, req, nxt) => {
    // only for adding cors on all requests
    nxt();
});

On Angular Side

constructor(private http: HttpClient) {
  this.root = 'http://localhost:8888';  //remove
  this.corsHeaders = new HttpHeaders({
    'Content-Type': 'application/json',
    'Accept': 'application/json',
    'Access-Control-Allow-Origin': '/' .  // edit 
  });
  //this.contents = '';
 }

and use <base> index.html

Upvotes: 0

vivek rautela
vivek rautela

Reputation: 155

In development environment(localhost),you can do it easily without following any of these hard steps.You can simply download https://addons.mozilla.org/firefox/addon/cors-everywhere/ if you are using firefox and i think there must be an extension for chrome also.After downloading it will come to the firefox menubar and then you can click it to activate it,thats all you can now access to all apis as it automatically sends header with all requests.For production environment you have to configure it from your server by adding access-control-allow-origin to * (all)

Upvotes: 5

Chandan Kumar
Chandan Kumar

Reputation: 889

Allow Server side that you hit from angular JS, in that case you need to allow http://localhost:8888/ because that is your server side URL, angular run on http://localhost:4200. please check the Http. then it work

Upvotes: -1

Related Questions