SendGrid incoming mail webhook - how to save the JSON format email data into my application folder in C# - email

This is regarding Sendgrid incoming mail webhook, I have referred this URL SendGrid incoming mail webhook - how do I secure my endpoint, and got some idea how to go about this, but, as I am new to MVC / WebAPI, could anyone give me the controller method code snippet to catch the JSON format HTTP post and save to my application folder.

This is the solution I found after googling and with slight modifications:
[HttpPost, HttpGet]
[EnableCors(origins: "*", headers: "*", methods: "*")]
public async Task Post()
{
if (Request.Content.IsMimeMultipartContent("form-data"))
try
{
//To get complete post in a string use the below line, not used here
string strCompletePost = await Request.Content.ReadAsStringAsync();
HttpContext context = HttpContext.Current;
string strFrom = context.Request.Form.GetValues("from")[0];
string strEmailText = context.Request.Form.GetValues("email")[0];
string strSubject = context.Request.Form.GetValues("subject")[0];
//Not useful I guess, because it always return sendgrid IP
string strSenderIP = context.Request.Form.GetValues("sender_ip")[0];
}
catch (Exception ex)
{
}
}
I tried, retrieving the values as
String to = context.Request.Params["to"];
but, the value returned is not consistent, i.e. most of the times it is returning null and occasionally returns actual value stored in it.
If anyone have a better solution, please let me know.
Thank you

If for some reason ["to"] doesn't work for you, try to get ["envelope"] value,
context.Request.Form.GetValues("envelope")[0]
which looks like
{"to":["emailto#example.com"],"from":"emailfrom#example.com"}

Related

Retrieving SendGrid Transactional Templates List

I have been trying to retrieve list of SendGrid transactional templates using API. I'm using correct API key and getting an empty array while there are about 5 transactional templates existing in my SendGrid account. Here is the response:
{
"templates": []
}
Any guesses what could be wrong?
Any guesses what could be wrong?
Yep, their documentation could be!
I also stuck with the problem and finally managed to solve it once I opened the devtools and saw how they request their own API from the UI. Long story short - one has to pass additional generations=dynamic query parameter. Here is the C# code I use:
var client = new SendGridClient("key");
var response = await client.RequestAsync(
SendGridClient.Method.GET,
urlPath: "/templates",
queryParams: "{\"generations\": \"dynamic\"}");
Using Api 7.3.0 PHP
require("../../sendgrid-php.php");
$apiKey = getenv('SENDGRID_API_KEY');
$sg = new \SendGrid($apiKey);
#Comma-delimited list specifying which generations of templates to return. Options are legacy, dynamic or legacy,dynamic
$query_params = json_decode('{"generations": "legacy,dynamic"}');
try {
#$response = $sg->client->templates()->get();
$response = $sg->client->templates()->get(null, $query_params);
echo $response->body();
exit;
} catch (Exception $e) {
echo '{"error":"Caught exception: '. $e->getMessage().'"}';
}
I had the same problem using the python wrapper provided by Sendgrid.
My code was similar to this:
response = SendGridAPIClient(<your api key>).client.templates.get({'generations': 'legacy,dynamic'})
This returned an empty array.
To fix you have to name the param or to pass None before the dict:
response = SendGridAPIClient(<your api key>).client.templates.get(None, {'generations': 'legacy,dynamic'})
or
response = SendGridAPIClient(<your api key>).client.templates.get(query_params={'generations': 'legacy,dynamic'})

Parse.com resend verification email

