HttpContext.Current is null after Task.Run - asp.net

Here are my project details
A web-API project containing webapi controllers
I have a separate c# Library project that contains models for my webapi and some method and api for intracting with database.
settings in webconfig
<httpRuntime targetFramework="4.5"/>
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true"/>
Issue: HttpContext.Current.user is null.I want to access Httpcontext.Current.user in my c# library project.
1.In the code snippet below HttpContext.Current.user is available.This method exist in webapi project.
[HttpPost]
public async Task<IHttpActionResult> Post([FromBody]TEntity entity)
{
System.Security.Principal.IPrincipal p = System.Threading.Thread.CurrentPrincipal;
// context is available till here
var context =HttpContext.Current.User;
return await Task<IHttpActionResult>.Run(() =>
{
if (!ModelState.IsValid)
{
return (IHttpActionResult)Content(HttpStatusCode.BadRequest, ModelState.ToUnexpectedResultWrapper());
}
try
{
TOrchestrator orchestrator = new TOrchestrator();
orchestrator.Insert(entity);
}
catch (ValidationException ex)
{
return Content(HttpStatusCode.BadRequest, ex.ToUnexpectedResultWrapper());
}
catch (DbEntityValidationException ex)
{
return Content(HttpStatusCode.BadRequest, ex.ToUnexpectedResultWrapper());
}
return CreatedAtRoute("REST", new { id = entity.Id }, entity);
});
}
2.In code snippet below that exist in C# library. HttContext.Current.user is null. this method is called from the above method "Post".
public void Insert(Market market)
{
// Context is
System.Security.Principal.IPrincipal p = System.Threading.Thread.CurrentPrincipal;
//IOATIApplicationUser user = UserContextHelper.GetOATIContext().OATIUser;
var http = HttpContext.Current.User;
RunAction<InsertAction, Market>(market);
}
More over I could access user object from System.Threading.Thread.CurrentPrincipal.
If I cant access user object from HttpContext. Can i user System> System.Threading.Thread.CurrentPrincipal. I have heard it is not safe to user "System.Threading.Thread.CurrentPrincipal" and it is ment for window forms only.

HttpContext.Current returns the current HttpContext being serviced by the calling thread. When you start a background task by using Task.Run, it is not associated with an HTTP context by design.
Since you should not be using Task.Run on ASP.NET anyway, removing it is the easiest solution:
[HttpPost]
public IHttpActionResult Post([FromBody]TEntity entity)
{
if (!ModelState.IsValid)
{
return (IHttpActionResult)Content(HttpStatusCode.BadRequest, ModelState.ToUnexpectedResultWrapper());
}
try
{
TOrchestrator orchestrator = new TOrchestrator();
orchestrator.Insert(entity);
}
catch (ValidationException ex)
{
return Content(HttpStatusCode.BadRequest, ex.ToUnexpectedResultWrapper());
}
catch (DbEntityValidationException ex)
{
return Content(HttpStatusCode.BadRequest, ex.ToUnexpectedResultWrapper());
}
return CreatedAtRoute("REST", new { id = entity.Id }, entity);
}

Related

can I take any object in webapi? (.Net Core 3.1)

like this:
[HttpGet]
[AllowAnonymous]
[Route("[action]")]
public ActionResult Get([FromQuery] object RequestModel)
{
try
{
IResult runResult = new Result();
ICommand api = Factory.APIName((Enums.APIName)Enum.ToObject(typeof(Enums.APIName), 9901));
var responseModel = api.HandleCommand<object, object>(RequestModel, out runResult);
api.Dispose();
if (runResult.Success)
return StatusCode(200, responseModel);
else
return StatusCode(200, new ERC_Models.BaseResponse() { StateCode = 999 });
}
catch
{
return StatusCode(500);
}
}
how can do it?
maybe use with Generic? but I tried, it response 404
the reason is I wish all entrance can through one webapi, and go factory layer
so I need can take any parameters webapi action...
thanks!

Which is the best way to validate current password?

