Gmail API与Go和gmail.NewService一起使用时出现无效的内存地址或空指针解引用。

huangapple go评论89阅读模式
英文:

Gmail API with Go and gmail.NewService invalid memory address or nil pointer dereference

问题

我对gmail api的新NewService功能遇到了问题。如果我使用已弃用的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\r\n", k, v)
	}
	msg += "\r\n" + string(m.Content)

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

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
}

使用旧的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
英文:

I have an issue with the new NewService functionality of gmail api. If I use the deprecated gmail.New() everything works.
With NewService() I get invalid memory address or nil pointer dereference

My implementation is the following

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\r\n", k, v)
}
msg += "\r\n" + string(m.Content)
gmailMessage.Raw = base64.RawURLEncoding.EncodeToString([]byte(msg))
return &gmailMessage
}

Using the old gmail.New() works, but it points out that the function is deprecated, so I need to change it to the new gmail.NewService. Though implementing it like below it doesn't work

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
}

Edit: the error I get is

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

答案1

得分: 0

问题并不明显,但很容易解决。
在初始化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实现的上下文中,你需要将所有在以下链接中使用的函数放入你的go文件中:

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

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

这些函数将生成一个AccessToken和Credentials,如果你按照指南正确操作,你需要将其保存为一个json文件并重复使用。credentials.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("无法读取凭据文件:", err))
}
config, err := google.ConfigFromJSON(b, gmail.GmailSendScope)
if err != nil {
return errors.New(fmt.Sprint("无法解析凭据文件配置:", 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("无法获取gmail客户端:%s", err)
}
// 创建消息
gMessage := ComposeMessage(m)
if err := doSend(gMessage, srv); err != nil {
return fmt.Errorf("无法发送邮件:%s", err)
}
fmt.Println("邮件已发送")
return nil
}
英文:

The issue was not that obvious but easy to solve.
While initializing the gmail.NewService() I needed to pass the config parameter as option, just like the previous implementation of gmail.New() was using

So before was

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

And now it is

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

In more context of the Gmail API implementation for Go, you need to take all the functions used at

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

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

and put them in your go file. These will generate both an AccessToken and the Credentials which, if you follow the guide correctly, you will need to save as a json file and reuse. credentials.json are used to update your AccessToken.

Now instead of using gmail.New(), the correct way is to use the function as shown above.

The entire sendGmail function is as follows

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
}

huangapple
  • 本文由 发表于 2022年2月17日 14:59:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/71153701.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定