SendGrid Asp.net controller - asp.net

I have two controllers
[HttpPost]
[ProducesResponseType(typeof(string), (int)HttpStatusCode.OK)]
[ProducesResponseType(typeof(string), (int)HttpStatusCode.BadRequest)]
[ProducesResponseType((int)HttpStatusCode.Unauthorized)]
[ProducesResponseType(typeof(string), (int)HttpStatusCode.InternalServerError)]
public async Task<ActionResult> SendEmail([FromForm]string message) {
await _emailSender.SendEmailAsync("mymail#somewhere.com", "Sending from contact", message.ToString());
return Ok();
}
And
[HttpPost]
[ProducesResponseType(typeof(string), (int)HttpStatusCode.OK)]
[ProducesResponseType(typeof(string), (int)HttpStatusCode.BadRequest)]
[ProducesResponseType((int)HttpStatusCode.Unauthorized)]
[ProducesResponseType(typeof(string), (int)HttpStatusCode.InternalServerError)]
[Route("subscribe")]
public async Task<ActionResult> Subscribe( string recipient, [FromForm] string message)
{
var emailTemplate = "";
await _emailSender.SendEmailAsync(recipient, "Thank you for subscribing", message.ToString());
return Ok();
}
Now the first one works absolutely fine when I send an api cal using axios like this:
const response = await axios.post(process.env.REACT_APP_API_URL + '/SendGrid', `message=${emailContent}`)
Where emailContent is a whole html page template with variables inside it. And it works great!
When I try and use the second controller my api call is almost the same I just add the email of the user but for some reason it does not work.
const response = await axios.post(process.env.REACT_APP_API_URL + '/SendGrid/subscribe', `recipient=${values.subscribeEmail}&message=${SubscribedEmailTemplate()}` )
No idea why. What I need is a SendGrid controller that accepts an email and html template as variables and send them respectively.
Thanks for the help!

So I figured it out. I cannot read more than one variable from body. So what I did was create a class and send the request as a json.
public class EmailData
{
public string Recipient { get; set; }
public string Message { get; set; }
}
[HttpPost]
[ProducesResponseType(typeof(string), (int)HttpStatusCode.OK)]
[ProducesResponseType(typeof(string), (int)HttpStatusCode.BadRequest)]
[ProducesResponseType((int)HttpStatusCode.Unauthorized)]
[ProducesResponseType(typeof(string), (int)HttpStatusCode.InternalServerError)]
[Route("subscribe")]
public async Task<ActionResult> Subscribe(EmailData emailData)
{
if (emailData.Recipient == null )
{
return BadRequest();
}
var emailTemplate = "";
await _emailSender.SendEmailAsync(emailData.Recipient.ToString(), "Thank you for subscribing", emailData.Message.ToString());
return Ok();
}

Related

Asp.Net Core create a controller to send emails using SendGrid