I created a form where the user can update his data account. In this form the user is also able to change the account password, before doing so, I ask him the current password, this is the field:
<div class="form-group">
<label>Current Password</label>
<input class="form-control" id="oldPassword"
asp-for="#Model.ExistingPassword" type="password" />
<div class="invalid-feedback"></div>
</div>
as you can see the oldPassword input bound the property ExistingPassword which is part of the ViewModel of that View and have the following declaration:
[Required, MinLength(6), MaxLength(50), DataType(DataType.Password)]
public string ExistingPassword { get; set; }
when the form is submitted I call the following ajax function:
$.post(url, user, function (response) {
//Some stuff
}).done(function (response) {
alert("Updated executed");
}).fail(function (jqXHR, textStatus, errorThrown) {
alert("Error happened!");
});
the parameter of the function are taken by the form, in particular:
url: $(this).attr('action');
user: $(this).serialize();
the action of the form will call the following controller: User\UpdateUser.
Inside the UpdateUser method I execute the following check:
public async Task<UserProfileViewModel> UpdateUserAsync(UserProfileViewModel updatedUser)
{
if (!await _userManager.CheckPasswordAsync(originalUser, updatedUser.ExistingPassword))
throw new Exception("Invalid password");
essentially, the condition check if the current password is correct, if not, then an exception will raised.
Now, my question with this is: how can I know which type of exception the method has generated?
I need to know which type of exception the method UpdateUser has generated because there are different exceptions in the method.
Suppose the Invalid Password exceptions is raised, I need to display a message inside invalid-feedback div, next to oldPassword, so the user know why the update has failed.
Thanks in advance for any help.
Normally, I recommend not using an exception except in actual exception circumstances, but given the way you've designed this, you have a few options.
I'd suggest creating a custom "UpdateUserException" that you can throw that will include additional information, which can be provided by an enum or just string.
public class UpdateUserException : Exception {
public UpdateUserError ErrorCondition;
public UpdateUserException(UpdateUserError error, string message)
{
ErrorCondition = error;
Message = message;
}
}
then you would throw it
throw new UpdateUserException(UpdateUserError.BadPassword, "Invalid Password");
then you would catch it
try {}
catch (UpdateUserException e)
{
if (e.ErrorCondition == UpdateUserException.BadPassword)
{
// handle your exception.
}
}
Have a look at the UserManager ChangePassword Method.
You can bind the UserManager to use DependencyInjection like this (in Startup.cs)
public async void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
app.UseAuthentication();
app.UseMvc();
var scopeFactory = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>();
using (var scope = scopeFactory.CreateScope())
{
UserManager<User> userManager = scope.ServiceProvider.GetRequiredService<UserManager<User>>();
}
}
And then in your Controller s Constructor
private readonly UserManager<User> _userManager;
public AccountController(UserManager<User> userManager)
{
_userManager = userManager;
}
And finally your endpoint:
[HttpPost("ChangePassword")]
public async Task<IActionResult> ChangePassword([FromBody]ChangePasswordRequest changePasswordParams)
{
if (changePasswordParams == null)
return BadRequest($"{nameof(changePasswordParams)} must not be null!");
if (string.IsNullOrWhiteSpace(changePasswordParams.OldPassword) || string.IsNullOrWhiteSpace(changePasswordParams.NewPassword))
return BadRequest("old and new passwords have to be provided, but they both are empty.");
var userId = User.Claims.FirstOrDefault(c => c.Type == "id")?.Value;
var user = await _userManager.FindByIdAsync(userId);
var result = await _userManager.ChangePasswordAsync(user, changePasswordParams.OldPassword, changePasswordParams.NewPassword);
if (result.Succeeded)
return NoContent();
return BadRequest(result.Errors);
}
after that you can handle the errors in a switch statement.
Using Exceptions for handled errors are not recommended since they generally ends up with Internal Server error and actually It is beyond of its purpose.
The best approach would be to send BadRequest as It is stated by #maerlin.
However, If you insist to use Exceptions in your application or your applciation is architected to work in this way. I suggest you to inherit new CustomApplcationException class from ApplicationException and then inherit UpdateUserException and vs. from CustomApplicationException class. After that, I Suggest you to handle your exceptions in ErrorHandlingMiddleware and return HandledExceptions at least with BadRequest (400) status code.
The Example Code would be
public class ExceptionMiddleware
{
private readonly RequestDelegate _next;
private readonly ILoggerManager _logger;
public ExceptionMiddleware(RequestDelegate next, ILoggerManager logger)
{
_logger = logger;
_next = next;
}
public async Task InvokeAsync(HttpContext httpContext)
{
try
{
await _next(httpContext);
}
catch (CustomApplicationException cae)
{
await HandleCustomExceptionAsync(httpContext, cae);
}
catch (Exception ex)
{
_logger.LogError($"Something went wrong: {ex}");
await HandleExceptionAsync(httpContext, ex);
}
}
private static Task HandleExceptionAsync(HttpContext context, Exception exception)
{
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
return context.Response.WriteAsync(new ErrorDetails()
{
StatusCode = context.Response.StatusCode,
Message = "Internal Server Error from the custom middleware."
}.ToString());
}
private static Task HandleCustomExceptionAsync(HttpContext context, Exception exception)
{
context.Response.StatusCode = 400;
return context.Response.WriteAsync(new ErrorDetails()
{
StatusCode = context.Response.StatusCode,
Message = exception.Message
}.ToString());
}
}
then you need to regiter middleware in your Startup.cs
app.UseMiddleware<ExceptionMiddleware>();
please see https://code-maze.com/global-error-handling-aspnetcore/ and http://www.talkingdotnet.com/global-exception-handling-in-aspnet-core-webapi/ for further details.

