Checking object access using ActionFilters - asp.net-mvc

I have been happily using a BaseController class with a custom ActionFilterAtribute, overriding OnActionExecuting(). I use this BaseController class for all my controllers as a convenient way of checking the id route value when it is passed in to the Controller.
This worked fine... I check for an id value, and if it exists I check to see the controller name which determines the type of object that the id belongs to.
But then... I started to get errors which are created when a PartialView is called in ControllerB and presenting the same id in the FilterContext that was used for the parent Controller/View. For example say I have a url of localhost/Project/Details/5 where Project is the controller and Details the action with an id=5. Controller "Notes" gets called due to a PartialView and my ActionFilter is triggered this time for Controller "Notes" and the original "Project" id. The user is permitted access to ProjectId=5 but not NoteId=5
I hope this makes sense - I am struggling to explain. I now need to either find some way of ignoring the PartialView, or handling it, or using a different approach altogether. My end goal is to be able to check object access in my application based on the id of an object. Is there a "best approach" for this kind of problem? Maybe I need to write a filter for every controller? Or possibly check the Action name as well so I know when to ignore "id"?
My BaseController...
public class AccessCheckAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
int id;
if (filterContext.RouteData.Values["id"] != null)
{
if (int.TryParse(filterContext.RouteData.Values["id"].ToString(), out id))
{
SPTdb db = new SPTdb();
switch (filterContext.RouteData.Values["Controller"].ToString().ToLower())
{
case "project":
project = db.Projects.Find(id);

I think this problem can be solved by using SQL query. You pass the table name and also with [key/value] of your object identity.

Related

MVC - How to instantiate, store and make a typed variable available throughout the application, once per page view

I am developing an MVC app to serve multiple domains - each is a branch of a larger company.
A LocalBranch class stores details such as phone, address, email, location coordinates etc.
I want to create a single instance of this class per http request and have it available throughout the application - from within controllers, views, some helper classes and other code.
Is there a recommended way of doing this?
Right now I have it as a property on a BaseController and use ViewBagto pass it to views. But I would prefer it strongly typed in Views if possible.
I don't want to put it in an application variable, because we need to serve different values to different domains.
I would rather avoid a session variable if possible because we might scale up to use multiple servers in the future, and I've heard this doesn't play well with sessions.
Please feel free to update tags / title if you think there is a clearer way of expressing what I'm after. Thank you.
The best way to maintain your state in a web application per request is simply use the HttpContext class.
You need to store your state(LocalBranch) as an Item in the HttpContext:
HttpContext.Current.Items.Add("LocalBranch", GetLocalBranch());
You can fetch the Item all across your application like this:
LocalBranch branch = HttpContext.Current.Items["LocalBranch"] as LocalBranch;
The Items property is simply a key value Dictionary. The value is an object. You will have to check for nulls and this is really similar to the Session object you know. The main difference is the scope. The HttpContext is a dot net object that has a lifetime of an http request.
Now using the HttpContext the way I've shown you is the simplest way to do it.
You can go two steps forward and use a framework called Unity and add a lifetime to your objects.
Unity does much more and the lifetime management is just one gem.
You can create a custom HttpContext lifetime that generates objects per request. Something like this.
And them all you need to do is:
1.Register you LocalBranch class with the HttpContext lifetime.
2.Add a static Current property which will use the Unity container and resolve the correct instance of LocalBranch.
3.Use it something like this: LocalBranch.Current
BTW, you can use Unity's dependency injection for injecting objects into controllers and other modules. That's a better practice then just using the static Current property.
You kind of have two questions here. The first is "How do I create a single instance of this class per HttpRequest?" The second is "How do I make this available to strongly typed views?"
The first has pretty much been answered by #amir-popovich to use dependency injection. However, FWIW I would probably use Ninject instead of Unity (just preference, really) and I would probably implement it differently. I would not use HttpContext, and simply build a service (which is instanciated using Ninject's OnePerHttpRequest Module, passing the domain as an argument to get the proper values).
Then, in order to add these LocalBranch values to your strongly typed View Model, you can first create a base view model which holds this type:
public class BaseViewModel
{
public LocalBranch Branch {get;set;}
}
Then, make all of your current view models inherit this base type
public MyViewModel : BaseViewModel
{
public string SomeValue {get;set;}
}
Then in your controller, it is easy enough to add these values from the service you created from the first step
public ActionResult SomeAction()
{
var vm = new MyViewModel();
vm.Branch = LocalBranchService.GetLocalBranchValues(); //Local Branch Service has been injected with Ninject
//do other stuff
return View(vm);
}
However, that gets pretty tedious to add that to each controller action, so you can instead create a Result Filter to add it for you:
public class LocalBranchResultFilter : FilterAttribute, IResultFilter
{
public void OnResultExecuting(ResultExecutingContext filterContext)
{
//This method gets invoked before the ActionResult is executed.
filterContext.Controller.ViewData.Model.Branch = LocalBranchService.GetLocalBranchValues(); //Local Branch Service has been injected with Ninject
}
}
Now, you can just decorate your Controller and/or Actions with the filter (you could even set it in the Global Filters if you want).
You can embed the child actions into your layout or a view. You can even cache its output so you don't keep re-querying the database.
controller
[ChildActionOnly]
[OutputCache(Duration=500, VaryByParam="*")]
public ActionResult Info()
{
var localBranch = db.GetLocalBranch();
return PartialView("_Info", localBranch);
}
_Info view
This bit will get inserted into your other views
#model LocalBranch
<span>#Model.address</span>
<span>#Model.phone</span>
Use in _Layout or other view
<p>lorem ipsum...</p>
#Html.Action("Info")

Can I access the ViewData from the HttpContext?

Im working with a project that sets variables such as the current user profile object in its authorize action filter, storing them in the ViewData for access by the following action method.
The action method then calls functionality from the repository. I'm trying to find a way to access the ViewData from the repository WITHOUT modifying the repository's method signature, and am hoping there is a way I can track back to it via the HttpContext.Current functionality which I can call from the repository.
Can anyone help with this? Just to be clear, the only code that I can modify is within the repository method :(
public class MyController : Controller {
[MyAuthorize] // ViewData items are set here
public void MyAction(int id)
{
new MyRepository().DoSomething(id); // Need to access ViewData items within this repository method and am unable to alter the method signature :(
}
}
I'm pretty sure the answer is "no".
When you review the ASP.NET MVC source code, ControllerBase instantiates a ViewData dictionary on first use. Then when you call View(), a new ViewResult is instantiated with the ControllerBase.ViewData dictionary as a parameter. It does not look like it gets applied to a public static property or class like HttpContext which you could access from inside your repository.
I think your best bet would be to use HttpContext.Items which is built for this type of communication. Though probably not as ideal as just modifying the repository to accept the extra data.

How can I create a binding in Ninject that changes based on the requested controller?

I have an ASP.NET MVC 3 app, and have run into the following situation. On my page, I have a side bar, which can contain related links specific to that page, i.e., determined by controller type. The links will be determined by the current page's content.
I have followed Phil Haack's blog post on rendering dynamic side bars such as this using Html.Action and a separate controller. I like the separation of concerns this approach gives me: my controllers don't know anything about the side bar at all, which is the way it should be.
I now want to inject an instance of a derived type of SideBar into my SideBarController, an action on which will be called to render the side bar itself. There is one derived type of SideBar per controller, and so I find myself wanting to write code similar to this:
kernel.Bind<SideBar>().ToMethod(_ => controllerName == "foo"
? new FooSideBar(kernel.Get<UrlHelper>())
: new BarSideBar(kernel.Get<UrlHelper>()));
but there's quite a lot that is wrong about that fragment of code, not least the fact that I can't get hold of the controller name in the lambda, and the question of what happens when a third type of controller comes along, and then a fourth, etc.
Note that I can't use WhenInjectedInto<T>(), as the SideBar instance will always be injected into the SideBarController.
For what it's worth, the instances of SideBar are currently being created via the Ninject Factory extension, so the relevant side bar bindings are as follows (I've hard-bound an implementation of SideBar just to prove the approach so far works):
kernel.Bind<ISideBarFactory>().ToFactory().InRequestScope();
kernel.Bind<SideBar>().To<FooSideBar>().InRequestScope();
Finally, I essentially have a one-to-one mapping between the derived types of SideBar and the controller types. It feels a little bit like there might be a bit of duplication here, but also it represents the relationship between the components, so I think I'm ok with it.
This all makes me think that my approach to this part of the problem is wrong, and so I would welcome suggestions on how to achieve a clean solution with Ninject.
I'll have a go at answering, but I'm not near a computer at the moment, and so it'll be a bit vague.
Fundamentally, you can pass another parameter to Html.Action, so if that parameter is either the Request or something gleaned from the Request (such as the controller name or Url parts) then you can use that to determine which sidebar to show. You may need to inject the factory into the sidebar controller, and use it to create the correct controller, or some other fiddling about, but once you know which sidebar is required, it becomes much easier.
I am not sure if this is possible using ninject but it is using ModelBinding like so:
public interface ISidebar
{
}
public class Sidebar1 : ISidebar
{
}
public class Sidebar2 : ISidebar
{
}
public class SidebarModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var controller = controllerContext.RouteData.Values["Controller"];
var action = controllerContext.RouteData.Values["Action"];
switch (controller.ToString())
{
case "Home":
return new Sidebar1();
default:
return new Sidebar2();
}
throw new NotImplementedException();
}
}
public class TestController : Controller
{
public TestController()
{
}
public string Index(ISidebar sidebar)
{
//Do something with it
return "OK";
}
}
//Add to the Application_Start
ModelBinders.Binders.Add(typeof(ISidebar), new SidebarModelBinder());
EDIT: Took me a while, but managed to get it working using Ninject.
Please read it at: http://blog.voltje.be/2012/08/22/creating-a-dynamic-sidebar-with-asp-net-mvc-ninject/
Suggestion:
Don't inject sidebar.
Instead inject [sidebar]ContentProvider.
Bind a default implementation in global asax (per request), then unbind and rebind if needed in the controller.

ASP.NET MVC: What part of controller action logic should we/should not move to attributes?

Let's assume that we have a controller with some action which could look like:
[HttpPost, Authorize]
public ActionResult Update(Guid itemId)
{
var item = repository.GetById(itemId);
if (item == null)
return NotFoundResult();
//Do something with item
return View();
}
We already have applied an Authorize attribute to be sure that only some specific users can perform our action.
We could also move the following block
var item = repository.GetById(itemId);
if (item == null)
return NotFoundResult();
to another attribute and apply it to our action.
There are another actions where we could extract some specific pieces of logic to attribute and apply them to our actions again and again.
Here comes my question: when should we do it and when we should not?
Is it actually a good thing to move such kind of logic to action method attributes?
I've came to this question when I was reviewing unit tests for some project. I was actually looking for code documentation and it was strange for me not seeing what should controller action do if, for example, item was not found. I've found this part in attributes unit tests, but is this actually attribute's responsibility?
I can understand Authorize attribute, it is actually another layer, but what about the logic that I've described above? Is it controller's action responsibility to work with it or ..?
Thanks in advance for any comments :)
If you want to run logic like this for a group of actions, you can override the OnActionExecuting method to perform the check.
Creating an attribute would be difficult, as you would also need to create a base controller and write some reflection code to inspect the action method to see if the attribute exists, then act on it.
protected override void OnActionExecuting(ActionExecutingContext ctx)
{
if(!ctx.ActionDescriptor.ActionName.StartsWith("Create"))
{
var item = repository.GetById(itemId);
if (item == null)
ctx.Result = NotFoundResult();
}
}
Attributes are used for various things in ASP.net MVC (for example filtering or exception handling). I would however not move logic like this to attributes as this is basic logic for the action you're executing. Moving this code to attributes will only make it harder to find what the action is actually doing.

