问题描述
我对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
}