Asp.net core 2 XUnit -- Unit Test MVC controller that throws exception

I am trying to unit test a controller that returns an IActionResult but can also throw an exception in certain circumstances. The problem I am running into is I'm not sure how to call it as the Assert throws an error where you cannot convert from IActionResult to Action.
How would I go about testing below statement?
Assert.Throws<Exception>(await controller.SendEmail(email)); //how to test this
I looked through the Microsoft testing controller documentation and didn't find something relevant. Most examples I see testing exceptions are for things like accessing repositories or services.
I understand I can return a badrequest or redirect to the page with an error message. But is what I am trying to accomplish possible?
My HomeController Method
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> SendEmail(EmailModel emailModel)
{
if(!ModelState.IsValid)
{
return View("Index",emailModel);
}
var response = await _sendEmail.SendEmailMessage(emailModel);
if (response != System.Net.HttpStatusCode.Accepted)
{
throw new Exception("Email failed to send, please try again later");
}
else
{
TempData["message"] = $"Email has been sent";
}
return RedirectToAction("Index");
}
XUnit HomeControllerTest Constructor for arrange
private Mock<ISendEmail> mockSendEmail;
private HomeController controller;
public HomeControllerShould()
{
mockSendEmail = new Mock<ISendEmail>();
mockSendEmail.Setup(x => x.SendEmailMessage(It.IsAny<EmailModel>())).ReturnsAsync(HttpStatusCode.Accepted);
controller = new HomeController(mockSendEmail.Object);
}
XUnit Test for Sending Email
[Fact]
public async Task SendEmailActionThrowsExceptionOnEmailFailure()
{
mockSendEmail.Setup(x => x.SendEmailMessage(It.IsAny<EmailModel>())).ReturnsAsync(HttpStatusCode.InternalServerError);
var email = new EmailModel();
Assert.Throws<Exception>(await controller.SendEmail(email)); //how to test this
}
Assert.Throws requires a function. You could use ThrowsAsync.
[Fact]
public async Task SendEmailActionThrowsExceptionOnEmailFailure()
{
mockSendEmail.Setup(x => x.SendEmailMessage(It.IsAny<EmailModel>()))
.ReturnsAsync(HttpStatusCode.InternalServerError);
var email = new EmailModel();
await Assert.ThrowsAsync<Exception>(() => controller.SendEmail(email));
}
FYI: We don't normally return HttpStatusCode from service layer such as email service, but I'll let you decide.

How can I validate a custom token (which is not JWT) in ASP .NET Core 2.0 Web API?

