ASP.NET VNext Request.Properties - c#

In previous MVC version i use authentication service like this
public class OvAuthorizeAttribute : FilterAttribute
{
public async Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext actionContext, CancellationToken cancellationToken, Func<Task<HttpResponseMessage>> continuation)
{
..........
var user = await ContainerFactory.Container.GetInstance<IMembershipService>().GetUser(token);
if (user == null)
........
actionContext.Request.Properties["User"] = user;
}
}
[OvAuthorize]
public class CommonController : Controller
{
public User CurrentUser
{
get
{
return Request.Properties["User"] as User; //ERROR
}
}
}
But now, i can't access Request.Properties in new Controller definition

You can get User directly from the Controller instance. The following property is exposed on Controller.
/// <summary>
/// Gets or sets the <see cref="ClaimsPrincipal"/> for user associated with the executing action.
/// </summary>
public ClaimsPrincipal User
{
get
{
return Context?.User;
}
}

Related

Net Core custom controller

Currently my code looks something like this:
public class UserController : ControllerBase
{
[HttpPost]
public async Task<ActionResult> Login(LoginCredentialsModel model)
{
if (someValidLogic){
return Ok(new { message = "User login success.",
additionalParameters = new {
param1 = "",
param2 = ""
}
});
}
else {
return BadRequest(new { errorMessage = "Username or password is incorrect.",
additionalParameters = {
StatusCode = 400,
RetryLeftCount = 3 }
});
}
}
}
I am manually creating JSON objects to return to UI in every endpoint so that I can have consistent communication and allow UI to handle messages at a global level. (using angular interceptors in my case). I am wanting to create a custom class that can be implemented by all controllers that have only two options of return types - Success(), Fail().
public class UserController : CustomControllerBase
{
[HttpPost]
public async Task<ActionResult> Login(LoginCredentialsModel model)
{
if (someValidLogic){
return Success("User login success.", additionalParameters)
}
else {
return Error("Username or password is incorrect.", additionalParameters);
}
}
}
And my CustomControllerBase would be in charge of formatting the results in the proper form. I know I can do this via middleware but I really do not want to as it still allows developers to accidentally send back some non-valid results and middleware not knowing how to handle it.
Even if you make a custom base controller you're going to have to make the custom base controller extend Microsoft.AspNetCore.Mvc.ControllerBase, else you won't get all the automatic routing, the [HttpPost] type attributes etc. And once you extend ControllerBase, all the methods like Ok() will be available to your other developers. I'd really just try to communicate with the developers to not use Ok() etc.
This is hacky, but you could "block" the other methods by overriding them in your CustomControllerBase and just throwing an exception. This won't create a compile time error but it will at least create a shallow runtime exception. Also there's a LOT of these methods and you'd have to override them all.
public class CustomControllerBase : ControllerBase
{
public ActionResult Success(string message)
{
return base.Ok(message);
}
public new ActionResult Ok(object value)
{
throw new Exception("don't use this");
}
}
public class UserController : CustomControllerBase
{
public async Task<ActionResult> Hello()
{
return Ok("hello"); // throws runtime exception
}
}
Alternatively use a style checker and outlaw all uses of Ok() and similar methods. You'd also have to disallow lines like return new OkObjectResult("hello"); It's really going to be quite an ordeal to get this right.
As you've requested, to have a custom class that you can re-use in returning response messages back to the calling request, I created a custom class that inherits from ActionResult that we can return.
After having this class in place, we are going to use it to create a base/custom controller where we will add our Success and Fail methods which any controller can inherit from to use the extended methods.
CustomActionResult
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text
/// <summary>
/// Customized <see cref="ActionResult"/> that allows easily setting <see cref="HttpStatusCode"/>
/// and data result object to be returned for a request.
/// </summary>
public class CustomActionResult : ActionResult
{
private static UTF8Encoding utf = new UTF8Encoding();
/// <summary>
/// Http response code.
/// </summary>
public HttpStatusCode StatusCode { get; set; }
/// <summary>
/// Data to return back to the user as an <see cref="object"/> of any <see cref="Type"/>
/// </summary>
public object Data { get; set; }
/// <summary>
/// Parameterless contructor that initializes the ActionResult with
/// <see cref="HttpStatusCode.OK"/> as the default Response Code.
/// </summary>
public CustomActionResult()
{
StatusCode = HttpStatusCode.OK;
Headers = new Dictionary<string, string>();
}
/// <summary>
/// Constructor that initializes the ActionResult with a specified <see cref="HttpStatusCode"/>
/// </summary>
/// <param name="statusCode">
/// Http response code to set for this ActionResult.
/// </param>
public CustomActionResult(HttpStatusCode statusCode)
:this()
{
StatusCode = statusCode;
}
/// <summary>
/// Constructor that initializes the ActionResult with a specified <see cref="HttpStatusCode"/>
/// </summary>
/// <param name="statusCode">
/// Http response code to set for this ActionResult.
/// </param>
/// <param name="message">Reason phrase</param>
public CustomActionResult(HttpStatusCode statusCode, string message)
:this()
{
StatusCode = statusCode;
Data = message;
}
private string Json
{
get
{
if(Data != null)
{
if (Data.GetType() == typeof(string))
{
return Data.ToString();
}
return JsonConvert.SerializeObject(Data);
}
return string.Empty;
}
}
public byte[] GetBuffer() => utf.GetBytes(Json);
public Dictionary<string,string> Headers { get; private set; }
public override void ExecuteResult(ActionContext context)
{
if (Headers.Count > 0)
{
for (int i = 0; i < Headers.Count; i++)
{
var item = Headers.ElementAt(i);
context.HttpContext.Response.Headers.Add(item.Key, item.Value);
}
}
if (!string.IsNullOrWhiteSpace(Json))
{
context.HttpContext.Response.ContentType = "application/json";
}
context.HttpContext.Response.StatusCode = (int)StatusCode;
context.HttpContext.Response.Body.Write(GetBuffer(), 0, GetBuffer().Length);
}
}
CustomBaseController
public class CustomBaseController : ControllerBase
{
public ActionResult Success(object value)
{
return new CustomActionResult(){ Data = value };
}
public ActionResult Success(string message, params object[] additionalParams)
{
if(additionalParams.Length > 0){
return new CustomActionResult(){
Data = new { message, additionalParams }
};
}else{
return new CustomActionResult() { Data = message };
}
}
public ActionResult Fail(object value)
{
return new CustomActionResult(HttpStatusCode.BadRequest){ Data = value };
}
public ActionResult Fail(string message, params object[] additionalParams)
{
if(additionalParams.Length > 0){
return new CustomActionResult(HttpStatusCode.BadRequest){
Data = new { ErrorMessage = message, additionalParams }
};
}else{
return new CustomActionResult(HttpStatusCode.BadRequest){
Data = new { ErrorMessage = message }
};
}
}
}
Usage
public class UserController : CustomBaseController
{
[HttpPost]
public async Task<ActionResult> Login(LoginCredentialsModel model)
{
if(!ModelState.IsValid)
return this.Fail(ModelState);
// add your other custom logic here
if(someLogic){
return this.Success("your-message", additionalParams);
} else {
return this.Fail("custom-error-message", additionalParams);
}
}
}

