How to use SendGrid from Google App Engine Golang?

2019-08-03 22:34发布

The example code at: https://sendgrid.com/blog/send-email-go-google-app-engine/

My guess this is very old sample code to use sendgrid-go on Google App Engine.

I've attempted 4 permutations and failed each time with:

https://api.sendgrid.com/v3/mail/send: http.DefaultTransport and http.DefaultClient are not available in App Engine. See https://cloud.google.com/appengine/docs/go/urlfetch/

Here is a minimum hardcoded attempt with some logging:

package sendgridgo


import(
    "github.com/sendgrid/sendgrid-go"
    "fmt"
    _"google.golang.org/appengine"
    "net/http"
    "google.golang.org/appengine/log"
    "google.golang.org/appengine"
    "google.golang.org/appengine/urlfetch"
     _ "github.com/sendgrid/sendgrid-go/helpers/mail"
)

func init(){
    http.HandleFunc("/", IndexHandler)
    appengine.Main()
}

func IndexHandler (w http.ResponseWriter, r *http.Request){
    ctx := appengine.NewContext(r)

    log.Infof(ctx, "IndexHandler")
    sg := sendgrid.NewSendClient("SENDGRID_API_KEY")
    log.Infof(ctx, "%v", sg)
    bob := urlfetch.Client(ctx)

    log.Infof(ctx, "UrlFetchClient %v", bob)
    //resp, err := sg.Send(m)
    request := sendgrid.GetRequest("SENDGRID_API_KEY", "/v3/mail/send", "https://api.sendgrid.com")
    request.Method = "POST"

    request.Body = []byte(` {
    "personalizations": [
        {
            "to": [
                {
                    "email": "darian.hickman@gmail.com"
                }
            ],
            "subject": "Sending with SendGrid is Fun"
        }
    ],
    "from": {
        "email": "darian.hickman@villagethegame.com"
    },
    "content": [
        {
            "type": "text/plain",
            "value": "and easy to do anywhere, even with Go"
        }
    ]
}`)
    resp, err := sendgrid.API(request)

    if err != nil{
        log.Errorf(ctx, "Failed %v", err)
    }

    fmt.Fprint(w, resp)


}

3条回答
Juvenile、少年°
2楼-- · 2019-08-03 22:48

The solution is documented in sendgrid.go:

// DefaultClient is used if no custom HTTP client is defined
var DefaultClient = rest.DefaultClient

So just do this at the beginning of your send, where ctx is appengine.NewContext(req):

sendgrid.DefaultClient = &rest.Client{HTTPClient: urlfetch.Client(ctx)}
查看更多
看我几分像从前
3楼-- · 2019-08-03 22:50

After 8 different attempts, including trying an example published in Google Cloud docs for using Sendgrid, an example from Sendgrid blog, and trying to use deprecated versions of Sendgrid api, I found Sendgrid curl examples at:

https://sendgrid.com/docs/Classroom/Send/v3_Mail_Send/curl_examples.html

curl --request POST \
  --url https://api.sendgrid.com/v3/mail/send \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{"personalizations": [{"to": [{"email": "recipient@example.com"}]}],"from": {"email": "sendeexampexample@example.com"},"subject": "Hello, World!","content": [{"type": "text/plain", "value": "Heya!"}]}'

I then translated the HelloWorld example to into URLFetch usage

client := urlfetch.Client(ctx)

request, err := http.NewRequest("POST", "https://api.sendgrid.com/v3/mail/send", buf)
    if err != nil {
        log.Errorf(ctx, "Failed Request %v", request)
    }
    request.Header.Set("Authorization", "Bearer SENDGRID_API_KEY")
    request.Header.Set("Content-Type", "application/json")
    resp, err := client.Do(request)

One Easter weekend, later, it works!

查看更多
beautiful°
4楼-- · 2019-08-03 23:03

You were on the right track, but skipped overriding the default sendgrid client with urlfetch client.

.
.
.
func IndexHandler (w http.ResponseWriter, r *http.Request){
    ctx := appengine.NewContext(r)    
    sg := sendgrid.NewSendClient("REPLACE_WITH_API_KEY")
    bob := urlfetch.Client(ctx)

    sg.Client = bob

    request := sendgrid.GetRequest("REPLACE_WITH_API_KEY", "/v3/mail/send", "https://api.sendgrid.com")
    request.Method = "POST"
.
.
.

Explanation

The error occurs as sendgrid tries to fetch a url with the default net/http method.

Quoting AppEngine Documentation

App Engine uses the URL Fetch service to issue outbound HTTP(S) requests. To issue an outbound HTTP request, use the http package as usual, but create your client using urlfetch.Client. urlfetch.Client returns an *http.Client that uses urlfetch.Transport, which is an implementation of the http.RoundTripper interface that makes requests using the URL Fetch API.

The workaround is to override the Sendgrid client to use urlfetch

        context := appengine.NewContext(r)
        sg := sendgrid.NewSendClient(os.Getenv("SENDGRID_API_KEY"))
        sg.Client = urlfetch.Client(context)

References

  1. GCloud Documentation- Issuing HTTP(S) Requests in App Engine Golang
查看更多
登录 后发表回答