In our ASP .NET Core 2.0, Web API, when the user logs in, we generate a GUID and return that to the user after storing it in database. What is the best practice to validate this token when the user submits a request to a controller having Authorize attribute on it.
Should I override AuthorizeAttribute.OnAuthorization and put my custom logic in there ? or is there any other place where I should place my custom logic ?
Thanks in advance.
In ASP .NET Core 2.0 you can write you own Middleware to validate token. You can see this video as exapmle - https://www.youtube.com/watch?v=n0llyujNGw8.
Summarily:
1. Create TokenMiddleware:
public class TokenMiddleware
{
// always should be RequestDelegate in constructor
private readonly RequestDelegate _next;
public TokenMiddleware(RequestDelegate next)
{
_next = next;
}
// always should be defiened Invoke or InvokeAsync with HttpContext and returned Task (You can also inject you services here - for example DataContext)
public async Task InvokeAsync(HttpContext context, DataContext dataContext)
{
var validKey = true;
// than you logic to validate token
if (!validKey)
{
context.Response.StatusCode = (int) HttpStatusCode.Forbidden;
await context.Response.WriteAsync("Invalid Token");
}
// if validm than next middleware Invoke
else
{
await _next.Invoke(context);
}
}
}
// Extension to IApplicationBuilder (to register you Middleware)
public static class TokenExtensions
{
public static IApplicationBuilder UseTokenAuth(this IApplicationBuilder builder)
{
return builder.UseMiddleware<TokenMiddleware>();
}
}
Registred you Middleware in Startup:
app.UseTokenAuth();
Question was made long time ago, but for people that might stumble upon it, here is the way I did it, taking advantage of authentication and authorization middlewares. The question doesn't have details about the way the token is passed in the request but I am assuming a standard Authorization header.
Create a custom AuthenticationHandler
MyCustomTokenHandler.cs
public class MyCustomTokenHandler: AuthenticationHandler<AuthenticationSchemeOptions>
{
public MyCustomTokenHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
{
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!Request.Headers.ContainsKey("Authorization"))
{
return AuthenticateResult.NoResult();
}
if (!AuthenticationHeaderValue.TryParse(Request.Headers["Authorization"], out AuthenticationHeaderValue? headerValue))
{
return AuthenticateResult.NoResult();
}
if (!Scheme.Name.Equals(headerValue.Scheme, StringComparison.OrdinalIgnoreCase))
{
return AuthenticateResult.NoResult();
}
if (headerValue.Parameter == null)
{
return AuthenticateResult.NoResult();
}
//The token value is in headerValue.Parameter, call your db to verify it and get the user's data
var claims = new[] { new Claim(ClaimTypes.Name, "username found in db") };
//set more claims if you want
var identity = new ClaimsIdentity(claims, Scheme.Name);
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, Scheme.Name);
return AuthenticateResult.Success(ticket);
}
}
Register the handler and enable authorization
Program.cs
builder.Services.AddAuthentication("Bearer").AddScheme<AuthenticationSchemeOptions, MyCustomTokenHandler>("Bearer", null);
//...
var app = builder. Build();
app.UseAuthentication();
app.UseAuthorization();
Most of the code is inspired by this blog post: https://joonasw.net/view/creating-auth-scheme-in-aspnet-core-2

Why doesn't Controller Factory use a Controller returned by that factory?

I implemented a custom controller factory in ASP.NET MVC, and I registered it in global.ascx. The idea is to handle the case of 404 and also exceptions in the controller constructors. I know the factory has been assigned to ASP.NET MVC, because on requests, I can step into it. I can see that I'm returning the controller that I think. But why, oh why on earth, is not my controller used? But I'd think I'd get the usual action not found exception, not controller..conceptually I'm wondering if this is even the right spot to do this in.
protected override IController GetControllerInstance
(RequestContext context,
Type controllerType)
{
IController controller = null;
try
{
controller = base.GetControllerInstance(context, controllerType);
}
catch (CurrentSessionException)
{
controller = new LoginController();
}
catch (System.Web.HttpException)
{
controller = new ErrorController();
}
catch (System.Exception)
{
controller = new ErrorController();
}
return controller;
}
Try manually clearing the errors in your catch statement.
requestContext.HttpContext.ClearError();
Ideally this is best handled as a Filter. MVC comes with a HandleErrorAttribute which you can subclass. You would override the OnException method and then simple handle the logic as you wish.
This is what MVC 3 does by default.
public virtual void OnException(ExceptionContext filterContext) {
if (filterContext == null) {
throw new ArgumentNullException("filterContext");
}
if (filterContext.IsChildAction) {
return;
}
// If custom errors are disabled, we need to let the normal ASP.NET exception handler
// execute so that the user can see useful debugging information.
if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled) {
return;
}
Exception exception = filterContext.Exception;
// If this is not an HTTP 500 (for example, if somebody throws an HTTP 404 from an action method),
// ignore it.
if (new HttpException(null, exception).GetHttpCode() != 500) {
return;
}
if (!ExceptionType.IsInstanceOfType(exception)) {
return;
}
string controllerName = (string)filterContext.RouteData.Values["controller"];
string actionName = (string)filterContext.RouteData.Values["action"];
HandleErrorInfo model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName);
filterContext.Result = new ViewResult {
ViewName = View,
MasterName = Master,
ViewData = new ViewDataDictionary<HandleErrorInfo>(model),
TempData = filterContext.Controller.TempData
};
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.StatusCode = 500;
// Certain versions of IIS will sometimes use their own error page when
// they detect a server error. Setting this property indicates that we
// want it to try to render ASP.NET MVC's error page instead.
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
}

Resources