How to do model validation in every method in ASP.NET Core Web API?

I am getting into ASP.NET Core 2.0 with Web API. One of my first methods are my login:
/// <summary>
/// API endpoint to login a user
/// </summary>
/// <param name="data">The login data</param>
/// <returns>Unauthorizied if the login fails, The jwt token as string if the login succeded</returns>
[AllowAnonymous]
[Route("login")]
[HttpPost]
public IActionResult Login([FromBody]LoginData data)
{
var token = _manager.ValidateCredentialsAndGenerateToken(data);
if (token == null)
{
return Unauthorized();
}
else
{
return Ok(token);
}
}
My LoginData using DataAnnotations:
public class LoginData
{
[Required]
[MaxLength(50)]
public string Username { get; set; }
[Required]
public string Password { get; set; }
[Required]
[MaxLength(16)]
public string IpAddress { get; set; }
}
So my ModelState is well filled automatically when the login happens and e.g. the password is empty (of course on client side there should be a validation too for it later).
What is the best way to
check the model state,
getting a readable string out of all errors and
return a BadRequest with this error?
Of course I could write it all myself in a helper method. But I thought about a filter maybe?
I would Highly recommend using [ApiController] and other attributes that help ease validation in web API based projects.
[ApiController] this attribute does all basic validation on the modal for you before it enters the method. So you only have to inspect the modal if your want to do some form of custom validation.
How to check the model state?
Check the controller's ModelState in the action to get the state of the model.
getting a readable string out of all errors and return a BadRequest with this error?
Use BadRequest(ModelState) to return HTTP bad request response which will inspect the model state and construct message using errors.
Completed code
/// <summary>
/// API endpoint to login a user
/// </summary>
/// <param name="data">The login data</param>
/// <returns>Unauthorizied if the login fails, The jwt token as string if the login succeded</returns>
[AllowAnonymous]
[Route("login")]
[HttpPost]
public IActionResult Login([FromBody]LoginData data) {
if(ModelState.IsValid) {
var token = _manager.ValidateCredentialsAndGenerateToken(data);
if (token == null) {
return Unauthorized();
} else {
return Ok(token);
}
}
return BadRequest(ModelState);
}
Of course I could write it all myself in a helper method... But I thought about a filter maybe?
To avoid the repeated ModelState.IsValid code in every action where model validation is required you can create a filter to check the model state and short-circuit the request.
For example
public class ValidateModelAttribute : ActionFilterAttribute {
public override void OnActionExecuting(ActionExecutingContext context) {
if (!context.ModelState.IsValid) {
context.Result = new BadRequestObjectResult(context.ModelState);
}
}
}
Can be applied to the action directly
[ValidateModel] //<-- validation
[AllowAnonymous]
[Route("login")]
[HttpPost]
public IActionResult Login([FromBody]LoginData data) {
var token = _manager.ValidateCredentialsAndGenerateToken(data);
if (token == null) {
return Unauthorized();
} else {
return Ok(token);
}
}
or added globally to be applied to all request where model state should be checked.
Reference Model validation in ASP.NET Core MVC
To check if the model state is valid use the ModelState property (exposed by the ControllerBase class which the Controller class inherits from)
ModelState.IsValid
To get the errors from the ModelState you could filter out the errors from the dictionary and return them as a list
var errors = ModelState
.Where(a => a.Value.Errors.Count > 0)
.SelectMany(x => x.Value.Errors)
.ToList();
One option is then to validate the state in every method/controller but i recommend you to implement the validation in a base class which validates the model in the
OnActionExecuting method like this
public class ApiController : Controller
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (!ModelState.IsValid)
{
var errors = ModelState
.Where(a => a.Value.Errors.Count > 0)
.SelectMany(x => x.Value.Errors)
.ToList();
context.Result = new BadRequestObjectResult(errors);
}
base.OnActionExecuting(context);
}
}
Then every controller which should have automatic model state validation just inherit from the base class
public class TokenController : ApiController
{
/// <summary>
/// API endpoint to login a user
/// </summary>
/// <param name="data">The login data</param>
/// <returns>Unauthorizied if the login fails, The jwt token as string if the login succeded</returns>
[AllowAnonymous]
[Route("login")]
[HttpPost]
public IActionResult Login([FromBody]LoginData data)
{
var token = _manager.ValidateCredentialsAndGenerateToken(data);
if (token == null)
{
return Unauthorized();
}
else
{
return Ok(token);
}
}
}

