I have a asp .net mvc application. For all my other attributes, I've used data annotations to verify the attributes. But I would like to have a custom validation. I was reading online that a custom data annotation validation may be the approach to take.
Following this link here ASP.NET MVC: Custom Validation by DataAnnotation it looks like a great solution. The only issue I'm coming across is how do I access my DBContext if it's not within a controller.
What I have so far
This is the code I typicalled used in controllers to grab the current user and db context.
Controller code
private ApplicationDbContext _dbContext => HttpContext.GetOwinContext().Get<ApplicationDbContext>();
private ApplicationUserManager _userManager;
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
ViewModel
[HasNoRedemption]
public string code {get; set;}
HasNoRedemption.cs
public class HasNoRedemption : ValidationAttribute
{
public override bool IsValid(object value)
{
//check if user has ever claimed the code, return true is good
}
}
If I may get some assistance in making a custom validation such that I'll be able to check the db or a suggestion for a better approach.
1) I would not recommend using data annotation attributes to implement your business logic inside. Validation attributes are ought to contain pure functions. The intention behind the validation attributes is to narrow down the range of the input data that is considered to be correct. Not to implement business rules of your application
2) If you really wish to do what you want (for curiosity sake), check the following instructions: https://andrewlock.net/injecting-services-into-validationattributes-in-asp-net-core/
public class CustomValidationAttribute : ValidationAttribute
{
protected override ValidationResult IsValid(
object value, ValidationContext validationContext)
{
// validationContext.GetService() ...
}
}
Related
I have a solution that contains just one single project (MVC 5 with Identity and EF).
The db context is an ApplicationDbContext (a subclass of IdentityDbContext which is itself a subclass of EF DbContext).
I also have a custom validation attribute that needs to use the db context to do its thing:
public class RequiredIfRol : ValidationAttribute, IClientValidatable {
//...
protected override ValidationResult IsValid(object value, ValidationContext validationContext) {
// ...
string nomRol = "";
//instance and use the ApplicationDbContext
using (ApplicationDbContext db = new ApplicationDbContext()) {
var roldb = db.Roles.Find(rolId);
if (roldb == null) return ValidationResult.Success;
nomRol = db.Roles.Find(rolId).Name;
}
// more code here
}
}
This is working ok.
Now, after reading about it, I am trying to separate this MVC project into several projects:
a class library for the DAL (it will contain the EF models and the ApplicationDbContext),
another one for the business logic,
another one with common things
and then the MVC project.
I am noob on this, since I have always had everything inside the same MVC project, so I am confused:
I think the attribute should reside in the Common project, so that it can be referenced from the DAL (to decorate models) and also from the MVC (to decorate viewmodels).
Since the Common project will be referenced from all the other projects, I guess I can not reference any of those projects from the Common (circular reference?). and, as the ApplicationDbContext (which the attribute needs to use) will reside in the DAL project, I have a problem here...
I am pretty sure I am designing this badly, but cant find a correct way to do it.. Any help?
ADDED:
this is something I had tried:
1.- in the common library, defined an interface:
public interface IMyInterface {
System.Data.Entity.IDbSet<CustomRole> Roles { get; set; }
}
2.- in the applicationDbContext, modified its declaration so that it implements the interface:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, CustomRole, int, CustomUserLogin, CustomUserRole, CustomUserClaim>, IMyInterface {...}
3.- then in the attribute, try to get an implementation of the interface by adding an getter property:
private IMyInterface _dbcontext { get { return DependencyResolver.Current.GetService<IMyInterface>(); } }
and then in the IsValid() method:
var roldb = _dbcontext.Roles.Find(rolId);
if (roldb == null) return ValidationResult.Success;
nomRol = _dbcontext.Roles.Find(rolId).Name;
but this did not work...
One way around it would be to create an abstraction for your code using context - IRoleFinder and put in the common project alongside with your RequiredIfRol. Then implement it in business logic layer and inject it in the MVC project. This way you should be able to decouple you attribute from context.
ValidationAttribute.IsValid() has a ValidationContext parameter which you can use to resolve the dependency via ValidationContext.GetService method.
UPD
Per your request in comment:
For simplification, lets say I have only 2 projects (the class library with the attribute, and the MVC project with everything else).
In library you will have something like this:
interface IRoleFinder
{
CustomRole Find(int id);
}
public class RequiredIfRol : ValidationAttribute, IClientValidatable {
//...
protected override ValidationResult IsValid(object value, ValidationContext validationContext) {
// ...
var roleFinder = (IRoleFinder) validationContext.GetService(typeof(IRoleFinder));
}
}
In your MVC project:
public class RoleFinder : IRoleFinder
{
public CustomRole Find(int id)
{
// use context here
}
}
And in Startup (this is for ASP.NET Core, for MVC 5 you should find another way):
services.AddTransient<IRoleFinder, RoleFinder>()
I am following this example: https://learn.microsoft.com/en-us/aspnet/core/mvc/models/validation and trying to implement my own custom attribute for validation.
Now, the viewmodel has two fields that I wish to access from inside this method, so that they can be rendered with the "data-val" attributes. My question is, how can I get say a property called "Myprop" from context here? When I debug I can se the information under context.ActionContext.ViewData.Model but I have no way of getting that info other then during the debug when I use Visual Studio "quick watch" feature. The custom attributes are on properties that are on the viewmodel.
public void AddValidation(ClientModelValidationContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
MergeAttribute(context.Attributes, "data-val", "true");
MergeAttribute(context.Attributes, "data-val-classicmovie", GetErrorMessage());
var year = _year.ToString(CultureInfo.InvariantCulture);
MergeAttribute(context.Attributes, "data-val-classicmovie-year", year);
}
I ran into a similar problem. The short answer is that you can't from there. You only have access to the metadata from there, not the actual model. The reason for this is that your model metadata and validation based on it are done on first time use, and then cached. So you'll never be able to change what validation rules to return based on the model via an attribute decorator.
If you need to dynamically decide which client side data-val-* attributes to render based off the instance/content of your model, you would need to inherit from DefaultValidationHtmlAttributeProvider, instead of using attributes, and override the AddValidationAttributes method. It's the only way I've found to do this so far. This is because inside of this method, you have access to the ModelExplorer
public class CustomValidationHtmlAttributeProvider : DefaultValidationHtmlAttributeProvider
{
private readonly IModelMetadataProvider metadataProvider;
public CustomValidationHtmlAttributeProvider(IOptions<MvcViewOptions> optionsAccessor, IModelMetadataProvider metadataProvider, ClientValidatorCache clientValidatorCache)
: base(optionsAccessor, metadataProvider, clientValidatorCache)
{
this.metadataProvider = metadataProvider;
}
public override void AddValidationAttributes(ViewContext viewContext, ModelExplorer modelExplorer, IDictionary<string, string> attributes)
{
//base implimentation
base.AddValidationAttributes(viewContext, modelExplorer, attributes);
//re-create the validation context (since it's encapsulated inside of the base implimentation)
var context = new ClientModelValidationContext(viewContext, modelExplorer.Metadata, metadataProvider, attributes);
//Only proceed if it's the model you need to do custom logic for
if (!(modelExplorer.Container.Model is MyViewModelClass model) || !modelExplorer.Metadata.PropertyName == "Myprop") return;
//Do stuff!
var validationAttributeAdapterProvider = viewContext.HttpContext.RequestServices.GetRequiredService<IValidationAttributeAdapterProvider>();
if (model.Myprop)
{
var validationAdapter = (RequiredAttributeAdapter)validationAttributeAdapterProvider.GetAttributeAdapter(new RequiredAttribute(), null);
validationAdapter.Attribute.ErrorMessage = "You not enter right stuff!";
validationAdapter.AddValidation(context);
}
}
}
And then register this class in the ConfigureServices() of your Startup
public void ConfigureServices(IServiceCollection services)
{
//All your other DI stuff here
//register the new ValidationHtmlAttributeProvider
services.AddSingleton<ValidationHtmlAttributeProvider, PseudoAttributeValidationHtmlAttributeProvider>();
}
The downside, is that if you have multiple models you need to do this for, it gets really ugly really fast. If anyone has found a better method, I'd love to hear it :-)
I am aiming to create a custom implementation of MVC's IAuthorizationFilter by subclassing AuthorizeAttribute and overriding bool AuthorizeCore(HttpContextBase httpContext);
Being the hipster I am, I want to do this TDD style.
(Just because I said I'm a hipster doesn't mean you can navigate back. Yes, I saw that.)
Additionally, I want to inject into certain parameters of the constructor, using ninject like so.
So, my question is how do I unit test such a setup?
Here is my Attribute setup:
public class MyAuthorize : FilterAttribute {
public MyAuthorize(params string[] Activities)
{
this.RequestedActivities = Activities.ToList();
}
public IEnumerable<string> RequestedActivities { get; set; }
}
public class MyAuthorizeFilter : AuthorizeAttribute
{
private readonly IEnumerable<string> _RequestingActivities;
private readonly IUserService _UserService;
public MyAuthorizeFilter(IUserService UserService, IEnumerable<string> Activities)
{
this._RequestingActivities = Activities;
_UserService = UserService;
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
return (_RequestingActivities.All(c=>c == "Permitted"));
}
}
I attempted to test the void OnAuthorization(AuthorizationContext filterContext); method exposed by AuthorizeAttribute but there wasn't anything to assert against. filterContext was not altered in any noticeable way.
My next step was to create a mock class with [MyAuthorize("APermission")] on an action, then invoke that like so:
controller.ActionInvoker.InvokeAction(controller.ControllerContext, "Permitted");
But since I'm not actually using MyAuthorizeFilter it's not calling the code that actually does the authorization checks.
I honestly have no idea how to proceed.
Remember, when you unit test, you're not testing the implementation of things like attributes being hooked up and being called. You're testing the actual attribute's functionality.
So all you need to do is instantiate you MyAuthorize class, probably passing in mocked services and activities. Then you simply call the methods that will get called by the framework, and verify that the proper results are achieved.
In this case, you would probably just call MyAuthorize.OnAuthorization() passing in a mocked authorization context, and check the state of the filterContext.Response object, which will be unchanged if it succeeds and will contain an Unauthorized (401) if it fails.
It may also throw exceptions if the filterContext is null or not configured correctly.
You can look at the source code to the AuthorizeAttribute to see what it's doing.. so you know what to assert against.
http://aspnetwebstack.codeplex.com/SourceControl/latest#src/System.Web.Mvc/AuthorizeAttribute.cs
I have found out that I need the current logged in user data in nearly every class (controllers, view, HTML helpers, services and so on). So I thought about to create an "Ambient Context" instead of injecting an IUserService or the User directly.
My approach looks something like that.
public class Bootstrapper
{
public void Boot()
{
var container = new Container();
// the call to IUserService.GetUser is cached per Http request
// by using a dynamic proxy caching mechanism, that also handles cases where we want to
// invalidate a cache within an Http request
UserContext.ConfigureUser = container.GetInstance<IUserService>().GetUser;
}
}
public interface IUserService
{
User GetUser();
}
public class User
{
string Name { get; set; }
}
public class UserContext : AbstractFactoryBase<User>
{
public static Func<User> ConfigureUser = NotConfigured;
public static User ActiveUser { get { return ConfigureUser(); } }
}
public class AbstractFactoryBase<T>
{
protected static T NotConfigured()
{
throw new Exception(String.Format("{0} is not configured", typeof(T).Name));
}
}
Example usage:
public class Controller
{
public ActionResult Index()
{
var activeUser = UserContext.ActiveUser;
return View();
}
}
Is my approach correct or do I missing something? Do you have better solutions in mind?
UPDATE:
More Detail of the User class:
public class User
{
string Name { get; set; }
bool IsSuperUser { get; set;}
IEnumerable<AzManOperation> Operations { get; set}
}
In Controllers we need to check if an User is a SuperUser to only provide the SuperUser some extra functionality.
public class BaseController : Controller
{
private readonly IUserService _userService;
BaseControler(IUserService userService)
{
_userService = userService
}
public User ActiveUser
{
get { return _userService.GetUser(); }
}
}
In Views we check Operations to only show an edit or delete button if the user has the right to do so. A view never uses the DependencyResolver, but ViewBag or ViewModel. My idea here is to implementing a custom ViewBasePage and providing an ActiveUser property, so that Views have an easy accesss.
In HtmlHelpers we render controls depending on IsSuperUser and Operations (passing in the User object or using DependencyResolver).
In Service Classes we need those properties too. For instance to decide if a basket is valid or not (check if the User is allowed to buy articles that are not in a standard list). So the Service class depends on IUserService and calling GetUser().
In Action Filters to force the user to change his password (only if it is not a SuperUser and User.ForcePasswordChange is true). Here we use the DependencyResolver.
My wish is to have a more easily way to get the User object, instead of using DependencyResolver.Current.GetService().GetUser() or using things like ViewBag.ActiveUser = User.
The User object is an object that is almost everywhere needed to check permissions or the like.
In Views we check Operations to only show an edit or delete button if the user has the right to do so.
The view should not do this check. The Controller should return a view model to the view that contains boolean properties that state whether those buttons should be visible. Returning a bool with IsSuperUser already moves to much knownledge into the view. The view shouldn't know that it should show a certain button for a super user: that's up to the controller. The view should only be told what to display.
If almost all views have this code, there are ways to extract repetitive parts out of your views, for instance with partial views. If you're finding yourself repeating those properties over many view models, perhaps you should define an envelope view model (a generic view model that wraps the specific model as T). A controller can create its view model, while you create a service or cross-cutting concern that wraps it in your envelope.
In Service Classes we need those properties too. For instance to decide if a basket is valid or not
In this case you are talking about validation, which is a cross-cutting concern. You should use decorators to add this behavior instead.
This is MVC, right?
You're reinventing the wheel.
Add this method to your Global.asax.cs:
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
var authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
var ticket = FormsAuthentication.Decrypt(authCookie.Value);
var user = ticket.Name;
var identity = new GenericIdentity(user, "Forms");
var principal = new GenericPrincipal(identity, null);
Context.User = principal;
}
}
This example shows forms authentication which you can strip if you're using another mechanism. The key is these three lines:
var identity = new GenericIdentity(user, "Forms");
var principal = new GenericPrincipal(identity, null);
Context.User = principal;
GenericIdentity and GenericPrincipal can be replaced with anything you want as long as they implement the (trivial) IIdentity and IPrincipal interfaces. You can create your own implementations of these classes with whatever extra properties you need.
You can then access the authenticated user from all the things you listed - controllers, views, etc. - via HttpContext.Current.User (which is static).
If you created your own implementation of IPrincipal you can just cast that reference to your custom type.
You'll note that IPrincipal has a method called IsInRole, so you'd say:
if (HttpContext.Current.User.IsInRole("SuperUser"))
TL;DR - you are overengineering something ASP.NET has already solved, and I'd have an aneurysm if I saw the types you're proposing in a production application.
I think the easiest and maintainable solution is to create a static class CurrentUserProvider which has only one method Get(HttpContextBase) that returns the current user, behind the scene you can use the DependencyResolver to get the service that actually returns the user. Then where you need the CurrentUser you can call CurrentUserProvider.Get(context) and do whatever custom logic you need to perform.
The other solution that you are trying to do is injecting the service in the base controller constructor which is okay if you have handful of controllers, it would become an issue if you have quite a number of controllers and not all of the controllers requires that service. Writing tests for those controller would be such pain in the neck, because you have to create stubs/mocks for that service for all your controller tests. Maybe you can use property injection instead of constructor to address it.
You can use the same property injection for Filters too.
Now, the remaining two are the view and the helper. For View you can create special base class that inherits from WebViewPage/ViewPage and use the IViewActivator to inject the service and the same applies for the helpers, create helpers that inherits from system helpers and use those in your base controllers and views.
I think the second approach is bit cumbersome and it does not add that much value to do all those custom things.
So my suggestion is to go with the first.
I was asking a related question but messed the title up and no-one would understand it. Since I am able now to ask the question more precisely, I decided to reformulate it in a new question and close the old one. Sorry for that.
So what I want to do is passing data (my custom user's nickname as stored in the db) to the LoginUserControl. This login gets rendered from the master page via Html.RenderPartial(), so what I really need to do is making sure that, say ViewData["UserNickname"] is present on every call. But I don't want to populate ViewData["UserNickname"] in each and every action of every controller, so I decided to use this approach and create an abstract base controller which will do the work for me, like so:
public abstract class ApplicationController : Controller
{
private IUserRepository _repUser;
public ApplicationController()
{
_repUser = RepositoryFactory.getUserRepository();
var loggedInUser = _repUser.FindById(User.Identity.Name); //Problem!
ViewData["LoggedInUser"] = loggedInUser;
}
}
This way, whatever my deriving Controller does, the user information will already be present.
So far, so good. Now for the problem:
I can't call User.Identity.Name because User is already null. This is not the case in all of my deriving controllers, so this is specific for the abstract base controller.
I am setting the User.Identity.Name via FormsAuthentication at another place in the code, but I think this can't be the problem - afaik User.Identity.Name can be null, but not User itself.
It looks to me like the HttpContext is not available (since also null ;-) and that I am missing a simple yet important point here. Can anyone give me some hints? I would really appreciate it.
The answer to this problem is actually quite simple. I can't execute the code from within the constructor for reasons pointed out by Raimond, but I can do it outside the constructor.
So what I did was overriding onActionExecuting() in the base controller class (I created a custom Attribute for it, but just overriding the method should also work) and then do my user lookup from there.
Now it works as expected and I have no repeated code.
The User property is not assigned until after the Controller has been instantiated, but you can gain early access from your constructor with:
System.Web.HttpContext.Current.User
My guess would be that the Controller's base constructor is not filling in the User, but that it is only known later when the ControllerContext is set for the Controller. You should check this in the documentation about the lifecycle of an MVC application, (the one here will probably do, although it might be a bit out of date since it's for the preview version), or just check the source code of MVC.
from the code that I have of MVC (also a preview version, but that should be fine):
(In Controller)
public IPrincipal User {
get {
return HttpContext == null ? null : HttpContext.User;
}
}
...
public HttpContextBase HttpContext {
get {
return ControllerContext == null ? null : ControllerContext.HttpContext;
}
}
I don't see en an implementation of a default constructor in the code.
That would prove that the ControllerContext is null at the time of construction.
So you should execute your code somewhere else.
Can you grab this using something like:
HttpContext currentContext = HttpContext.Current;
string userName = currentContext.User.Identity.Name;
Or is the HttpContext always empty??
Could you set the httpContext through the constructor of the abstract class? and use it this way?
Thanks Raimond. I was too tired to see the obvious.
#Keeney: Yes the context is always null. Raimond pointed out why. Thanks anyway, I didn't see why too :-)
My current working solution (albeit not what I wanted) is a Attribute that I use to decorate all my controller actions. Here is the implementation:
public class MasterPageDataAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
IUserRepository _repUser = RepositoryFactory.getUserRepository();
IPrincipal siteUser = filterContext.Controller.ControllerContext.HttpContext.User;
User loggedInUser = null;
if (siteUser == null || siteUser.Identity.Name == null)
{
//do nothing
}
else
{
loggedInUser = _repUser.findUserById(siteUser.Identity.Name);
}
filterContext.Controller.ViewData["LoggedInUser"] = loggedInUser ?? new User { Nickname = "Guest" };
}
}
I will be looking into how to get that code executed in a way that follows the DRY principle, since using attributes for that definitely means repeating oneself. Maybe some sort of interceptor (interesting idea) or hook might help.
Cheers for that.
I am doing this in a basecontroller implementation and it works as expected.
public abstract class BaseController : Controller
{
public bool LoggedOn
{
get { return User.Identity.IsAuthenticated; }
}
}
This always returns true or false for me so User != null
to Masterfu:
I did something similiar with your help, wish that can help latter visitors.
In my case, i need to create reposiotry of controllers for different users, yet in the constructor of controllers, (principal)User is not ready. So i created a attribute for controllers:
[CreateRepositoryByUser]
public class MFCController : Controller
{
protected MFCRepository _repository
{
get { return ViewData["repository"] as MFCRepository; }
}
...
the _repository, indeed, is not a private variable of controller, but somethign create by the attribute:
public class CreateRepositoryByUser : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
CreateRepository(filterContext);
}
public static void CreateRepository(ActionExecutingContext filterContext)
{
if (filterContext.Controller.ViewData["repository"] == null)
{
filterContext.Controller.ViewData["repository"] =
MFCRepository.CreateMFCRepository(filterContext.Controller.ControllerContext.HttpContext.User);
}
}
}
I put codes of creating the repository in a seperate method, in case of that other attributes may want to use (principal)User before this attribute being triggered.
Calling from a constructor is too soon in the MVC pipeline.
Moving code to OnAuthorization, you get authorized user in a parameter. Worked for me!
From your example I would do something like this:
public abstract class ApplicationController : Controller {
private IUserRepository _repUser;
protected override void OnAuthorization(AuthorizationContext filterContext)
{
_repUser = RepositoryFactory.getUserRepository();
var loggedInUser = _repUser.FindById(filterContext.HttpContext.User.Identity.Name); //Problem!
ViewData["LoggedInUser"] = loggedInUser;
}
}
Inject IPrincipal if you need User in the constructor.
// startup.cs
// Inject IPrincipal
services.AddTransient<IPrincipal>(provider => provider.GetService<IHttpContextAccessor>().HttpContext.User);
Then add as IPrincipal in your constructor. Note that it is guaranteed to be ClaimsPrincipal with ASPNET - because that's what HttpContext.User is.
Similar question
Select Project -> press F4 -> anonymous login -> false | windows authentication - > True