Best way to populate a base viewmodel - asp.net-mvc-3

I know there are two ways to populate a base modelview:
First way, using OnActionExecuted method:
public abstract class BaseController : Controller
{
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
var result = filterContext.Result as ViewResultBase;
if (result != null)
{
var model = filterContext.Controller.ViewData.Model as BaseViewModel;
if (model != null)
{
model.CurrentUser = HttpContext.Current.Request.Cookies["CurrentUser"].Value;
}
}
}
Second way, property get accessor:
public abstract class BaseViewModel
{
public string CurrentUser
{
get
{
return HttpContext.Current.Request.Cookies["CurrentUser"].Value;
}
}
}
Which way is better? any pros/cons I'm missing?

I'd do it the second way, as it'd be a little more clearer as to what the model value of CurrentUser is, without having to go poking around in the controllers.

Related

Modify ControllerContext in CreateController method of DefaultControllerFactory in asp.net core mvc

I want to override my controllers method. Here i have overriden CreateController method of DefaultControllerFactory to return the CatalogCustomController object if request come for CatalogController.
But the problem is here that i need to pass all the dependency into controller constructor.
public class CustomControllerFactory: DefaultControllerFactory
{
public CustomControllerFactory(ICatalogModelFactory catalogModelFactory,
IProductModelFactory productModelFactory,
IControllerActivator controllerActivator, IEnumerable<IControllerPropertyActivator> propertyActivators)
:base(controllerActivator, propertyActivators)
{
this._catalogModelFactory = catalogModelFactory;
this._productModelFactory = productModelFactory;
}
public override object CreateController(ControllerContext context)
{
if (context.ActionDescriptor.ControllerTypeInfo.AsType() == typeof(CatalogController))
{
return new CatalogCustomController(_catalogModelFactory,
_productModelFactory,
_categoryService,
}
return base.CreateController(context);
}
}
While i want to do it something like this, by modifying
ControllerContext context
public override object CreateController(ControllerContext context)
{
if (context.ActionDescriptor.ControllerTypeInfo.AsType() == typeof(CatalogController))
{
context.ActionDescriptor.ControllerName = "CatalogCustomController";
}
return base.CreateController(context);
}
You could try register Controller into IServiceCollection, and then retrieve Controller from IServiceCollection in CreateController.
Extension method for AddControllersAsServices
public static class Extension
{
public static IMvcBuilder AddControllersAsServices(this IMvcBuilder builder)
{
var feature = new ControllerFeature();
builder.PartManager.PopulateFeature(feature);
foreach (var controller in feature.Controllers.Select(c => c.AsType()))
{
builder.Services.TryAddTransient(controller, controller);
}
builder.Services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
return builder;
}
}
Register Services
services.AddMvc()
.AddControllersAsServices();
CustomControllerFactory
public class CustomControllerFactory : DefaultControllerFactory
{
public CustomControllerFactory(
IControllerActivator controllerActivator, IEnumerable<IControllerPropertyActivator> propertyActivators)
: base(controllerActivator, propertyActivators)
{
}
public override object CreateController(ControllerContext context)
{
if (context.ActionDescriptor.ControllerTypeInfo.AsType() == typeof(CatalogController))
{
return context.HttpContext.RequestServices.GetRequiredService(typeof(CatalogCustomController));
}
return base.CreateController(context);
}
}
I found a solution to do it without registering the controllers as service.
Inherit the custom controller from main one.
In custom controller factory, get the type of requested controller.
Replace the ControllerTypeInfo with the custom one
public override object CreateController(ControllerContext context)
{
Type typeOfController = context.ActionDescriptor.ControllerTypeInfo.UnderlyingSystemType;
if (typeOfController == typeof(Nop.Web.Controllers.CatalogController))
{
context.ActionDescriptor.ControllerTypeInfo = typeof(Controllers.CatalogCustomController).GetTypeInfo();
}
else if (typeOfController == typeof(Nop.Web.Areas.Admin.Controllers.ProductController))
{
context.ActionDescriptor.ControllerTypeInfo = typeof(Areas.Admin.Controllers.ProductCustomController).GetTypeInfo();
}
return base.CreateController(context);
}<pre>

Custom ErrorHandling action filter that catches only certain type of exceptions

I have implemented the following action filter to handle ajax errors:
public class HandleAjaxCustomErrorAttribute : ActionFilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
if (!filterContext.HttpContext.Request.IsAjaxRequest()) return;
filterContext.Result = AjaxError(filterContext.Exception.Message, filterContext);
//Let the system know that the exception has been handled
filterContext.ExceptionHandled = true;
}
}
I want the filter to be able to catch only certain types of errors and use it like this in the controller action:
[HandleAjaxCustomErrorAttribute(typeof(CustomException))]
public ActionResult Index(){
// some code
}
How can this happen? Thanks!
I take it you're looking for this: http://msdn.microsoft.com/en-us/library/aa288454%28v=vs.71%29.aspx#vcwlkattributestutorialanchor1
To give the attribute a parameter you can either make a nonstatic property or have a constructor. In your case it'd look something like this:
public class HandleAjaxCustomErrorAttribute : ActionFilterAttribute, IExceptionFilter
{
private Type _exceptionType;
public void OnException(ExceptionContext filterContext)
{
if (filterContext.Exception.GetType() != _exceptionType) return;
if (!filterContext.HttpContext.Request.IsAjaxRequest()) return;
filterContext.Result = AjaxError(filterContext.Exception.Message, filterContext);
//Let the system know that the exception has been handled
filterContext.ExceptionHandled = true;
}
public HandleAjaxCustomErrorAttribute(Type exceptionType)
{
_exceptionType = exceptionType;
}
}

