问题
I want to send a transactional mail via Sendgrid when a user registers (I use devise for authentication). I had this working fine in my_mailer.rb using SMTP as follows:
def confirmation_instructions(record, token, opts={})
# SMTP header for Sendgrid - v2
# headers["X-SMTPAPI"]= {
# "sub": {
# "-CONFIRM_TOKEN-": [
# token
# ]
# },
# "filters": {
# "templates": {
# "settings": {
# "enable": 1,
# "template_id": "1111111-1111-1111-1111-111111111111"
# }
# }
# }
# }.to_json
However, prompted by Sendgrid to use v3 syntax to support newer mail templates, I changed code to the following (from the sendgrid help docs, as opposed to a real understanding):
def confirmation_instructions(record, token, opts={})
require 'sendgrid-ruby'
include SendGrid
sg = SendGrid::API.new(api_key: ENV['SENDGRID_API_KEY'])
data = JSON.parse('{
"substitutions": {
"-CONFIRM_TOKEN-": [
token
],
"template_id": "1111111-1111-1111-1111-111111111111"
}')
response = sg.client.mail._("send").post(request_body: data)
puts response.status_code
puts response.body
puts response.parsed_body
puts response.headers
Now I get the error message:'NoMethodError (undefined method `include' for #<MyMailer:0x0000000003cfa398>):'
If I comment out the 'include' line I get:'TypeError (no implicit conversion of nil into String):' on the line: "sg = SendGrid..."
I use the Gem: sendgrid-ruby (5.3.0)
Any ideas would be appreciated - I've been trying to hit on the correct syntax by trial-and-error for a while now and finally admit I am stuck.
UPDATE#1:
The first issue was I was using the wrong API_KEY env. variable (copied from 2 different help docs): "SENDGRID_API_KEY" (in code) vs. SENDGRID_APIKEY_GENERAL (set in Heroku). Fixed.
UPDATE #2:
With the "include" line commented out I now seem to be getting a JSON parse error:
JSON::ParserError (416: unexpected token at 'token
So my 2 current issues are now:
(1) I would like 'token' to be the confirmation token variable but it is not being passed
(2) Sending the below simple (1 line) content of 'data' does not throw up an error, but the appropriate template within Sendgrid is not selected:
data = JSON.parse('{
"template_id": "1111111-1111-1111-1111-111111111111"
}')
UPDATE #3:
Here's an update on the status of my issue and exactly where I am now stuck:
This code works fine (using Sendgrid v2 which I am trying to upgrade from):
def confirmation_instructions(record, token, opts={})
#
# SMTP header for Sendgrid - v2
# This works fine
#
headers["X-SMTPAPI"]= {
"sub": {
"-CONFIRM_TOKEN-": [
token
]
},
"filters": {
"templates": {
"settings": {
"enable": 1,
"template_id": "1111111-1111-1111-1111-111111111111"
}
}
}
}.to_json
This Sendgrid v3 code does not work (the email does get sent via Sendgrid but it does not select the template within Sendgrid - it just uses whatever code is in app/views/my_mailer/confirmation_instructions.html.erb):
#
# Sendgrid API v3
# This sends an email alright but it takes content from app/views/my_mailer/confirmation_instructions.html.erb
# It DOES NOT select the template within Sendgrid
#
data = JSON.parse('{
"template_id": "1111111-1111-1111-1111-111111111111",
"personalizations": [
{
"substitutions": {
"-CONFIRM_TOKEN-": "'+token+'"
}
}
]
}')
sg = SendGrid::API.new(api_key: ENV['SENDGRID_APIKEY_GENERAL2'])
response = sg.client.mail._("send").post(request_body: data)
puts response.status_code
puts response.body
puts response.parsed_body
puts response.headers
As always, any insight appreciated.
回答1:
For anyone trying to get SendGrid v3 API working with Ruby/Devise/Heroku and use SendGrid's dynamic transactional emails these tips may help you. I spent a week getting this to work and these steps (& mistakes I made) were not apparent in the various documentation:
Generating the SendGrid API key (on SendGrid website): when generating the key, the API key only appears once allowing you to copy it, from then on it is invisible. As I could not see the key later I mistakenly used the "API Key ID" in my Heroku environment variables, rather than the true API Key.
Ensure the name you give the key in Heroku (for me: "SENDGRID_APIKEY_GENERAL") matches the code you use to reference it i.e. sg = SendGrid::API.new(api_key: ENV['SENDGRID_APIKEY_GENERAL'])
For sending variables to be substituted in the template use "dynamic_template_data" and not "substitutions". This should be within the "personalizations" section (see code example below).
I found it useful to refer to the Sendgrid dynamic template ID by using an environment variable in Heroku (for me: 'SENDGRID_TRANS_EMAIL_CONFIRM_TEMPLATE_ID') as opposed to hard-coding in Ruby (just allowed me to experiment with different templates rather than changing code).
The correct syntax for using a variable in the JSON string in Ruby is e.g. "CONFIRM_TOKEN": "'+token+'" (see code example below)
Do not use other characters in the name: i.e. "CONFIRM_TOKEN" worked but "-CONFIRM_TOKEN-" did not work
In the HTML of the transactional email template on SendGrid use this syntax for the substitution: {{CONFIRM_TOKEN}}
When creating a transactional template on SendGrid you can only have a 'design' view or a 'code' view not both. You must select at the start when creating the template and cannot switch after.
In the devise confirmations_instructions action refer to the user as a record (e.g. email) as record.email
Gemfile: gem 'rails', '5.2.2' ruby '2.6.1' gem 'devise', '4.6.1' gem 'sendgrid-ruby', '6.0.0'
Here is my successful ruby code that I have in my_mailer.rb:
def confirmation_instructions(record, token, opts={})
data = JSON.parse('{
"personalizations": [
{
"to": [
{
"email": "'+record.email+'"
}
],
"subject": "Some nice subject line content",
"dynamic_template_data": {
"CONFIRM_TOKEN": "'+token+'",
"TEST_DATA": "hello"
}
}
],
"from": {
"email": "aaaa@aaaa.com"
},
"content": [
{
"type": "text/plain",
"value": "and easy to do anywhere, even with Ruby"
}
],
"template_id": "'+ENV['SENDGRID_TRANS_EMAIL_CONFIRM_TEMPLATE_ID']+'"
}')
sg = SendGrid::API.new(api_key: ENV['SENDGRID_APIKEY_GENERAL'])
response = sg.client.mail._("send").post(request_body: data)
puts response.status_code
puts response.body
puts response.headers
回答2:
You cannot include
a module in a method. You have to include it in your class, so outside of the methode, like
class SomethingMailer
require 'sendgrid-ruby'
include SendGrid
def confirmation_instructions(record, token, opts={})
...
end
end
For your third update problem:
You are not sending a JSON but instead you are creating a JSON, then parsing it into a hash, then sending that hash, instead of the JSON.
JSON.parse #parses a JSON into a Hash
You should do the opposite and have a hash that you transform into a JSON
Something like
data = {
template_id: your_template_id # or pass a string
personalizations: [
...
]
}
Then you call
data_json = data.to_json
response = sg.client.mail._("send").post(request_body: data_json)
However this does not explain why your template in app/views/my_mailer/confirmation_instructions.html.erb
gets sent. So I think you are either calling a different mailer_method at the same time, or you are not calling your actual confirmation_instructions method at all. Try to confirm that you SendGrid Methods actually is called and see what it returns. It should have returned some kind of error before, when you were sending a hash instead of a string.
来源:https://stackoverflow.com/questions/56967786/sendgrid-v3-not-working-for-me-with-rails-my-mailer-rb