ASP.NET Core Authorization Policies: Can't step into the handler?

I have JWT-based claims authentication/ authorization set up in my .NET Core application, which authenticates as expected, but my policy enforcement is not acting as I would expect.
I have a requirements implementation and handler set up as follows:
public class ImpersonationRequirement : IAuthorizationRequirement
{
}
public class ImpersonationHandler : AuthorizationHandler<ImpersonationRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
ImpersonationRequirement requirement)
{
if (context.User.CanImpersonate()) context.Succeed(requirement);
return Task.CompletedTask;
}
}
I have a helper set up like so:
public static bool CanImpersonate(
this ClaimsPrincipal principal)
{
var val = principal?.FindFirst(MyClaimTypes.CAN_IMPERSONATE)?.Value;
return bool.TryParse(val, out var value) && value;
}
public class MyClaimTypes
{
/// <summary>
/// Boolean value indicating this user is authorized to impersonate other customer accounts.
/// </summary>
public const string CAN_IMPERSONATE = "cim";
...
/// <summary>
/// Actual name of the user impersonating the current user.
/// </summary>
public const string IMPERSONATING_USER = "imp";
}
...off of my Startup.cs, I have the policy defined:
services.AddAuthorization(options =>
{
options.AddPolicy("Impersonator", policy => policy.Requirements.Add(new ImpersonationRequirement()));
});
...and on my controller, it's written as such:
[Produces("application/json")]
[Authorize(Policy = "Impersonator")]
public class ImpersonationController : Controller
{
private readonly ILogger _logger;
private readonly ITokenManagementService _tokenManagementService;
private readonly UserManager<MyUser> _userManager;
public ImpersonationController(ITokenManagementService tokenManagementService, ILoggerFactory loggerFactory, UserManager<MyUser> userManager)
{
_tokenManagementService = tokenManagementService;
_userManager = userManager;
_logger = loggerFactory.CreateLogger<ImpersonationController>();
}
[HttpPost]
[Route("~/api/impersonation/token")]
[ProducesResponseType(typeof(AuthenticationResponse), 200)]
[ProducesResponseType(typeof(Exception), 500)]
public async Task<IActionResult> Impersonate([FromBody] string userNameToImpersonate)
{
try
{
var impersonated = await _userManager.FindByNameAsync(userNameToImpersonate);
if (impersonated == null) throw new EntityNotFoundException($"Unable to find user '{userNameToImpersonate}' in the data store.");
var actualUserId = User.UserId();
var token = await _tokenManagementService.GenerateJwt(impersonated.Id, actualUserId);
var refresh = await _tokenManagementService.GenerateRefreshToken(impersonated.Id, actualUserId);
var response = new AuthenticationResponse {AuthenticationToken = token, RefreshToken = refresh};
return Ok(response);
}
catch (Exception ex)
{
return new OopsResult(ex);
}
}
}
If I run this with the AuthorizeAttribute commented out, I can take a look at the user's claims, and the "cim: true" is in the claims enumeration, but if I run it with the AuthorizeAttribute enabled, I get a 403 Forbidden error.
I tried putting a breakpoint on the line in the ImpersonationHandler:
if (context.User.CanImpersonate()) context.Succeed(requirement);
...but the debugger never stops here, so I don't know what the problem is. Can someone educate me as to what I'm doing wrong?
It seems you forgot to register your ImpersonationHandler in DI container (which is indeed easy to forget):
services.AddSingleton<IAuthorizationHandler, ImpersonationHandler>();
Asp.net resolves all such handlers from container and tries to match for specific requirement. Since no such handler is registered - nothing sets context.Succeed and whole authorization fails.

