SendGrid works, but still throws exception not catchable in catch() block - c#

Note: To the moderator that incorrectly closed this question, it's completely different from the generic nullref question. This is specific to SendGrid.
I believe I'm following pretty close to documented SendGrid usage:
public async Task<string> SendEmailSendGrid(string emailTo, string subject, string body) {
var apiKey = SafeTrim(ConfigurationManager.AppSettings["SendGridAPIKey"]);
var client = new SendGridClient(apiKey);
var from = new EmailAddress(SafeTrim(ConfigurationManager.AppSettings["SendGridEmail"]));
var to = new EmailAddress(emailTo);
var msg = MailHelper.CreateSingleEmail(from, to, subject, string.Empty, body);
try {
var response = await client.SendEmailAsync(msg);
//return response;
return "SUCCESS";
} catch (Exception ex) {
return "ERROR in SendEmailSendGrid(): " + ex.Message;
}
}
And the caller:
var result = utils.SendEmailSendGrid(decodedEmail, "email test", "This is a test email using SendGrid.");
And the error I get every time EVEN THOUGH IT WORKS and the email actually sends and arrives in my inbox:
Object reference not set to an instance of an object.
That error occurs on this line:
var response = await client.SendEmailAsync(msg);
I verified that all my variables are populated as expected - none are null or empty. I am passing an empty string to the plain text param (because I always want HTML contents), but I also tried passing that some content and it made no difference; same error.
A strange thing: this blows up so hard that my catch block is never entered. Instead, as soon as the exception is thrown, this full-screen window comes up in my VS2022:
So it is working and sending the email, but why the heavy crash? What am I doing wrong?

