带有go和gmail.NewService的Gmail API内存地址无效或无指针取消引用

人气:673 发布:2022-10-16 标签: gmail-api go

问题描述

我对Gmail API的新服务功能有一个问题。如果我使用不推荐使用的gmail.New(),则一切正常。 使用NewService()我得到invalid memory address or nil pointer dereference

我的实现如下

type MailData struct {
    To      string
    Name    string
    Subject string
    Content template.HTML
}    

func doSend(msg *gmail.Message, srv *gmail.Service) error {
    _, err := srv.Users.Messages.Send("me", msg).Do()
    if err != nil {
        return err
    }
    return nil
}

func ComposeMessage(m models.MailData) *gmail.Message {

    var gmailMessage gmail.Message

    from := mail.Address{Name: "Sender", Address: os.Getenv("MAIL_FROM")}
    replyTo := os.Getenv("MAIL_REPLYTO")
    to := mail.Address{Name: m.Name, Address: m.To}

    header := make(map[string]string)
    header["From"] = from.String()
    header["Reply-To"] = replyTo
    header["To"] = to.String()
    header["Subject"] = m.Subject
    header["MIME-Version"] = "1.0"
    header["Content-Type"] = "text/html; charset="utf-8""
    header["Content-Transfer-Encoding"] = "base64"

    var msg string
    for k, v := range header {
        msg += fmt.Sprintf("%s: %s
", k, v)
    }
    msg += "
" + string(m.Content)

    gmailMessage.Raw = base64.RawURLEncoding.EncodeToString([]byte(msg))
    return &gmailMessage
}

使用旧的gmail.New()可以工作,但它指出该函数已弃用,因此我需要将其更改为新的gmail.NewService。虽然像下面这样实现它,但它不起作用

func sendGMail(m models.MailData) error {
    credentials := "../gmail_credentials.json"

    ctx := context.Background()
    srv, err := gmail.NewService(
        ctx,
        option.WithCredentialsFile(credentials),
        option.WithScopes("https://www.googleapis.com/auth/gmail.send"),
    )
    if err != nil {
        return errors.New(fmt.Sprintf("unable to retrieve gmail client: %s", err))
    }

    // Create message
    gMessage := ComposeMessage(m)

    if err := doSend(gMessage, srv); err != nil {
        return errors.New(fmt.Sprintf("could not send mail: %s", err))
    }
    fmt.Println("Email sent")

    return nil
}

编辑:我收到的错误是

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x9a161b]

goroutine 13 [running]:
golang.org/x/oauth2/authhandler.authHandlerSource.Token({{0xc2fb30, 0xc00003c108}, 0xc0002a1340, 0x0, {0x0, 0x0}})
        /home/joss/go/pkg/mod/golang.org/x/oauth2@v0.0.0-20211104180415-d3ed0bb246c8/authhandler/authhandler.go:48 +0x5b
golang.org/x/oauth2.(*reuseTokenSource).Token(0xc00011d1e0)
        /home/joss/go/pkg/mod/golang.org/x/oauth2@v0.0.0-20211104180415-d3ed0bb246c8/oauth2.go:304 +0xd5
golang.org/x/oauth2.(*Transport).RoundTrip(0xc00011d220, 0xc000124600)
        /home/joss/go/pkg/mod/golang.org/x/oauth2@v0.0.0-20211104180415-d3ed0bb246c8/transport.go:45 +0xa7
net/http.send(0xc000124600, {0xc1d200, 0xc00011d220}, {0xb13600, 0xc000263701, 0x0})
        /usr/local/go/src/net/http/client.go:252 +0x5d8
net/http.(*Client).send(0xc000483200, 0xc000124600, {0xc0002637f8, 0x4f49b5, 0x0})
        /usr/local/go/src/net/http/client.go:176 +0x9b
net/http.(*Client).do(0xc000483200, 0xc000124600)
        /usr/local/go/src/net/http/client.go:725 +0x908
net/http.(*Client).Do(...)
        /usr/local/go/src/net/http/client.go:593
google.golang.org/api/internal/gensupport.SendRequest({0x0, 0x0}, 0xb33a63, 0xc000124600)
        /home/joss/go/pkg/mod/google.golang.org/api@v0.30.0/internal/gensupport/send.go:43 +0xb8
google.golang.org/api/gmail/v1.(*UsersMessagesSendCall).doRequest(0xc000263e10, {0xb2b9fa, 0x4})
        /home/joss/go/pkg/mod/google.golang.org/api@v0.30.0/gmail/v1/gmail-gen.go:6836 +0xa05
google.golang.org/api/gmail/v1.(*UsersMessagesSendCall).Do(0xc000263e10, {0x0, 0x1b, 0xb4e56a})
        /home/joss/go/pkg/mod/google.golang.org/api@v0.30.0/gmail/v1/gmail-gen.go:6848 +0x78
github.com/user/mailprj/internal.doSend(0xc0000f6180, 0x12)
        /home/joss/user/mailprj/internal/gmail-api.go:159 +0xa5
github.com/user/mailprj/internal.sendGMail({{0xb3cd52, 0x12}, {0xb2b38e, 0x4}, {0xb4e56a, 0x29}, {0xc000610000, 0x8a66}})
        /home/joss/user/mailprj/internal/gmail-api.go:149 +0x1b2
github.com/user/mailprj/internal.ListenForGMail.func1()
        /home/joss/user/mailprj/internal/gmail-api.go:114 +0xc6
created by github.com/user/mailprj/internal.ListenForGMail
        /home/joss/user/mailprj/internal/gmail-api.go:111 +0x25
exit status 2

推荐答案

这个问题不是很明显,但很容易解决。 在初始化gmail.NewService()时,我需要将配置参数作为选项传递,就像gmail.New()的上一个实现使用

以前是

client := getClient(config)
srv, err := gmail.New(client)

现在是

client := getClient(config)
ctx := context.Background()
srv, err := gmail.NewService(
    ctx,
    option.WithHTTPClient(client),
)

在Go的Gmail API实现的更多上下文中,您需要使用

中使用的所有函数

https://developers.google.com/gmail/api/quickstart/go或

https://github.com/googleworkspace/go-samples/blob/master/gmail/quickstart/quickstart.go

并将它们放入您的围棋文件中。它们将生成AccessToken和凭据,如果您正确按照指南操作,则需要另存为json文件并重复使用。Json用于更新您的AccessToken。

现在不使用gmail.New(),正确的方法是使用如上所示的函数。

整个sendGmail函数如下

func sendGMail(m models.MailData) error {
    credentials := "../gmail_credentials.json"
    b, err := ioutil.ReadFile(credentials)
    if err != nil {
        return errors.New(fmt.Sprint("unable to read credentials file:", err))
    }

    config, err := google.ConfigFromJSON(b, gmail.GmailSendScope)
    if err != nil {
        return errors.New(fmt.Sprint("unable to parse credentials file config:", err))
    }

    client := getClient(config)
    ctx := context.Background()
    srv, err := gmail.NewService(
        ctx,
        option.WithHTTPClient(client),
        option.WithScopes("https://www.googleapis.com/auth/gmail.send"),
    )
    if err != nil {
        return fmt.Errorf("unable to retrieve gmail client: %s", err)
    }

    // Create message
    gMessage := ComposeMessage(m)

    if err := doSend(gMessage, srv); err != nil {
        return fmt.Errorf("could not send mail: %s", err)
    }
    fmt.Println("Email sent")

    return nil
}

613