ASP.NET MVC - Set ViewData for masterpage in base controller

I'm using a masterpage in my ASP.NET MVC project. This masterpage expects some ViewData to be present, which displays this on every page.
If I don't set this ViewData key in my controllers, I get an error that it can't find it. However, I don't want to set the ViewData in every controller (I don't want to say ViewData["foo"] = GetFoo(); in every controller).
So, I was thinking of setting this in a base controller, and have every controller inherit from this base controller. In the base controller default constructur, I set the ViewData. I found a similar approach here: http://www.asp.net/learn/MVC/tutorial-13-cs.aspx. So far so good, this works... but the problem is that this data comes from a database somewhere.
Now when I want to Unit Test my controllers, the ones that inherit from the base controller call its default constructor. In the default constructor, I initialize my repository class to get this data from the database. Result: my unit tests fail, since it can't access the data (and I certainly don't want them to access this data).
I also don't want to pass the correct Repository (or DataContext, whatever you name it) class to every controller which in turn pass it to the default controller, which I could then mock with my unit tests. The controllers in turn rely on other repository classes, and I would end up passing multiple parameters to the constructor. Too much work for my feeling, or am I wrong? Is there another solution?
I've tried using StructureMap but in the end I didn't feel like that is going to fix my problem, since every controller will still have to call the base constructor which will initialize the repository class, so I can't mock it.
This is a similar question but I find no satisfactory answer was given. Can I solve this in a neat way, maybe using StructureMap as a solution? Or should I jsut suck it and pass a Repository to every controller and pass it again to the base controller? Again, It feels like so much work for something so simple. Thanks!
I see two options:
First:
Set the ViewData for MasterPage in YourBaseController.OnActionExecuting() or YourBaseController.OnActionExecuted():
public class YourBaseController : Controller
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Optional: Work only for GET request
if (filterContext.RequestContext.HttpContext.Request.RequestType != "GET")
return;
// Optional: Do not work with AjaxRequests
if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest())
return;
...
filterContext.Controller.ViewData["foo"] = ...
}
}
Second:
Or create custom filter:
public class DataForMasterPageAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Optional: Work only for GET request
if (filterContext.RequestContext.HttpContext.Request.RequestType != "GET")
return;
// Optional: Do not work with AjaxRequests
if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest())
return;
...
filterContext.Controller.ViewData["foo"] = ...
}
}
and then apply to your controllers:
[DataForMasterPage]
public class YourController : YourBaseController
{
...
}
I think the second solution is exactly for your case.

Resources