9

I have seen several other questions like this, but they do not solve the issue. I used MailChimp's API to make a simple call to add a member to my mailing list when they sign up.

However when I test, I get a 401 unauthorized and the API complains of no API key submitted. When I inspect the request in Chrome, I don't see any Authorization header. Here is my code:

        const formData = {
            email_address: this.emailControl.value,
            status: 'subscribed',
            merge_fields: {
                NAME: this.nameControl.value
            }
        };
        const header = new HttpHeaders({
            'Authorization': 'apikey:' + environment.mailChimpApiKey
        });

        this.http.post(this.mailChimpUrl, formData, {
            headers: header,
            observe: 'response'
        }).subscribe(response => {
            console.log('response', response);
            if (response.status === 200) {
                this.submitted = true;
            }
        });

I have checked and double-checked the HttpClient.post method signature, and how the MailChimp API expects to receive the Auth header. It seems like I'm doing everything right, so why isn't Angular setting the header?

I am noticing that the value changes only for the Access-Control-Request-Headers when I set optional headers. Am I reading the chrome console wrong?

enter image description here

Angular version: 5.2.4

0

4 Answers 4

4

The issue is not with Angular. Modern browsers send a preflight OPTIONS request for most cross-domain requests, which is not supported by Mailchimp. The Mailchimp API does not support client-side implementations:

MailChimp does not support client-side implementation of our API using CORS requests due to the potential security risk of exposing account API keys.

It would have been nice if this was stated a bit more obviously, but I didn't notice it at first. The best solution is to use jsonp.

Sign up to request clarification or add additional context in comments.

Comments

3

To do this we need to import Headers and RequestOptions along with Http from @angular/http library.

And as @Maciej suggested you can also use withCredentials : true to your request options.

ApplicationService.ts

import { Injectable } from '@angular/core';
import { Http, Headers, Response, RequestOptions } from '@angular/http';

@Injectable()
export class ApplicationService {

  constructor(private http: Http) {
  }

  myformPost(id:number, formData : any){

     let header = this.initHeaders();
     let options = new RequestOptions({ headers: header, method: 'post'});
     let body = JSON.stringify(formData);

     return this.http.post(this.myapiUrl, body, options)
                .map(res => {
                    return res.json();
                })
                .catch(this.handleError.bind(this));
  }

  private initHeaders(): Headers {
      var headers = new Headers();
      let token = localstorage.getItem(StorageKey.USER_TOKEN);
      if (token !== null) {
         headers.append('Authorization', token);
      }

      headers.append('Pragma', 'no-cache');
      headers.append('Content-Type', 'application/json');
      headers.append('Access-Control-Allow-Origin', '*');
      return headers;
  }

  private handleError(error: any): Observable<any> {
      return Observable.throw(error.message || error);
  }             
}

Comments

0

From the screenshot, I assume that you are making OPTIONS request, not POST.

Probably the problem is on the server-side. Check out this answer: http post - how to send Authorization header?

You should also consider adding withCredentials to your request options.

const formData = {
            email_address: this.emailControl.value,
            status: 'subscribed',
            merge_fields: {
                NAME: this.nameControl.value
            }
        };
        const header = new HttpHeaders({
            'Authorization': 'apikey:' + environment.mailChimpApiKey
        });

        this.http.post(this.mailChimpUrl, formData, {
            headers: header,
            observe: 'response',
            withCredentials: true
        }).subscribe(response => {
            console.log('response', response);
            if (response.status === 200) {
                this.submitted = true;
            }
        });

According to documentation:

withCredentials: boolean | null

Enable use credentials for a Request.

11 Comments

Weird. It's still not adding the header. Or any other test header that I try. Are there any other weird gotchas I should look out for?
I am just checking angular.io/guide/http#adding-headers and in my opinion everything is OK with your request...
It's stupid, but maybe you need to create RequestOptions separately, not "on the fly". Or at least 'cast' this object {headers: 'someHeader'} as RequestOptions ?
I added a screenshot, it is not actually setting headers, but values inside Access-Control-Request-Headers which seems like a clue
It's not Angular. It is browser :)
|
0

I'd very much prefer to comment on the original posters answer as I feel it is correct and a clarification comment may help that person get +1 for answer.

So..

If I am running code between angular and two of my domains and using say node to serve the data, I have to enter the following during development in the node server. I do this very open at first to just make sure all is working in dev.

app.use(function (req, res, next) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, 
PATCH, DELETE');
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
res.setHeader('Access-Control-Allow-Credentials', true); //
next();

});

This clears up your error but is too open for cross scripting, you can read on how to allow specific domains etc to access it instead of the * provided here.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.