How can I override ChildActionOnly attribute set on controller class for a particular method? - asp.net

I have ChildActionOnly set on the class of a controller called 'NavController'. I'd like one of the methods to be exposed and not be restricted by the ChildActionOnly attribute. How can I do that?
Here's a glimpse of the controller:
[ChildActionOnly]
public class NavController : Controller
{
[NotChildActionOnly] //does something like this exist natively in asp.net Mvc?
public ActionResult GetSomething()
{
return View()
}
}
If the 'NotChildActionOnly' attribute doesn't exist natively then I'll go down the path of writing a custom action filter such as 'OverridableChildActionOnly(enable/disable)' but I wanted to make sure I wasn't reinventing the wheel.

Related

Set ViewBag property in the constructor of a ASP.NET MVC Core controller

My theme has some sort of breadcrumb. The controller is always the category. To avoid repeat myself, I want to set it in the constructor of the controller for all actions like this:
class MyController:Controller{
public MyController() {
ViewBag.BreadcrumbCategory = "MyCategory";
}
}
When I access ViewBag.BreadcrumbCategory in the layout-view, its null. In a Action it works:
class MyController:Controller{
public IActionResult DoSomething() {
ViewBag.BreadcrumbCategory = "MyCategory";
}
}
I'm wondering that setting a ViewBag property is not possible in a constructor? It would be annoying and no good practice to have a function called on every action which do this work. In another question using the constructor was an accepted answear, but as I said this doesn't work, at least for ASP.NET Core.
There is an GitHub issue about it and it's stated that this is by design. The answer you linked is about ASP.NET MVC3, the old legacy ASP.NET stack.
ASP.NET Core is written from scratch and uses different concepts, designed for both portability (multiple platforms) as well as for performance and modern practices like built-in support for Dependency Injection.
The last one makes it impossible to set ViewBag in the constructor, because certain properties of the Constructor base class must be injected via Property Injection as you may have noticed that you don't have to pass these dependencies in your derived controllers.
This means, when the Controller's constructor is called, the properties for HttpContext, ControllerContext etc. are not set. They are only set after the constructor is called and there is a valid instance/reference to this object.
And as pointed in the GitHub issues, it won't be fixed because this is by design.
As you can see here, ViewBag has a dependency on ViewData and ViewData is populated after the controller is initialized. If you call ViewBag.Something = "something", then you it will create a new instance of the DynamicViewData class, which will be replaced by the one after the constructor gets initialized.
As #SLaks pointed out, you can use an action filter which you configure per controller.
The following example assumes that you always derive your controllers from Controller base class.
public class BreadCrumbAttribute : IActionFilter
{
private readonly string _name;
public BreadCrumbAttribute(string name)
{
_name = name;
}
public void OnActionExecuting(ActionExecutingContext context)
{
base.OnActionExecuting(context);
var controller = context.Controller as Controller;
if (controller != null)
{
controller.ViewBag.BreadcrumbCategory = _name;
}
}
}
Now you should be able to decorate your controller with it.
[BreadCrumb("MyCategory")]
class MyController:Controller
{
}
I have the same issue and solve it overriding the OnActionExecuted method of the controller:
public override void OnActionExecuted(ActionExecutedContext context)
{
base.OnActionExecuted(context);
ViewBag.Module = "Production";
}
Here is a better way to do this for .NET Core 3.x, use the ResultFilterAttribute:
Create your own custom filter attribute that inherits from ResultFilterAttribute as shown below:
public class PopulateViewBagAttribute : ResultFilterAttribute
{
public PopulateViewBagAttribute()
{
}
public override void OnResultExecuting(ResultExecutingContext context)
{
// context.HttpContext.Response.Headers.Add(_name, new string[] { _value });
(context.Controller as MyController).SetViewBagItems();
base.OnResultExecuting(context);
}
}
You'll need to implement the method SetViewBagItems to populate your ViewBag
public void SetViewBagItems()
{
ViewBag.Orders = Orders;
}
Then Decorate your Controller class with the new attribute:
[PopulateViewBag]
public class ShippingManifestController : Controller
That's all there is to it! If you are populating ViewBags all over the place from your constructor, then you may consider creating a controller base class with the abstract method SetViewBagItems. Then you only need one ResultFilterAttribute class to do all the work.