Why would a custom MVC3 action filter work on one controller action but not on another?

Here's the situation. I've got a single action filter that I'm using in two different controllers. The action filter is defined as:
public class ValidSubmissionAttribute : FilterAttribute, IActionFilter
{
public void OnActionExecuted(ActionExecutedContext filterContext)
{
}
public void OnActionExecuting(ActionExecutingContext filterContext)
{
var controller = filterContext.Controller;
var session = filterContext.HttpContext.Session;
var isValid = controller.TempData["IsValid"];
if (isValid == null || !(bool)isValid)
{
SharedUtilities.LogOutUser(session, controller.ViewData.ModelState);
filterContext.Result = SharedUtilities.GetThankYouRedirect();
}
}
}
When I invoke the Attribute in one controller, like this:
[HttpPost]
public ActionResult DoSomething(string button, Model data)
{
try
{
if (ModelState.IsValid)
{
TempData["IsValid"] =
Request.Form["ValidRequest"] == Session.SessionID;
Session["VerifyDoingSomethingData"] = data;
return RedirectToAction("VerifyDoingSomething");
}
}
catch (Exception ex)
{
}
}
[ValidSubmission]
public ActionResult VerifyDoingSomething()
{
ViewData.Model = Session["VerifyDoingSomethingData"];
return View("VerifyDoingSomething");
}
it functions as expected. However, when I call it from a different controller, like this:
[HttpPost]
public ActionResult Index(string button, Model data)
{
try
{
if (ModelState.IsValid)
{
TempData["IsValid"] =
Request.Form["ValidRequest"] == Session.SessionID;
Session["ViewModel"] = data;
return RedirectToAction("VerifyCancellation");
}
}
catch (Exception ex)
{
}
}
[ValidSubmission]
public ActionResult VerifyCancellation()
{
ViewData.Model = Session["ViewModel"];
return View("VerifyCancellation");
}
the attribute doesn't run at all. My breakpoint in the OnActionExecuting method doesn't get hit.
If I had to guess, I'd say there was some difference in the controllers or in the action methods, but they appear to be functionally similar. Any insights? Why would I be seeing such different behavior?
Aaaaand, I'm a schmuck.
Turns out there's a completely different execution path that I'd forgotten about. That path didn't have the TempData information to use in the ValidSubmisionAttribute. Everything is functioning correctly now.

Defining authentication requirements in MVC3 by action method attribute