The method is awaitable:
public async Task<string> SendEmailSendGrid(...
Yet, the caller is not awaiting the result:
var result = utils.SendEmailSendGrid(decodedEmail, ...
Either await the result:
var result = await utils.SendEmailSendGrid(decodedEmail, ...
Or, if the invoking method is not an async method:
var result = utils.SendEmailSendGrid(decodedEmail, ...).GetAwaiter().GetResult();
Visual Studio is not breaking inside of your try/catch b/c the exception is in the .NET framework by not awaiting the result. You should be able to resolve that by enabling "just my code" in the visual studio debugger settings (IIRC).

Related

SendGrid SendEmailAsync() - uncatchable exception thrown [duplicate]

This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed last year.
Note: To the moderator that closed this, it's completely different from the generic nullref question. If you read my post, it's obviously specific to SendGrid.
I believe I'm following pretty close to documented SendGrid usage:
public async Task<string> SendEmailSendGrid(string emailTo, string subject, string body) {
var apiKey = SafeTrim(ConfigurationManager.AppSettings["SendGridAPIKey"]);
var client = new SendGridClient(apiKey);
var from = new EmailAddress(SafeTrim(ConfigurationManager.AppSettings["SendGridEmail"]));
var to = new EmailAddress(emailTo);
var msg = MailHelper.CreateSingleEmail(from, to, subject, string.Empty, body);
try {
var response = await client.SendEmailAsync(msg);
//return response;
return "SUCCESS";
} catch (Exception ex) {
return "ERROR in SendEmailSendGrid(): " + ex.Message;
}
}
And the caller:
var result = utils.SendEmailSendGrid(decodedEmail, "email test", "This is a test email using SendGrid.");
And the error I get every time EVEN THOUGH IT WORKS and the email actually sends and arrives in my inbox:
Object reference not set to an instance of an object.
I verified that all my variables are populated as expected - none are null or empty. I am passing an empty string to the plain text param (because I always want HTML contents), but I also tried passing that some content and it made no difference; same error.
A strange thing: this blows up so hard that my catch block is never entered. Instead, as soon as the exception is thrown, this full-screen window comes up in my VS2022:
So it is working and sending the email, but why the heavy crash? What am I doing wrong?
Can you try this my approach.
I am using a single instance approach, try it and lets see.
public async Task SendAsync(IdentityMessage message)
{
var apiKey = new MvcApplication().SENDGRID_APIKEY;
var client = new SendGridClient(apiKey);
var msg = new SendGridMessage()
{
From = new EmailAddress("noreply#questersworld.net", "Questersworld Team"),
Subject = message.Subject,
HtmlContent = "<table width=\"80%\"><tr><td><img src=\"http://www.questersworld.net/Images/quester.png\" width=\"50\" height=\"50\"> Questers World <p><strong>Welcome, Questersworld Participant!</strong></p><br> We value your connection.<br><p>" + message.Body + "</p><p>visit www.questersworld.net </p><br><strong>© Questersworld.net</strong></td></tr></table>"
};
msg.AddTo(new EmailAddress(message.Destination, "Questersworld Participant"));
var response = await client.SendEmailAsync(msg);
}
Don't mind my info. Just try this and see

MS Graph Service Client - How to prove a subscription was created?

I have the following code: (which used to work and really should work from what I understand from MS' documentation)
[Route("msgraphsubscription")]
[HttpPost]
[AllowAnonymous]
public async Task<String> msgraphsubscription()
{
try{
this.userId = "<guid>"
GraphServiceClient graphClient = await GenerateGraphAuthToken(this.accountManagerParameters);
var subscription = new Subscription
{
ChangeType = "updated",
NotificationUrl= notificationURL,
Resource = $"users/{userId}/drive/root",
ExpirationDateTime = DateTimeOffset.Parse("2021-04-23T18:23:45.9356913Z"),
ClientState = "secretClientValue",
LatestSupportedTlsVersion = "v1_2"
};
await graphClient.Subscriptions
.Request()
.AddAsync(subscription);
return $"New subscriptionID: {subscription.Id.ToString()}";
}
catch(Exception ex){
Console.Write(ex);
return "";
}
When I run this code, I get the following error on the return statement in the try{} block:
{System.NullReferenceException: Object reference not set to an instance of an object.
at msgraphtests.msgraphsubscription() in /src/demos/msgraph/SubscriptionManager.cs:line 132}
error CS1733: Expected expression
What's Broken / Not working
The code actually creates a subscription. But... it doesn't return the subscription ID.
What I've Checked
Verified data. Using Postman, I have created a subscription request using the exact same user, and notification url. I pasted both notification URLs from the POSTMAN request and from my app into a text editor, along with both user ids. Then I do a search and verify that the URL from postman actually matches the one from the vscode file. So i'm fairly certain it's not a typo in those fields.
I put a breakpoint though on the return statement, and checked out the contents of my "subscription" variable and this is what it shows:
{Microsoft.Graph.Subscription}
AdditionalData [IDictionary]:null
ApplicationId [string]:null
ChangeType [string]:"updated"
ClientState [string]:"secretClientValue"
CreatorId [string]:null
EncryptionCertificate [string]:null
EncryptionCertificateId [string]:null
ExpirationDateTime:{2021-04-23 6:23:45 PM +00:00}
Resource [string]:"users//drive/root"
But in postman, i see this new subscription:
{
"id": "a5234fffd-75f0-3364-bd12-ddddassf",
"resource": "users/<userguid>/drive/root",
"applicationId": "asdfasdf-97ec-1b33-8888-asdfasdf",
"changeType": "updated",
"clientState": null,
"notificationUrl": "<mynotificationURL>",
"notificationQueryOptions": null,
"lifecycleNotificationUrl": null,
"expirationDateTime": "2021-04-23T18:23:45.9356913Z",
"creatorId": "asdfr-52f5-4822-fffs-afwerwer",
"includeResourceData": null,
"latestSupportedTlsVersion": "v1_2",
"encryptionCertificate": null,
"encryptionCertificateId": null
},
I changed the code to explicitly request the subscription.Id to be returned like this:
await graphClient.Subscriptions
.Request()
.Select(subscription => new {
subscription.Id,
subscription.ExpirationDateTime
})
.AddAsync(subscription);
return $"New subscriptionID: {subscription.ToString()}";
There are no errors... but the id with a value of Null is still being returned to me.
Question
Does anyone know of another way to "prove" the subscription was created using the subscription object itself? (what artifacts can i check in the subscription object?) Did something change on the MS end? cuz this used to work just fine.
THanks.
By looking at your code, it seems that you're expecting that Microsoft's code updates the Subscription object you pass into .AddAsync() method.
However, looking at Microsoft's implementation of the method, they are returning a whole new object back. So, you should do the following:
Subscription subscription = await graphClient.Subscriptions
.Request()
.AddAsync(subscription);

async/await seems to hang inside web method

In C# I have a web service with an operation result defined as below:
OperationResult<createAccountResponse> CreateAccount(string token, createAccountServiceModel model);
Inside that method I call another method with a signature indicating it is async, like so:
var sendEmailInvite = this.SendExhibitorInviteEmailAsync(new ExhibitorInviteEmailPartialRequest()
{
CompanyId = company.CompanyID,
EventId = event.EventID
});
And inside SendExhibitorInviteEmailAsync I await a method which is also marked as async. Here is that method (snipped for brevity)
public async Task<ExhibitorInviteEmailResponse> SendExhibitorInviteEmailAsync(ExhibitorInviteEmailResolvedRequest request)
{
ExhibitorInviteEmailResponse response = null;
try
{
response = new ExhibitorInviteEmailResponse();
var apiKey = "snip";
var client = new SendGridClient(apiKey);
var msg = new SendGridMessage();
msg.SetFrom(new EmailAddress("noreply#domain.com", "Display name"));
msg.AddTo(new EmailAddress(request.EmailAddress, request.AccountOwnerFirstName));
msg.SetTemplateId("snipped");
\
msg.SetTemplateData(dynamicTemplateData);
await client.SendEmailAsync(msg);
}
catch (Exception ex)
{
response = new ExhibitorInviteEmailResponse
{
Success = false,
Error = true,
ErrorMessage = ex.Message
};
}
return response;
}
If the email is meant to be sent (flag field in the json) then I start working on the email.If no email is meant to be sent, the whole method takes about a second which was what it was before.
The issue I am having is when I run this method from Postman or from C# generated by Postman, it seems the async code for sending the email causes the duration of the request to be 30+ seconds - so it seems like something is not waiting for the email to send? When I run this via a browser it works in 1-2 seconds with no delay.
What is the recommended flow when using Postman and async? Do all internal method's parents have to await as well?

Web API 2 return an error and make http.post catch it

Up to now if a web api 2 error happened and I caught it, I'd return a custom object and fill in the error message from the catch. This would however make the actually http.post() go into success method instead of the error and then I'd have to look at my own boolean success variable and if true all good, if false show error. This is kind of annoying as I have to look for errors in 2 different places for 2 different reasons. From Web API 2 is there a way I can make the http.post() trigger the error callback while I fill out the error message if I catch an error in the web api controller?
[HttpPost]
public MyResponseObject UpdateData(RequestObject req)
{
MyResponseObject resp = new MyResponseObject();
resp.Success = true;
try{
// error happens here
}catch(Exception ex){
resp.Success = false;
resp.Msg = ex.Message;
}
return resp;
}
The http.post() call will still be successful but now I have to look in the success callback for my resp.Success to see if it was REALLY successful or not. Sure the API call was able to be made, but something went wrong inside of it. I'd like to just be able to display that message and fail the call so the http.post() error callback is called with the exception message.
Just throw an exception:
throw new HttpResponseException(HttpStatusCode.InternalServerError);
If you want to customize the response that is returned you can create a HttpResponseMessage with more detail:
var response = new HttpResponseMessage(HttpStatusCode.InternalServerError)
{
Content = new StringContent("We messed up"),
ReasonPhrase = "Error"
}
throw new HttpResponseException(resp);
Documentation here

How do I include content with a GET request?

EDIT: Please note, I know the heart of the problem lies in the service I need to communicate with is not following protocol. This is software which I am not capable of touching and will not be changed anytime soon. Thus, I need help with circumventing the problem and breaking protocol. Development at its finest!
I'm attempting to communicate with an external service. Whomever made it decided to split various calls into not just different folders, but also HTTP request types. The problem here, is that I need to send a GET request that includes content.
Yes, this violates the protocol.
Yes, this works if I formulate the call using Linux commands.
Yes, this works if I manually craft the call in Fiddler (although Fiddler gets angry at the breach of protocol)
When I craft my call, it's wrapped in an async method. Sending it, however, results in an error:
Exception thrown: 'System.Net.ProtocolViolationException' in mscorlib.dll ("Cannot send a content-body with this verb-type.")
Code for the call:
/// <summary>
/// Gets a reading from a sensor
/// </summary>
/// <param name="query">Data query to set data with</param>
/// <returns></returns>
public async Task<string> GetData(string query)
{
var result = string.Empty;
try
{
// Send a GET request with a content containing the query. Don't ask, just accept it
var msg = new HttpRequestMessage(HttpMethod.Get, _dataApiUrl) { Content = new StringContent(query) };
var response = await _httpClient.SendAsync(msg).ConfigureAwait(false);
// Throws exception if baby broke
response.EnsureSuccessStatusCode();
// Convert to something slightly less useless
result = await response.Content.ReadAsStringAsync();
}
catch (Exception exc)
{
// Something broke ¯\_(ツ)_/¯
_logger.ErrorException("Something broke in GetData(). Probably a borked connection.", exc);
}
return result;
}
_httpClient is created in the constructor and is a System.Net.Http.HttpClient.
Does anyone have an idea how to override the regular protocols for the HttpClient and force it to make the call as a GET call, but with a content containing my query for the server?
To me the less devastating way to achieve that is to set ContentBodyNotAllowed field of Get KnownHttpVerb to false using reflection.
You can try with this:
public async Task<string> GetData(string query)
{
var result = string.Empty;
try
{
var KnownHttpVerbType = typeof(System.Net.AuthenticationManager).Assembly.GetTypes().Where(t => t.Name == "KnownHttpVerb").First();
var getVerb = KnownHttpVerbType.GetField("Get", BindingFlags.NonPublic | BindingFlags.Static);
var ContentBodyNotAllowedField = KnownHttpVerbType.GetField("ContentBodyNotAllowed", BindingFlags.NonPublic | BindingFlags.Instance);
ContentBodyNotAllowedField.SetValue(getVerb.GetValue(null), false);
var msg = new HttpRequestMessage(HttpMethod.Get, _dataApiUrl) { Content = new StringContent(query) };
var response = await _httpClient.SendAsync(msg).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
result = await response.Content.ReadAsStringAsync();
}
catch (Exception exc)
{
_logger.ErrorException("Something broke in GetData(). Probably a borked connection.", exc);
}
return result;
}

Categories

Resources