c# WebApi provider IHttpActionResult in Class Library

So I have created a provider which will handle all my code.
Originally it looked like this:
public class AnswerProvider : ApiController
{
private readonly IUnitOfWork _unitOfWork;
private readonly AnswerService _answerService;
private QuestionService _questionService;
public QuestionService QuestionService => _questionService ?? (_questionService = new QuestionService(this._unitOfWork));
public AnswerProvider(IUnitOfWork unitOfWork)
{
this._unitOfWork = unitOfWork;
this._answerService = new AnswerService(unitOfWork);
}
public async Task<IHttpActionResult> CreateAsync(AnswerRequestModel model)
{
try
{
// Validate our answer count
await ValidateAnswerCountAsync(model.QuestionId);
// Create our model
var answer = ModelFactory.Create(model);
// Add our images to our answer
answer.Images = model.Images;
// Save our model
this._answerService.Create(answer);
// Save the database changes
await this._unitOfWork.SaveChangesAsync();
// Return our updated model
return Ok(ModelFactory.Create(answer));
// If there is an error
}
catch (Exception ex)
{
// Return our error
return BadRequest(ex.Message.ToString());
}
}
/// <summary>
/// Validates the answers based on the question type
/// </summary>
/// <param name="id">The id of the question</param>
/// <returns></returns>
private async Task ValidateAnswerCountAsync(int id)
{
// Get our question
var question = await this.QuestionService.GetAsync(id, "Answers");
// If we have 3 answers or more
if (question.Answers.Count >= 3 && question.Type == QuestionType.Boolean)
{
// Throw an error
throw new InvalidOperationException("A Boolean question can only have 3 answers");
}
}
}
I inherited ApiController because I want to gain access to the Ok, BadRequest and other such methods, that is the only reason.
When I try to run that code, even though it compiles I get this error:
HttpControllerContext.Configuration must not be null
I assume that is because I am trying to inherit the ApiController and I shouldn't be doing that.
Is there another way I can get access the the Ok and other similar methods without inheriting the ApiController.
Please bare in mind that I will have more than one provider.
Do not inherit from ApiController as this is instantiated by a factory in the request pipeline. You should only inherit it for actual api controller instances, not for convenience of some of the existing methods. The best solution would be to throw custom exceptions in your Provider/Service/ whatever and catch them in your controller and return the correct HttpStatus OR let the exception pass through and it would result in a 500 status.
As requested though I have created a small wrapper around the ApiController that you could reuse in your Provider/Service/etc based on an interface (so its easy to abstract this AND easy to test).
// demo of controller calling your Provider
public class SomeController : ApiController
{
public async Task<IHttpActionResult> Get()
{
var wrapper = this.ActionWrapper();
var answerProvider = new AnswerProvider(wrapper);
var result = await answerProvider.CreateAsync(model);
}
}
// a simple extension on the ApiController
public static class WrapperExtension
{
public static IActionWrapper ActionWrapper(this ApiController controller)
{
return new ApiActionWrapperContext(controller);
}
}
// wrapped in interface so its easy to unit test the Provider
public interface IActionWrapper
{
OkResult Ok();
BadRequestResult BadRequest();
BadRequestErrorMessageResult BadRequest(string message);
OkNegotiatedContentResult<T> Ok<T>(T content);
}
// the implementation, this takes the current Controller and uses it as the context to return the same result types
// only implemented Ok and BadRequest as a demo, you can extend it as needed
public class ApiActionWrapperContext : IActionWrapper
{
private ApiController _controller;
public ApiActionWrapperContext(ApiController controller)
{
_controller = controller;
}
public BadRequestResult BadRequest()
{
return new BadRequestResult(_controller);
}
public BadRequestErrorMessageResult BadRequest(string message)
{
return new BadRequestErrorMessageResult(message, _controller);
}
public OkResult Ok()
{
return new OkResult(_controller);
}
public OkNegotiatedContentResult<T> Ok<T>(T content)
{
return new OkNegotiatedContentResult<T>(content, _controller);
}
}
// provider shortered with just some relevant code to demo
// notice constructor, the new private field, and the use of it
public class AnswerProvider
{
private IActionWrapper _actionWrapper;
public AnswerProvider(IActionWrapper actionWrapper)
{
if(actionWrapper == null)
throw new ArgumentNullException("actionWrapper");
_actionWrapper = actionWrapper;
}
public async Task<IHttpActionResult> CreateAsync(AnswerRequestModel model)
{
try
{
// Validate our answer count
await ValidateAnswerCountAsync(model.QuestionId);
// Create our model
var answer = ModelFactory.Create(model);
// Add our images to our answer
answer.Images = model.Images;
// Save our model
this._answerService.Create(answer);
// Save the database changes
await this._unitOfWork.SaveChangesAsync();
// Return our updated model
return this._actionWrapper.Ok(ModelFactory.Create(answer));
// If there is an error
}
catch (Exception ex)
{
// Return our error
return this._actionWrapper.BadRequest(ex.Message.ToString());
}
}
}