I am using the email verification feature that Parse offers and would like my users to be able to resend the email verification if it fails to send or they cannot see it. Last I saw, Parse does not offer an intrinsic way to do this (stupid) and people have been half-hazzerdly writing code to change the email and then change it back to trigger a re-send. Has there been any updates to this or is changing the email from the original and back still the only way? Thanks
You should only need to update the email to its existing value. This should trigger another email verification to be sent. I haven't been able to test the code, but this should be how you do it for the various platforms.
// Swift
PFUser.currentUser().email = PFUser.currentUser().email
PFUser.currentUser().saveInBackground()
// Java
ParseUser.getCurrentUser().setEmail(ParseUser.getCurrentUser().getEmail());
ParseUser.getCurrentUser().saveInBackground();
// JavaScript
Parse.User.current().set("email", Parse.User.current().get("email"));
Parse.User.current().save();
You have to set the email address to a fake one save and then set it back to the original and then parse will trigger the verification process. Just setting it to what it was will not trigger the process.
iOS
if let email = PFUser.currentUser()?.email {
PFUser.currentUser()?.email = email+".verify"
PFUser.currentUser()?.saveInBackgroundWithBlock({ (success, error) -> Void in
if success {
PFUser.currentUser()?.email = email
PFUser.currentUser()?.saveEventually()
}
})
}
Poking around the source code for Parse server, there doesn't seem to be any public api to manually resend verification emails. However I was able to find 2 undocumented ways to access the functionality.
The first would be to use the internal UserController on the server (for instance from a Cloud function) like this:
import { AppCache } from 'parse-server/lib/cache'
Cloud.define('resendVerificationEmail', async request => {
const userController = AppCache.get(process.env.APP_ID).userController
await userController.resendVerificationEmail(
request.user.get('username')
)
return true
})
The other is to take advantage of an endpoint that is used for the verification webpage:
curl -X "POST" "http://localhost:5000/api/apps/press-play-development/resend_verification_email" \
-H 'Content-Type: application/json; charset=utf-8' \
-d $'{ "username": "7757429624" }'
Both are prone to break if you update Parse and internals get changed, but should be more reliable than changing the users email and then changing it back.
We were setting emails to an empty string, but found that there was a race condition where 2 users would hit it at the same time and 1 would fail because Parse considered it to be a duplicate of the other blank email. In other cases, the user's network connection would fail between the 2 requests and they would be stuck without an email.
Now, with Parse 3.4.1 that I'm testing, you can do (for Javascript):
Parse.User.requestEmailVerification(Parse.User.current().get("email"));
BUT NOTE that it will throw error if user is already verified.
Reference:
http://parseplatform.org/Parse-SDK-JS/api/3.4.1/Parse.User.html#.requestEmailVerification
To resend the verification email, as stated above, you have to modify then reset the user email address. To perform this operation in secure and efficient way, you can use the following cloud code function:
Parse.Cloud.define("resendVerificationEmail", async function(request, response) {
var originalEmail = request.params.email;
const User = Parse.Object.extend("User");
const query = new Parse.Query(User);
query.equalTo("email", originalEmail);
var userObject = await query.first({useMasterKey: true});
if(userObject !=null)
{
userObject.set("email", "tmp_email_prefix_"+originalEmail);
await userObject.save(null, {useMasterKey: true}).catch(error => {response.error(error);});
userObject.set("email", originalEmail);
await userObject.save(null, {useMasterKey: true}).catch(error => {response.error(error);});
response.success("Verification email is well resent to the user email");
}
});
After that, you just need to call the cloud code function from your client code. From Android client, you can use the following code (Kotlin):
fun resendVerificationEmail(email:String){
val progress = ProgressDialog(this)
progress.setMessage("Loading ...")
progress.show()
val params: HashMap<String, String> = HashMap<String,String>()
params.put("email", email)
ParseCloud.callFunctionInBackground("resendVerificationEmail", params,
FunctionCallback<Any> { response, exc ->
progress.dismiss()
if (exc == null) {
// The function executed, but still has to check the response
Toast.makeText(baseContext, "Verification email is well sent", Toast.LENGTH_SHORT)
.show()
} else {
// Something went wrong
Log.d(TAG, "$TAG: ---- exeception: "+exc.message)
Toast.makeText(
baseContext,
"Error encountered when resending verification email:"+exc.message,
Toast.LENGTH_LONG
).show()
}
})
}

PUT Request not happening at all in Fantom