Accessing AuthorizationAttribute within Controller

I have a custom AuthorizeAttribute written in MVC. I have it applied to a controller for security. In that AuthorizeAttribute class I have written are several variables I gathered from a web service call I would like to access inside the controller to prevent having to call the web service again. Is this possible?
Your best approach would be to use HttpContext.Current.Items for storing those variables because that data will only be valid for a single http request. Something like this:
public class CustomAuthorize : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext.User.Identity == null) return false;
if (!httpContext.Request.IsAuthenticated) return false;
var user = new WSUser(); //get this from your webservice
if(user == null) return false;
httpContext.Items.Add("prop", user.Property);
return user.Authorized;
}
}
public class HomeController : Controller
{
[CustomAuthorize]
public ActionResult Index()
{
var property = (string) HttpContext.Items["prop"];
return View();
}
}
You would also want to encapsulate logic for storing and retrieving items from HttpContext.Current into a separate class to keep the code clean and to follow Single responsibility principle
You could save these variables in a static class to store it. But, a elegant solution would be to have a modelbinder object that you call like parameter in your controller and that read the static class and return the properties that you need.
Perhaps, if you are applying security, the best will be that call the webservices each once.
Reference for your custom model binder

Overriding validation attribute from a base Model

i have a MVC controller called MyController with an action called MyAction. For other hand i have a Model called MyModel, and all this classes are in a project called Portal.Website (Asp.net MVC3 Application) that i use as a generic website and that store common functionalities for custom websites that i will add in the future.
For other hand i have another website project with a reference to Portal.Website project called Portal.Website.MyCustomWebsite.
This is the viewmodel MyModel.cs in the generic website part:
namespace Portal.Website
{
public class MyModel
{
[Required(ErrorMessage="The field Name is required.")]
[Display("MyPropertyOriginal")]
public virtual string Name{get;set;}
}
}
This is the controller and action in the generic website part:
namespace Portal.Website
{
public class MyController: Controller
{
[HttpPost]
public ActionResult MyAction(MyModel model)
{
if(Model.IsValid)
....
//My issue: Im getting the error message in english, not the overridden one.
}
}
}
This is the viewmodel that i created in the custom part:
namespace Portal.Website.MyCustomWebsite
{
public class MyModel: MyModel
{
[Required(ErrorMessage="My error message in other language.")]
[Display("MyPropertyOverriden")]
public override string Name{get;set;}
}
}
My problem:
I would like to override the ErrorMessage of the Required attribute. For this reason i created a new Model in my custom project. For other hand i would like to use the Controller/Action (MyController/MyAction) that is already defined in my common part.
Do you know if this is possible? Im only getting the issue with the Required attribute, but with the Display one its working perfect.
Thanks in advance.
Greets.
Jose.
You may want to check out this article that suggests two possible solutions :
http://www.codeproject.com/Articles/130586/Simplified-localization-for-DataAnnotations
I've found it was making more sense to re-create some DataAnnotation classes with my custom logic.
MVC3 comes with better support for I18N (internationalisation) than it's predecessors - you can pass the RequiredAttribute the type of your resource class and the resource key and the error message will be displayed in whichever language is most appropriate:
[Required(ErrorMessageResourceType = typeof(MyResources), ErrorMessageResourceName = "ResourceKey")]
public override string Name { get; set; }

Parent Controller Class in ASP.NET MVC 2