WebAPI force deserialization different object than defined in controller method

I'm trying to introduce automapper in such a way that for WebAPI all DTOs are transparent, so basically I want to map objects in runtime and convert them to Domain objects before they reach controller method.
I have automapper filter attribute
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class AutoMapAttribute : ActionFilterAttribute
{
private readonly Type _sourceType;
private readonly Type _destType;
public AutoMapAttribute(Type sourceType, Type destType)
{
_sourceType = sourceType;
_destType = destType;
}
#region Overrides of ActionFilterAttribute
/// <summary>Occurs before the action method is invoked.</summary>
/// <param name="actionContext">The action context.</param>
public override void OnActionExecuting(HttpActionContext actionContext)
{
var mapper = ServiceLocator.Locator.GetInstance<IMapper>();
var jsonObject = actionContext.ActionArguments.FirstOrDefault(x => x.Value.GetType() == typeof(Newtonsoft.Json.Linq.JObject));
var dto = JsonConvert.DeserializeObject(jsonObject.Value.ToString(), _sourceType);
object model = mapper.Map(dto, _sourceType, _destType);
actionContext.ActionArguments[jsonObject.Key] = model;
base.OnActionExecuting(actionContext);
}
#endregion
#region Overrides of ActionFilterAttribute
/// <summary>Occurs after the action method is invoked.</summary>
/// <param name="actionExecutedContext">The action executed context.</param>
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
base.OnActionExecuted(actionExecutedContext);
}
#endregion
public Type SourceType
{
get { return _sourceType; }
}
public Type DestType
{
get { return _destType; }
}
}
and controller method:
[HttpPost]
[Route("")]
[AutoMap(typeof(Public.Dto.Product), typeof(Domain.Model.Product))]
public IHttpActionResult Post(object product)
{
_productBusinessLogic.Create(product);
return Ok();
}
and it works quite well as product variable in controlelr method is actually my Domain product. Now I want to change object type to concrete Domain.Product type in controller method definition. Unfortunately WebAPI tries to immediately deserialize object that comes from Request to this object type which breaks the whole idea.
I am able to use OWIN if it helps.
The key point is that the handling of action filters takes place after model binding in the Web API request stack.
Using OWIN we could read the original body as the source type and then map it to the target type:
Override the OnActionExecutingAsync method:
public override async Task OnActionExecutingAsync(HttpActionContext actionContext,
CancellationToken cancellationToken)
{
var argumentKey = actionContext.ActionArguments.FirstOrDefault().Key;
var owinContext = (OwinContext) actionContext.Request.Properties["MS_OwinContext"];
owinContext.Request.Body.Seek(0, SeekOrigin.Begin);
var source = await actionContext.Request.Content.ReadAsAsync(_sourceType, cancellationToken);
actionContext.ActionArguments[argumentKey] = Mapper.Map(source, _sourceType, _destType);
await base.OnActionExecutingAsync(actionContext, cancellationToken);
}
To reread the request's content in the action filter we need to add some handler to the pipeline in the OWIN Startup class:
public class Startup
{
public void Configuration(IAppBuilder app)
{
var config = new HttpConfiguration();
app.Use(async (context, next) =>
{
var content = context.Request.Body;
if (content == Stream.Null)
{
await next();
}
else
{
using (var stream = new MemoryStream())
{
await content.CopyToAsync(stream);
stream.Position = 0;
context.Request.Body = stream;
await next();
}
context.Request.Body = content;
}
});
app.UseWebApi(config);
}
}

Categories

Resources