I am having some trouble with PUT requests to the google sheets api.
I have this code
spreadsheet_inputer := WebClient(`$google_sheet_URI_cells/R3C6?access_token=$accesstoken`)
xml_test := XDoc{
XElem("entry")
{
addAttr("xmlns","http://www.w3.org/2005/Atom")
addAttr("xmlns:gs","http://schemas.google.com/spreadsheets/2006")
XElem("id") { XText("https://spreadsheets.google.com/feeds/cells/$spreadsheet_id/1/private/full/R3C6?access_token=$accesstoken"), },
XElem("link") { addAttr("rel","edit");addAttr("type","application/atom+xml");addAttr("href","https://spreadsheets.google.com/feeds/cells/$spreadsheet_id/1/private/full/R3C6?access_token=$accesstoken"); },
XElem("gs:cell") { addAttr("row","3");addAttr("col","6");addAttr("inputValue","testing 123"); },
},
}
spreadsheet_inputer.reqHeaders["If-match"] = "*"
spreadsheet_inputer.reqHeaders["Content-Type"] = "application/atom+xml"
spreadsheet_inputer.reqMethod = "PUT"
spreadsheet_inputer.writeReq
spreadsheet_inputer.reqOut.writeXml(xml_test.writeToStr).close
echo(spreadsheet_inputer.resStr)
Right now it returns
sys::IOErr: No input stream for response 0
at the echo statement.
I have all the necessary data (at least i'm pretty sure) and it works here https://developers.google.com/oauthplayground/
Just to note, it does not accurately update the calendars.
EDIT: I had it return the response code and it was a 0, any pointers on what this means from the google sheets api? Or the fantom webclient?
WebClient.resCode is a non-nullable Int so it is 0 by default hence the problem would be either the request not being sent or the response not being read.
As you are obviously writing the request, the problem should the latter. Try calling WebClient.readRes() before resStr.
This readRes()
Read the response status line and response headers. This method may be called after the request has been written via writeReq and reqOut. Once this method completes the response status and headers are available. If there is a response body, it is available for reading via resIn. Throw IOErr if there is a network or protocol error. Return this.
Try this:
echo(spreadsheet_inputer.readRes.resStr)
I suspect the following line will also cause you problems:
spreadsheet_inputer.reqOut.writeXml(xml_test.writeToStr).close
becasue writeXml() escapes the string to be XML safe, whereas you'll want to just print the string. Try this:
spreadsheet_inputer.reqOut.writeChars(xml_test.writeToStr).close

SpringWS - Logging SoapRequest and SoapResponse in a table

I have a SpringWS inplementation with below enpoint implementation
#PayloadRoot(namespace="http://college.com/schema/get_XML_Request/v2",localPart="get_XML_Request")
#ResponsePayload
public JAXBElement<GetStudentResponseType> handleStudentXML(#RequestPayload JAXBElement<GetStudentXMLRequestType> SoapRequest)throws Exception
{
String xmlResponse = "";
com.college.get_student_xml_response.v2.ObjectFactory objectFactory = new com.company.schema.get_student_xml_response.v2.ObjectFactory();
com.college.schema.get_student_xml_response.v2.GetResponseType resType = objectFactory.createGetResponseType();
return objectFactory.createGetStudentResponse(resType);
}
Here my objective is to log the request which coming to my webservice and response which the web service sent back in a table. Is it possible to get the SoapRequest/Soapresponse (In Soapformat) from the above method as a String.Here am able to get the payload, but i need to log with entire SoapRequest(with soapenvelope,body) Please anyone advice on this.
Have a look at the SoapEnvelopeLoggingInterceptor which logs the whole SOAP
Envelope including headers. So basically you can extend it to add the saving to the database functionality.

How do I handle/fix "Error getting response stream (ReadDone2): ReceiveFailure" when using MonoTouch?

I am using MonoTouch to build an iPhone app. In the app I am making Web Requests to pull back information from the web services running on our server.
This is my method to build the request:
public static HttpWebRequest CreateRequest(string serviceUrl, string methodName, JsonObject methodArgs)
{
string body = "";
body = methodArgs.ToString();
HttpWebRequest request = WebRequest.Create(serviceUrl) as HttpWebRequest;
request.ContentLength = body.Length; // Set type to POST
request.Method = "POST";
request.ContentType = "text/json";
request.Headers.Add("X-JSON-RPC", methodName);
StreamWriter strm = new StreamWriter(request.GetRequestStream(), System.Text.Encoding.ASCII);
strm.Write(body);
strm.Close();
return request;
}
Then I call it like this:
var request = CreateRequest(URL, METHOD_NAME, args);
request.BeginGetResponse (new AsyncCallback(ProcessResponse), request);
And ProcessResponse looks like this:
private void ProcessResponse(IAsyncResult result)
{
try
{
HttpWebRequest request = (HttpWebRequest)result.AsyncState;
using (HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result)) // this is where the exception gets thrown
{
using (StreamReader strm = new System.IO.StreamReader(response.GetResponseStream()))
{
JsonValue value = JsonObject.Load(strm);
// do stuff...
strm.Close();
} // using
response.Close();
} // using
Busy = false;
}
catch(Exception e)
{
Console.Error.WriteLine (e.Message);
}
}
There is another question about this issue for Monodroid and the answer there suggested explicitly closing the output stream. I tried this but it doesn't solve the problem. I am still getting a lot of ReadDone2 errors occurring.
My workaround at the moment involves just re-submitting the Web Request if an error occurs and the second attempt seems to work in most cases. These errors only happen when I am testing on the phone itself and never occur when using the Simulator.
Whenever possible try to use WebClient since it will deal automatically with a lot of details (including streams). It also makes it easier to make your request async which is often helpful for not blocking the UI.
E.g. WebClient.UploadDataAsync looks like a good replacement for the above. You will get the data, when received from the UploadDataCompleted event (sample here).
Also are you sure your request is always and only using System.Text.Encoding.ASCII ? using System.Text.Encoding.UTF8 is often usedm, by default, since it will represent more characters.
UPDATE: If you send or receive large amount to byte[] (or string) then you should look at using OpenWriteAsync method and OpenWriteCompleted event.
This is a bug in Mono, please see https://bugzilla.xamarin.com/show_bug.cgi?id=19673