I've got an MVC3 application with 4 levels of authentication, and 4 base controllers that tie to each one:
Unauthenticated - BaseController
User - BaseAuthController : BaseController
Advisor - BaseAdvisorController : BaseAuthController
Admin - BaseAdminController : BaseAuthController
Right now I have a series of overrides in place for special cases... e.g. a controller that is typically only for admins can have an action method or two that advisors can use... I have the overrides defined as strings in an array.
public class BaseAuthController : BaseController
{
/// <summary>
/// Enter action names in here to have them ignored during login detection
/// </summary>
public string[] NoAuthActions = new string[] { };
/// <summary>
/// Actions only usable by Users+
/// </summary>
public string[] UserOnlyActions = new string[] { };
/// <summary>
/// Actions only usable by Advisors+
/// </summary>
public string[] AdvisorOnlyActions = new string[] { };
/// <summary>
/// Actions only usable by Admins+
/// </summary>
public string[] AdminOnlyActions = new string[] { };
.......
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
//special code here to determine what to do with requested action...
//verifies that user is logged in and meets requirements for method...
//if not, redirects out to another page...
}
}
At the controller level I have them defined like this...
public class GrowerController : BaseAdminController
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
UserOnlyActions = new string[] { "GrowthStageSelection" };
AdvisorOnlyActions = new string[] { "Landing", "SeedSelection", "UpdateProjection",
"NitrogenApplications", "DeleteNitrogen", "MassUpload",
"VerifyHolding", "ConfirmHolding", "DeleteHoldingDir", "DeleteHoldingFile" };
base.OnActionExecuting(filterContext);
}
//......
[HttpPost]
public ActionResult GrowthStageSelection(int growerID, int reportGrowthStageID = 0)
{
//code...
}
}
This system has actually worked out pretty well for us, but the problem for me has been that it feels messy. You have to define the methods one place, and override their authentication level elsewhere if necessary. If you change the method name you have to remember to change it elsewhere.
What I'd LOVE to be able to do is decorate the methods themselves with authentication specific attributes and do away the string-based definitions (or at least make them transparent and use List<string> dynamically or something). Here's an example of what I'm looking for...
[HttpPost]
[AdvisorAuthentication]
public ActionResult GrowthStageSelection(int growerID, int reportGrowthStageID = 0)
{
//code...
}
Problem is that I can't find a good way to achieve this with attributes. I've tried creating subclasses of ActionFilterAttribute but they run after my BaseAuthController's override for OnActionExecuting. At that point it's too late in the game to add new methods to the string lists dynamically, and moreover I can't even seem to access the current controller instance from the attributes.
Maybe this whole idea is off base. Can anyone point me in the right direction? Thanks.
Final solution
First, I went ahead and deleted all of my special controllers except for BaseController - I had no use for them anymore. I moved the current special authentication code from BaseAuthController into BaseController. Next, I defined a series of attributes for each of my authentication states:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class BaseAuthAttribute : Attribute
{
public AuthLevels AuthLevel { get; protected set; }
public BaseAuthAttribute(AuthLevels level)
{
this.AuthLevel = level;
}
public override string ToString()
{
return string.Format("Auth Required: {0}", this.AuthLevel.ToString());
}
}
public class UnauthenticatedAccess : BaseAuthAttribute
{
public UnauthenticatedAccess()
: base(AuthLevels.Unauthenticated)
{
}
}
public class UserAccess : BaseAuthAttribute
{
public UserAccess()
: base(AuthLevels.User)
{
}
}
public class AdvisorAccess : BaseAuthAttribute
{
public AdvisorAccess()
: base(AuthLevels.Advisor)
{
}
}
public class AdminAccess : BaseAuthAttribute
{
public AdminAccess()
: base(AuthLevels.Admin)
{
}
}
Then in my BaseController I modified the OnActionExecuting to check the current auth level of the logged in user (if any) against the attribute. This is much cleaner than it was before! (Note: SessionUser and AuthLevels are custom objects for our project - you won't have those)
public partial class BaseController : Controller
{
/// <summary>
/// Override security at higher levels
/// </summary>
protected bool SecurityOverride = false;
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
BaseAuthAttribute authAttribute = filterContext.ActionDescriptor.GetCustomAttributes(false).OfType<BaseAuthAttribute>().FirstOrDefault();
if (authAttribute == null) //Try to get attribute from controller
authAttribute = filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes(false).OfType<BaseAuthAttribute>().FirstOrDefault();
if (authAttribute == null) //Fallback to default
authAttribute = new UnauthenticatedAccess(); //By default, no auth is required for base controller
if (!SessionUser.LoggedIn
&& authAttribute.AuthLevel == AuthLevels.Unauthenticated)
{
SecurityOverride = true;
}
else if (SessionUser.LoggedIn
&& SessionUser.LoggedInUser.AuthLevel >= (int)authAttribute.AuthLevel)
{
SecurityOverride = true;
}
if (!SessionUser.LoggedIn && !SecurityOverride)
{
//Send to auth page here...
return;
}
else if (!SecurityOverride)
{
//Send somewhere else - the user does not have access to this
return;
}
base.OnActionExecuting(filterContext);
}
// ... other code ...
}
That's it! Now just put it to use like so...
[AdminAccess]
public class GrowerController : BaseController
{
public ActionResult Index()
{
//This method will require admin access (as defined for controller)
return View();
}
[AdvisorAccess]
public ActionResult Landing()
{
//This method is overridden for advisor access or greater
return View();
}
}
If I understood your question properly, you can implement your own custom attributes (not authorisation attributes) and in the overriden OnActionExecuting of the base controller, you can retrieve the custom attributes of the executing method and based on wich ones are defined you can take appropriate actions. So if a method has the [AdvisorAuthentication] you know that you need to check for those credentials before proceeding.
EDIT:
I don't have an example to point you to as this is something I have implemented in one of my projects. I have no access to that code now but here is an outline:
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
IEnumerable<MyCustomAttribute> attributes = filterContext.ActionDescriptor.GetCustomAttributes(false).OfType<MyCustomAttribute>();
foreach (MyCustomAttributeobj in attributes)
{
switch(MyCustomAttribute.AttribType){
case MyCustomeAttribute.AdvisorAuthentication:
break;
case MyCustomeAttribute.AdminAuthentication:
break;
}
}
}
You can implement just one custom attribute MyCustomAttribute and have it accept a parameter to indicate which authorization type you want. Like that the use of the attribute becomes [MyCustomAttribute("MyCustomeAttribute.AdminAuthentication")]
You can create different Authorize attributes extending IAuthorizationFilter and FilterAttribute something like this
public sealed class AuthenticateAdvisorAttribute : IAuthorizationFilter, FilterAttribute
{
public void OnAuthorization(AuthorizationContext filterContext)
{
//advisor specific logic goes here
}
}
public sealed class AuthenticateAdminAttribute : IAuthorizationFilter, FilterAttribute
{
public void OnAuthorization(AuthorizationContext filterContext)
{
//admin specific logic goes here
}
}
And then you can apply those attributes wherever you require to controller classes/actions
as
[AuthenticateAdmin]
public class AdminController : Controller
{
}
[AuthenticateAdvisor]
public class AdvisorController : Controller
{
}

If there is ViewBag for ViewData, why is there no TempBag for TempData?

Why is there no dynamic dictionary object for TempData as there is for ViewData?
There isn't because no-one ever bothered to implement it. But that would be trivially easy to do. For example as an extension method (unfortunately extension properties are not yet supported in .NET so you cannot quite get the syntax you might have hoped for):
public class DynamicTempDataDictionary : DynamicObject
{
public DynamicTempDataDictionary(TempDataDictionary tempData)
{
_tempData = tempData;
}
private readonly TempDataDictionary _tempData;
public override IEnumerable<string> GetDynamicMemberNames()
{
return _tempData.Keys;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = _tempData[binder.Name];
return true;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
_tempData[binder.Name] = value;
return true;
}
}
public static class ControllerExtensions
{
public static dynamic TempBag(this ControllerBase controller)
{
return new DynamicTempDataDictionary(controller.TempData);
}
}
and then:
public ActionResult Index()
{
this.TempBag().Hello = "abc";
return RedirectToAction("Foo");
}
The question is: why would you need that and how is it better/safer than:
public ActionResult Index()
{
TempData["Hello"] = "abc";
return RedirectToAction("Foo");
}

Resources