I've been working on a rather large web application that requires a specific id param in the url for every page visited (for example, /controller/action/id?AccountId=23235325). This id has to be verified every time someone visits a page.
I want to reduce code replication as much as possible, and was wondering if there is a way to use an init method or constructor in a controller that inherits the MVC controller, and then have that controller extended by the others.
I'm using ASP.NET MVC 2.
Yes this is possible using either a base controller class that all your controllers inherit or by creating a custom attribute that you decorate your controller with.
Base controller:
public class BaseController : Controller
{
protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
// verify logic here
}
}
Your controllers:
public class AccountController : BaseController
{
// the Initialize() function will get called for every request
// thus running the verify logic
}
Custom Authorization Attribute:
public class AuthorizeAccountNumberAttribute : AuthorizationAttribute
{
protected override AuthorizationResult IsAuthorized(System.Security.Principal.IPrincipal principal, AuthorizationContext authorizationContext)
{
// verify logic here
}
}
On your controller(s):
[AuthorizeAccountNumber]
public class AccountController : Controller
{
// the IsAuthorized() function in the AuthorizeAccountNumber will
// get called for every request and thus the verify logic
}
You can combine both approaches to have another custom base controller class which is decorated with the [AuthorizeAccountNumber] which your controllers that require verification inherit from.

ASP MVC Authorize all actions except a few

I have a controller and I would like to require Authorization for all actions by default except a couple. So in the example below all actions should require authentication except the Index. I don't want to decorate every action with the Authorize, I just want to override the default authorization in certain circumstances probably with a custom filter such as NotAuthorize.
[Authorize]
public class HomeController : BaseController
{
[NotAuthorize]
public ActionResult Index()
{
// This one wont
return View();
}
public ActionResult About()
{
// This action will require authorization
return View();
}
}
Ok, this is what I did. If there is a better way let me know.
public class NotAuthorizeAttribute : FilterAttribute
{
// Does nothing, just used for decoration
}
public class BaseController : Controller
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Check if this action has NotAuthorizeAttribute
object[] attributes = filterContext.ActionDescriptor.GetCustomAttributes(true);
if (attributes.Any(a => a is NotAuthorizeAttribute)) return;
// Must login
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.Result = new HttpUnauthorizedResult();
}
}
}
What about [AllowAnonymous] ??
MVC4 has a new attribute exactly meant for this [AllowAnonymous] (as pointed out by Enrico)
[AllowAnonymous]
public ActionResult Register()
Read all about it here:
http://blogs.msdn.com/b/rickandy/archive/2012/03/23/securing-your-asp-net-mvc-4-app-and-the-new-allowanonymous-attribute.aspx
Here's what I would do, similar to Craig's answer with a couple of changes:
1) Create an ordinary attribute deriving from System.Attribute (no need to derive from FilterAttribute since you aren't going to be using anything FilterAttribute provides).
Maybe create a class hierarchy of attributes so you can test based on the hierarchy, e.g.
Attribute
AuthorizationAttribute
AuthorizationNotRequiredAttribute
AuthorizationAdminUserRequiredAttribute
AuthorizationSuperUserRequiredAttribute
2) In your BaseController override the OnAuthorization method rather than the OnActionExecuting method:
protected override void OnAuthorization(AuthorizationContext filterContext)
{
var authorizationAttributes = filterContext.ActionDescriptor.GetCustomAttributes(true).OfType<AuthorizationAttribute>();
bool accountRequired = !authorizationAttributes.Any(aa => aa is AuthorizationNotRequiredAttribute);
I like the approach of being secure by default: even if you forget to put an attribute on the Action it will at least require a user to be logged in.
Use a custom filter as described in Securing your ASP.NET MVC 3 Application.
Mark the controller with [Authorize]
[Authorize]
public class YourController : ApiController
Mark actions you want public with :
[AllowAnonymous]
Little late to the party, but I ended up creating a Controller-level auth attribute and an Action-level auth attribute and just skipping over the Controller auth if the Action had its own Auth attribute. See code here:
https://gist.github.com/948822

Resources