I am using SendGrid mailhelper (as part of C# SDK) to send email. I need to send to multiple users, and hence I am using Personalization.
I get an error : Bad Request
This is my code:
static async Task Execute(string sub, string body, List<Recipient> recipients)
{
string apiKey = Environment.GetEnvironmentVariable("SendGrid_ApiKey", EnvironmentVariableTarget.User);
dynamic sg = new SendGridAPIClient(apiKey);
SendGrid.Helpers.Mail.Email from = new SendGrid.Helpers.Mail.Email("test1#gmail.com");
string subject = sub;
Personalization personalization = new Personalization();
SendGrid.Helpers.Mail.Email emails = new SendGrid.Helpers.Mail.Email();
var i = 0;
foreach (var recp in recipients)
{
emails.Address = recp.Email;
emails.Name = recp.FirstName + " " + recp.LastName;
personalization.AddTo(emails);
i++;
}
SendGrid.Helpers.Mail.Email to = new SendGrid.Helpers.Mail.Email("test1#gmail.com");
Content content = new Content("text/plain", body);
Mail mail = new Mail(from, subject, to, content);
mail.AddPersonalization(personalization);
dynamic response = await sg.client.mail.send.post(requestBody: mail.Get());
}
I appreciate if someone could advise me what am I doing incorrect.
Sendgrid API responds with bad request when there are more than 1 email address that is the same in the Personalization object. Make sure all the emails are unique
Related
I need to send a simple email, but using authentication. Using AppPasswords is not an option.
My difficulty is to retrieve the response after sending the authorization and at what point should I generate the email. I know that to send the email I must use the password and authentication user identity.
Here's what I've done.
Credentials are read from a file
ClientSecrets varClientSecrets = new ClientSecrets();
UserCredential credential;
using (var stream =
new FileStream(pathFile, FileMode.Open, FileAccess.Read,FileShare.Read))
{
varClientSecrets = GoogleClientSecrets.FromStream(stream).Secrets;
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
varClientSecrets,
new[] { "email", "profile", "https://mail.google.com/" },
"use",
CancellationToken.None,
new FileDataStore(credPath, true)
).Result;
strCLIENT_ID = varClientSecrets.ClientId;
strCLIENT_SECRET = varClientSecrets.ClientSecret;
}
// Create Gmail API service.
var service = new GmailService(new BaseClientService.Initializer
{
HttpClientInitializer = credential,
ApplicationName = "XXX"
});
Get credential data
// Define parameters of request.
UsersResource.LabelsResource.ListRequest request = service.Users.Labels.List("me");
ConsultaWeb CW = new ConsultaWeb();
IRestResponse irrRetornoServidor = null;
strSCOPE = credential.Token.Scope;
strREDIRECT_URI = "https://accounts.google.com/o/oauth2/v2/auth";
strTokenURL = "https://www.googleapis.com/oauth2/v4/token";
/// model: {Authorization: token_type base64(userEmail:userPassword)}
strHeader = "Authorization: " + credential.Token.TokenType + " "
+ Base64.EncodeToBase64(userEmail + ":" + userPassword);
strURL = "https://accounts.google.com/o/oauth2/v2/auth"
+ "?scope=" + strSCOPE
+ "&access_type=offline"
+ "&include_granted_scopes=true"
+ "&response_type=code"
+ "&state=state_parameter_passthrough_value"
+ "&redirect_uri=" + strREDIRECT_URI
+ "&client_id=" + strCLIENT_ID;
strServidorRetorno = CW.RequisicaoWebREST(strURL, GET, JSON
, out irrRetornoServidor, "", "", "", "", strHeader);
// Return from OAuth 2.0 server
// OAuth 2.0 server responds to your application's access request
// using the URL specified in the request.
////If the user approves the access request, the response
//will contain an authorization code. If the user does not approve the request,
//the response will contain an error message.
//The authorization code or error message that is returned to the web server
//appears on the query string, as shown below:
strAuthCode = "";
Get response from server
if ((short)irrRetornoServidor.StatusCode == (short)RegrasGlobais.HTTPStatusCode.OK)
{
if (irrRetornoServidor.ResponseUri.AbsolutePath.Contains("error"))
{
// An error response:
// https://oauth2.example.com/auth?error=access_denied
return -1;
}
else if (irrRetornoServidor.ResponseUri.AbsolutePath.Contains("ServiceLogin"))
{
//An authorization code response
// https://oauth2.example.com/auth?code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7
// strAuthCode = "P7q7W91a-oMsCeLvIaQm6bTrgtp7";
// how to get the autorization response, if this open in the browser
strAuthCode = irrRetornoServidor.ResponseUri.Query.ToString();
}
}
else
{
return -1;
}
send the email
// Gmail API server address
//MailMessage msg = new MailMessage();
// build mail
thks
my problem was and how to do the authentication before sending the email. Now I got it. Thanks.
Im a little confused as to what it is you are trying to do. You say you want to send an email using the SMTP server yet you are connecting to the gmail api. Why not just send your emails via the gmail api then?
Assuming you have just gone in the wrong direction. You should know that the SmtpClient can handle the client from the google .Net client library directly. Just let it get its access token when needed.
await client.AuthenticateAsync (oauth2, CancellationToken.None);
If all you want to do is send an email from the smtp server. Try the following sample.
Full sample
using Google.Apis.Auth.OAuth2;
using Google.Apis.Util.Store;
using MailKit.Net.Smtp;
using MailKit.Security;
using MimeKit;
var to = "test#Gmail.com";
var from = "test#gmail.com";
var path = #"C:\YouTube\dev\credentials.json";
var scopes = new[] { "https://mail.google.com/" };
var credential = GoogleWebAuthorizationBroker.AuthorizeAsync(GoogleClientSecrets.FromFile(path).Secrets,
scopes,
"GmalSmtpUser",
CancellationToken.None,
new FileDataStore(Directory.GetCurrentDirectory(), true)).Result;
var message = new EmailMessage()
{
From = from,
To = to,
MessageText = "This is a test message using https://developers.google.com/gmail/imap/xoauth2-protocol",
Subject = "Testing GmailSMTP with XOauth2"
};
try
{
using (var client = new SmtpClient())
{
client.Connect("smtp.gmail.com", 465, true);
var oauth2 = new SaslMechanismOAuth2 (message.From, credential.Token.AccessToken);
await client.AuthenticateAsync (oauth2, CancellationToken.None);
client.Send(message.GetMessage());
client.Disconnect(true);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
public class EmailMessage
{
public string To { get; set; }
public string From { get; set; }
public string Subject { get; set; }
public string MessageText { get; set; }
public MimeMessage GetMessage()
{
var body = MessageText;
var message = new MimeMessage();
message.From.Add(new MailboxAddress("From a user", From));
message.To.Add(new MailboxAddress("To a user", To));
message.Subject = Subject;
message.Body = new TextPart("plain") { Text = body };
return message;
}
}
I am trying to send the same automatic email to multiple email addresses but I can't get it to work.
[HttpGet("largeorderemail")]
public IActionResult LargeOrderEmail()
{
try
{
//var bodyString = $"<h3>{msg}</h3><br/><p> Visit the site <a href='{Startup.appSettings.AllowOrigin}/lidarweb'> LiDAR GIS portal.</a></p>";
var bodyString = $"<h3>email body</h3>" +
<br/>" +
var emailService = new Email { To = "info#tel.net" };
var response = emailService.ExecuteLargeOrder(bodyString);
return Ok();
}
catch (Exception e)
{
Log.Error(e);
return NotFound();
}
}
public async Task<Response> ExecuteLargeOrder(string bodyString)
{
var fromAddr = new EmailAddress(from, "Info");
subject = "large order";
var toAddr = new EmailAddress(to, "User");
plainTextContent = "";
htmlContent = bodyString;
var msg = MailHelper.CreateSingleEmail(fromAddr, toAddr, subject, plainTextContent, htmlContent);
var response = await client.SendEmailAsync(msg);
return response;
}
When I send an email to a single address, it works. Like so: var emailService = new Email { To = "info#tel.net" };
but when I try something like this, it doesn't send the email var emailService = new Email { To = "info#tel.net, info#gmail.com" };
I also tried separating the address like so var emailService = new Email { To = "info#tel.net; info#gmail.com" }; but this also doesn't work.
Any suggestions?
Instead of putting Email addresses, try doing this way. Keep all your Email address in Array and try looping through the Array so that you can achieve your goal.
I am using SendGrid, and I want to add one or more category to the email, but the added category hadn't been sent!
This is the code:
internal class Example
{
private static void Main()
{
Execute().Wait();
}
static async Task Execute()
{
//FYI, the following three variables are not real
var apiKey = "SG.XXX";
var fromEmail = "";
var toEmail = "";
var client = new SendGridClient(apiKey);
var from = new EmailAddress(fromEmail);
var subject = "Sending with SendGrid is Fun";
var to = new EmailAddress(toEmail);
var plainTextContent = "and easy to do anywhere, even with C#";
var htmlContent = "<strong>and easy to do anywhere, even with C#</strong>";
var msg = MailHelper.CreateSingleEmail(from, to, subject, plainTextContent, htmlContent);
msg.AddHeader("category", "cat1"); //This line does nothing!
var response = await client.SendEmailAsync(msg);
}
}
Thanks Kami, I tried your answer and it worked properly.
I replaced this line msg.AddHeader("category", "cat1"); with msg.AddCategory("cat1");
I have a method in my MVC Controller which I am trying to call an API from in order to send an email (The Mail Message is generated in the MVC Method)
Create Mail message is as follows and this works fine.
public static MailMessage CreateMailMessage(string from,
string to,
string cc,
string bcc,
string subject,
string body,
List<Attachment> attachments,
string differentServer,
bool eatError)
{
MailMessage mm = new MailMessage();
mm.From = new MailAddress(from);
mm.Subject = subject;
mm.Body = body;
mm.IsBodyHtml = true;
//send to multiple addresses separated by semi-colon or comma
if (!string.IsNullOrEmpty(to))
{
var toAddresses = to.Split(new char[] { ';', ',' });
foreach (string toAddress in toAddresses)
{
if (!string.IsNullOrWhiteSpace(toAddress))
mm.To.Add(toAddress);
}
}
if (!string.IsNullOrEmpty(cc))
{
mm.CC.Add(cc);
}
if (!string.IsNullOrEmpty(bcc))
{
mm.Bcc.Add(bcc);
}
if (attachments != null)
{
foreach (Attachment attachment in attachments)
{
mm.Attachments.Add(attachment);
}
}
return mm;
}
However in order to send the email I need to write and call a WebAPI method - sending the email is fine - just not sure how to post the mail message and some other properties to a WebAPI Method?
So My WebAPI method at the minute is like:
/// <summary>
/// Method to email the Report
/// </summary>
/// <returns></returns>
[HttpPost]
[Route("api/Document/EmailReport/")]
public HttpResponseMessage EmailFarmFeatures([FromBody]MailMessage email)
{
return Request.CreateResponse(HttpStatusCode.OK);
}
I am attempting to call this WebAPI from MVC Method as below:
private void EmailReport(string reportName, byte[] bytes)
{
ContentType ct = new ContentType(MediaTypeNames.Application.Octet);
var attachments = new List<Attachment>();
var attach = new Attachment(new MemoryStream(bytes), ct);
attach.ContentDisposition.FileName = reportName;
attachments.Add(attach);
string strFrom = ConfigurationManager.AppSettings["FromEmail"];
string strTo = ConfigurationManager.AppSettings["ToEmail"];
string subject = string.Format("Customer Report - {0}", customerId);
string body = string.Format("Report for Customer {0} attached.", customerId);
string mailServer = ConfigurationManager.AppSettings["SmtpServer"];
MailMessage message = EmailHelper.CreateMailMessage(strFrom, strTo, "", "", subject, body, attachments, mailServer, false);
using (var client = new HttpClient())
{
var requestBody = JsonConvert.SerializeObject(message);
var postRequest = new StringContent(requestBody, Encoding.UTF8, "application/json");
var response = client.PostAsync("http://localhost/myWS/api/Document/EmailReport/", postRequest).GetAwaiter().GetResult();
if (response.StatusCode != HttpStatusCode.OK)
{
throw new Exception("Error occured emailing report");
}
}
}
I am currently getting an error on this line:
var requestBody = JsonConvert.SerializeObject(message);
[InvalidOperationException: Timeouts are not supported on this stream.]
System.IO.Stream.get_ReadTimeout() +57
GetReadTimeout(Object ) +81
Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue(Object target) +114
[JsonSerializationException: Error getting value from 'ReadTimeout' on 'System.IO.MemoryStream'.]
Newtonsoft.Json.Serialization.DynamicValueProvider.GetValue(Object target) +274
I believe it is something to do with attemptiong to seriliaze the memory stream attachment though I am not sure what the fix is to make sure this attachment gets included on the POST Request to the API Method
MailMessage isn't Serializable Just create a Separate regular object which represents a mail message.
Have a look at this Thread I guess it's pretty much what you need:
(https://discuss.hangfire.io/t/help-sending-email-mailmessage-deserialize-problem/354/5)[https://discuss.hangfire.io/t/help-sending-email-mailmessage-deserialize-problem/354/5]
I am sending an email from .net core application using MailKit, and it will sent it successfully.
But I want to use HTML template to send email with MailKit in .Net core.
Here are the code currently sending email with static body part
var emailMessage = new MimeMessage();
if (!string.IsNullOrWhiteSpace(cc))
{
emailMessage.Cc.Add(new MailboxAddress(cc));
}
else if (!string.IsNullOrWhiteSpace(EmailUserNameCC))
{
emailMessage.Cc.Add(new MailboxAddress(EmailUserNameCC));
}
if (!string.IsNullOrWhiteSpace(EmailUserNameBCC))
{
emailMessage.Bcc.Add(new MailboxAddress(EmailUserNameBCC));
}
emailMessage.From.Add(new MailboxAddress(mailFrom));
emailMessage.To.Add(new MailboxAddress(mailTo));
emailMessage.Subject = subject;
if (!string.IsNullOrWhiteSpace(replyTo))
{
emailMessage.InReplyTo = replyTo;
}
var builder = new BodyBuilder();// { TextBody = message };
builder.HtmlBody = message;
if (attachments != null && attachments.Count > 0)
{
foreach (var item in attachments)
{
builder.Attachments.Add(item.Key, item.Value);
}
builder.HtmlBody = builder.HtmlBody + " \n" + " PFA";
}
var multipart = new Multipart("mixed");
multipart.Add(new TextPart("html") { Text = message });
emailMessage.Body = builder.ToMessageBody();
using (var client = new SmtpClient())
{
var credentials = new NetworkCredential
{
UserName = EmailUserName,
Password = EmailPassword
};
if (!client.IsConnected)
{
client.Connect(SmtpHost, Convert.ToInt32(EmailHostPort));
client.Authenticate(EmailUserName, EmailPassword);
}
client.MessageSent += c_EmailReached;
client.Send(emailMessage);
}
Now, I want to use HTML template to replace body part.
So how can I use HTML Template with MailKit in .Net Core ?
Additional:
-> Also the special characters are not showing in actual email after sending email with html template. For some special characters it is displaying � . So how can I resolved this, to show special characters also.
Thanks.
You can use StreamReader to read the source file and assign it to your builder.HtmlBody.
using (StreamReader SourceReader = System.IO.File.OpenText(path to your file))
{
builder.HtmlBody = SourceReader.ReadToEnd();
}