I'm trying to create a controller following this https://www.ryadel.com/en/asp-net-core-send-email-messages-sendgrid-api/ tutorial
I've added everything apart from the controller. What I have as a code in the controller is this
public class SendGridController : BaseApiController
{
private readonly IEmailSender _emailSender;
public SendGridController(IEmailSender emailSender)
{
_emailSender = emailSender;
}
[HttpPost]
[ProducesResponseType(typeof(string), (int)HttpStatusCode.OK)]
[ProducesResponseType(typeof(string), (int)HttpStatusCode.BadRequest)]
[ProducesResponseType((int)HttpStatusCode.Unauthorized)]
[ProducesResponseType(typeof(string), (int)HttpStatusCode.InternalServerError)]
public async Task<ActionResult> SendEmail() {
await _emailSender.SendEmailAsync("test#mail.com", "subject", "something");
return Ok(await _emailSender.SendEmailAsync("test#mail.com", "subject", "something"));
}
}
But I'm getting the following error
Argument 1: cannot convert from 'void' to 'object'
I would like to be able to see the response from send grid if it has sent it or not.
Here is the SendGridEmailSender class:
public class SendGridEmailSender : IEmailSender
{
public SendGridEmailSender(
IOptions<SendGridEmailSenderOptions> options
)
{
this.Options = options.Value;
}
public SendGridEmailSenderOptions Options { get; set; }
public async Task SendEmailAsync(
string email,
string subject,
string message)
{
await Execute(Options.ApiKey, subject, message, email);
}
private async Task<Response> Execute(
string apiKey,
string subject,
string message,
string email)
{
var client = new SendGridClient(apiKey);
var msg = new SendGridMessage()
{
From = new EmailAddress(Options.SenderEmail, Options.SenderName),
Subject = subject,
PlainTextContent = message,
HtmlContent = message
};
msg.AddTo(new EmailAddress(email));
// disable tracking settings
// ref.: https://sendgrid.com/docs/User_Guide/Settings/tracking.html
msg.SetClickTracking(false, false);
msg.SetOpenTracking(false);
msg.SetGoogleAnalytics(false);
msg.SetSubscriptionTracking(false);
return await client.SendEmailAsync(msg);
}
}
You aren't returning the result of the private method you have:
await Execute(Options.ApiKey, subject, message, email);
So, you need to return the result
public async Task SendEmailAsync(
string email,
string subject,
string message)
{
result = await Execute(Options.ApiKey, subject, message, email);
// do some checks with result
}
If you need to examine the result in your controller code, it would be more tricky, as the signature of the IEmailSender doesn't provide the generic task object, and you need to convert it manually (which isn't recommended). You can simply assume that the sending was successful after the method finishes (because in other case you'll get the exception):
public async Task<ActionResult> SendEmail() {
await _emailSender.SendEmailAsync("test#mail.com", "subject", "something");
// email was sent, no exception
return Ok();
}
In case you need the response from the method, you can do something like this answer with _emailSender.SendEmailAsync("test#mail.com", "subject", "something"), without using await construct (still can't recommend this approach):
/// <summary>
/// Casts a <see cref="Task"/> to a <see cref="Task{TResult}"/>.
/// This method will throw an <see cref="InvalidCastException"/> if the specified task
/// returns a value which is not identity-convertible to <typeparamref name="T"/>.
/// </summary>
public static async Task<T> Cast<T>(this Task task)
{
if (task == null)
throw new ArgumentNullException(nameof(task));
if (!task.GetType().IsGenericType || task.GetType().GetGenericTypeDefinition() != typeof(Task<>))
throw new ArgumentException("An argument of type 'System.Threading.Tasks.Task`1' was expected");
await task.ConfigureAwait(false);
object result = task.GetType().GetProperty(nameof(Task<object>.Result)).GetValue(task);
return (T)result;
}

Asp.net core attribute route issue

I have this code:
[Route("Users")]
public class UserRegistrationController : Controller
{
[HttpGet("details/{userId}")]
public async Task<IActionResult> UserDetails(Guid userId)
{
// .....
}
[HttpPost("Save")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> SaveUser(UserVM userVM)
{
// ......
return RedirectToAction("details", "Users", new { userId = userVM.UserId });
}
}
If I save the user redirectToAction generate userId as query string, this create an issue.
I mean instead of
https://localhost:5001/Users/Details/5de304c7-4c69-4819-c879-08d90306b555
redirect to action creates the URL as
https://localhost:5001/Users/Details?userId=5de304c7-4c69-4819-c879-08d90306b555
which causes a 404 error.
How do I solve this issue? I want to pass userId in route as below
https://localhost:5001/Users/Details/5de304c7-4c69-4819-c879-08d90306b555
Thanks in advance.
The issue was, the action method UserDetails need to add route [Route("details")] This will solve the issue.
[Route("Users")]
public class UserRegistrationController : Controller
{
[HttpGet("details/{userId}")]
[Route("details")]
public async Task<IActionResult> UserDetails(Guid userId)
{
// .....
}
[HttpPost("Save")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> SaveUser(UserVM userVM)
{
// ......
return RedirectToAction("details", "Users", new { userId = userVM.UserId });
}
}

ASP Net Core Auth - RedirectToAction back to Target Action

In an ASP.NET Core Rest API project, I have set up custom authentication, and I can annotate Controller Actions with the [Authorize] attribute, which redirects unauthorized requests back to my AuthController:
[Route("api/[controller]")]
[ApiController]
public class ResponseController : ControllerBase
{
[Authorize]
[HttpGet("{id}")]
public ActionResult<string> Get(int id)
{
return JsonConvert.SerializeObject(Repository.GetResponse(id), Formatting.Indented);
}
}
[Route("api/[controller]")]
[ApiController]
public class MetaController : ControllerBase
{
[Authorize]
[HttpGet("{id}")]
public ActionResult<string> Get(int id)
{
return JsonConvert.SerializeObject(Repository.GetMeta(id), Formatting.Indented);
}
}
[Route("api/[controller]")]
[ApiController]
public class AuthController : Controller
{
UserManager _userManager;
public AuthController(UserManager userManager)
{
_userManager = userManager;
}
[HttpGet]
[HttpPost]
public ActionResult<string> LogIn()
{
try
{
//authenticate
var username = Request.Headers["username"];
var password = Request.Headers["pass"];
if (String.IsNullOrEmpty(username) || String.IsNullOrEmpty(password))
throw new Exception("Authentication Exception: Missing Username or Password");
Task.Run(async () => {
await _userManager.SignInAsync(this.HttpContext, username, password);
}).GetAwaiter().GetResult();
return RedirectToAction("Search", "Home", null);
//^^^ How to send back to intended action?^^^
}
catch (Exception ex)
{
return AuthError();
}
}
}
This works, except how do I use the RedirectToAction method to return back to the intended controller (MetaController or ResponseController, in this case)? (the one with the method marked [Authorize], which put us into this authentication controller to begin with)
You need to use returnUrl parameter, like so:
[HttpGet]
[HttpPost]
public async Task<IActionResult> LogIn(string returnUrl = null)
{
try
{
//authenticate
var username = Request.Headers["username"];
var password = Request.Headers["pass"];
if (String.IsNullOrEmpty(username) || String.IsNullOrEmpty(password))
throw new Exception("Authentication Exception: Missing Username or Password");
await _userManager.SignInAsync(this.HttpContext, username, password);
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
return RedirectToAction("Search", "Home", null);
}
catch (Exception ex)
{
return BadRequest(new {error = "Authentication Failed"});
}
}
I also fix async/await for controller's action. You need use async Task<ActionResult> insted of your ActionResult<string>
Instead of redirection, create your own attribute inheriting from the AuthorizeAttribute and override the OnAuthorization method. That way you don't have to worry about redirection.

Web API Post giving me error

I have a web API application. I'm supposed to do a post to an endpoint. When l tried my API controller in postman, l get the error message "
Requested resource does not support HTTP 'POST'
I'm new to Web API so any help and suggestions are welcomed.
This is my model class:
namespace Products.Models
{
public class Prouct
{
public string ProductID { get; set; }
public string ProductName { get; set; }
public string ProductPrice { get; set; }
public string VoucherID { get; set; }
}
}
Here is my controller class
[RoutePrefix("api/products")]
public class ProductsController : ApiController
{
static HttpClient client = new HttpClient();
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
public string Get(int id)
{
return "value";
}
[Route("products")]
public async Task PostAsync(string ProductID, string ProductName, string ProductPrice,
string VoucherID)
{
Products p = new Products();
p.ProductID = ProductID;
p.ProductName = ProductName;
p.ProductPrice = ProductPrice;
p.VoucherID = VoucherID;
var client = new HttpClient { BaseAddress = new
Uri("http://localhost:51613/") };
var response = await
client.PostAsJsonAsync("api/products",
p);
if (response.IsSuccessStatusCode)
{
}
public void Put(int id, [FromBody]string value)
{
}
public void Delete(int id)
{
}
You need to specify HttpPost on PostAsync method. by default, it is [HttpGet].
[HttpPost]
[Route("products")]
public async Task PostAsync(string ProductID, string ProductName, string ProductPrice, string VoucherID)
{
// implementation
}
Looks like you're stuck in a loop. Why does the PostAsync method call itself after having been invoked? This will result in an endless request loop.
var client = new HttpClient { BaseAddress = new Uri("http://localhost:51613/") };
This is not related to the fact that the [HttpPost] attribute is required however.
Please observe that you are supposed to use [FromBody] . Also inside Postman (image attached) you have to choose "Raw" data with the product json with type as JSON(application.json).
[HttpPost]
[Route("products")]
public async Task PostAsync([FromBody] Products p)
{
var client = new HttpClient
{
BaseAddress = new
Uri("http://localhost:51613/")
};
var response = await
client.PostAsJsonAsync("api/products",
p);
if (response.IsSuccessStatusCode)
{
}
}

How to return a class to Angular?

I want to return a class from Controller to angular
$scope.GetUser = function (user_id) {
$.ajax({
type: "POST",
url: "Menu/GetUser",
data: '{"user_id":"' + user_id + '"}',
contentType: "application/json; charset=utf-8",
success: function (resp) {
debugger
},
failure: function (resp) {
alert(resp);
}
});
}
My controller
[HttpPost]
//public JsonResult GetUser(string user_id)
public User GetUser(string user_id)
{
return rep.GetUser(user_id);
//return Json(new
//{
// user = rep.GetUser(user_id)
//}, JsonRequestBehavior.AllowGet);
}
My model
public User GetUser(string user_id)
{
var user = ctx.tblUsuario.
Where(x => x.usuario_id.Trim() == user_id).FirstOrDefault();
if (user == null)
return null;
return new User(user.usuario_id, user.usuario_nome, user.usuario_perfilid, user.usuario_dtcadastro, user.usuario_senha);
}
My class
namespace DB_Pro.Models
{
public class User
{
private string Usuario_id { get; set; }
private string Usuario_nome { get; set; }
private int Usuario_perfilid { get; set; }
private DateTime Usuario_dtcadastro { get; set; }
private string Usuario_senha { get; set; }
public User(string usuario_id, string usuario_nome, int usuario_perfilid, DateTime usuario_dtcadastro, string usuario_senha)
{
Usuario_id = usuario_id;
Usuario_nome = usuario_nome;
Usuario_perfilid = usuario_perfilid;
Usuario_dtcadastro = usuario_dtcadastro;
Usuario_senha = usuario_senha;
}
}
}
But when I return to success Ajax function, it just have response
equals "DB_Pro.Models.User"
What am I doing wrong?
Thx a lot Davidivad to help me.
I also did some changes too and it worked well
My Model
public tblUsuario GetUser(string user_id)
{
return ctx.tblUsuario.
Where(x => x.usuario_id == user_id).FirstOrDefault();
//var user = ctx.tblUsuario.
// Where(x => x.usuario_id == user_id).FirstOrDefault();
//if (user == null)
// return null;
//return new User(user.usuario_id, user.usuario_nome, user.usuario_perfilid, user.usuario_dtcadastro, user.usuario_senha);
}
My controller
[HttpPost]
public JsonResult GetUser(string user_id)
{
return Json(new
{
user = rep.GetUser(user_id)
});
}
Why don't you create a json in your controller and send that to the angular controller?. That should really work and I see that you have the json creation commented in the controller.
The idea is that angular uses javascript objects and if you send that from the controller you should be able to access its contents easily.
In the json instead of placing the user, get its parameters,
name: u.NAME,
surname: u.SURNAME,
...
And from angular you cab directly access them. If you create a json with the user, angular will not know what a user is, that's why you are getting that response. Let the json be the user, an object with the parameters of the user, in this way when angular receives the data you just have to take then info that you need from data:
data.name
data.surname
EDIT:
After researching, I think this could work:
[HttpPost]
//public JsonResult GetUser(string user_id)
public User GetUser(string user_id)
{
User u = rep.GetUser(user_id);
var result = Json(new { Usuario_nome = u.Usuario_nome,
Usuario_perfilid = u.Usuario_perfilid});
return result;
}
Try it and tell me if it solves the problem
in order to send any object through, you should serialize it to a stream (preferably JSON string).
because you dont serialize, your return new User(...); calls the default .toString() of your User class, which returns the type-name (in your case: DB_Pro.Models.User)

Resources