0
votes

I'm trying to see an Email using Sendgrid from an Angular 6 application using the minimum payload. Using a Google Cloud Function when I post the request I get error 405 from the browser:

https://MyCloudFunctions/httpEmail 405

Access to XMLHttpRequest at 'https://MyCloudFunctions/httpEmail' from origin 'http://localhost:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

The Cloud Function Log shows:

Error: Only POST requests are accepted at Promise.resolve.then (/user_code/index.js:26:23) at process._tickDomainCallback (internal/process/next_tick.js:135:7)

Cloud Function code

const sendgrid = require('sendgrid');
const client = sendgrid("MyAPI_KEY");

function parseBody(body) {
  var helper = sendgrid.mail;
  var fromEmail = new helper.Email(body.from);
  var toEmail = new helper.Email(body.to);
  var subject = body.subject;
  var content = new helper.Content('text/html', body.content);
  var mail = new helper.Mail(fromEmail, subject, toEmail, content);
  return  mail.toJSON();
}

exports.sendgridEmail = (req, res) => {
    res.setHeader("Access-Control-Allow-Origin", "*");
    res.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
    res.setHeader("Content-Type", "application/json");
  return Promise.resolve()
    .then(() => {
      if (req.method !== 'POST') {
        const error = new Error('Only POST requests are accepted');
        error.code = 405;
        throw error;
      }

      // Build the SendGrid request to send email
      const request = client.emptyRequest({
        method: 'POST',
        path: '/v3/mail/send',
        body: getPayload(req.body),
      });

      // Make the request to SendGrid's API
      console.log(`Sending email to: ${req.body.to}`);
      return client.API(request);
    })
    .then(response => {
      if (response.statusCode < 200 || response.statusCode >= 400) {
        const error = Error(response.body);
        error.code = response.statusCode;
        throw error;
      }

      console.log(`Email sent to: ${req.body.to}`);

      // Forward the response back to the requester
      res.status(response.statusCode);
      if (response.headers['content-type']) {
        res.set('content-type', response.headers['content-type']);
      }
      if (response.headers['content-length']) {
        res.set('content-length', response.headers['content-length']);
      }
      if (response.body) {
        res.send(response.body);
      } else {
        res.end();
      }
    })
    .catch(err => {
      console.error(err);
      const code =
        err.code || (err.response ? err.response.statusCode : 500) || 500;
      res.status(code).send(err);
      return Promise.reject(err);
    });
}

** Update: simplified TS file ** Angular TS File

    import { Component, OnInit } from '@angular/core';
import { HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
import { Router } from '@angular/router';

@Component({
  selector: 'app-contact',
  templateUrl: './contact.component.html',
  styleUrls: ['./contact.component.css']
})
export class ContactComponent implements OnInit {

  //constructor(private sendgridService: SendgridService){}
  constructor(private _http: HttpClient, private router: Router) { }
  ngOnInit() { }

  sendEmail() {

    let url = `https://us-central1-probalance-214005.cloudfunctions.net/httpEmail?sg_key=MY_API_KEY`
    let body = {
      "personalizations": [
        {
          "to": [
            {
              "email": "[email protected]",
              "name": "Postman"
            }
          ],
          "subject": "Success"
        }
      ],
      "from": {
        "email": "[email protected]",
        "name": "Angular App"
      },
      "reply_to": {
        "email": "[email protected]",
        "name": "Test"
      },
      "content": [
        {
          "type": "text/plain",
          "value": "Request Successful 001!"
        }
      ]
    };

    let httpOptions = {
      headers: new HttpHeaders({
        'Content-Type': 'application/json',
      })
    };
    console.log("Payload:")
    console.log(url)
    console.log(body);
    console.log(httpOptions.headers)

    return this._http.post(url, body, httpOptions)
      .toPromise()
      .then(res => {
        console.log(res)
      })
      .catch(err => {
        console.log(err)
      })

  }
}

Angular HTML File

<button type="submit" id="submit" class="btn btn-primary (click)="sendEmail()">Sendgrid </button>

1

1 Answers

1
votes

A few things:

  • Access-Control-Allow-Origin is a response header. So, it should be set in the server code, not in the client code written in Angular.
  • Since you have set Content-Type, the browser sends an OPTIONS request instead of POST. So, make sure OPTIONS request type is enabled and Content-Type is allowed in Access-Control-Allow-Headers in the server.

You can understand more about CORS issue here: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS