Sending a parameter to the controller in ASP MVC 2 - asp.net-mvc

I am writing an area for administering several subsites, almost all of the functionality will be the same across each site (add/edit/remove pages etc) and my repository on instantiation takes the SiteIdentity so all the data access methods are agnostic in relation to this. The problem I have at the moment is trying to make my action methods also agnostic.
The URL pattern I want to use is along the lines of:
"ExternalSite/{identity}/{controller}/{action}/{id}"
A naive approach is to have each action take the identity parameter, but this means having to pass this in to my repository on each action as well as include it in the ViewData for a couple of UI elements. I'd much rather have this something that happens once in the controller, such as in its constructor.
What is the best way to do this? Currently the best I can come up with is trying to find and cast identity from the RouteData dictionary but part of me feels like there should be a more elegant solution.

It sounds like you want to use OnActionExecuting or a Custom ModelBinder to do that logic each time you have a specific parameter name (also known as a RouteData dictionary key).
Creating a custom modelbinder in ASP.NET MVC
Creating an OnActionExecuting method in ASP.NET MVC, Doing Serverside tracking in ASP.NET MVC

You have access to your route values in Request.RequestContext.RouteData, so you can make base controller and public property SiteIdentity, in such case you can access it from all actions in all inherited controllers.

Related

Working with helpers in the dotnetcore DI world

I like to have static helper classes in my apps for common plumbing stuff, like checking roles, or Razor Html helpers and stuff... but how do you do this in the DI world?
Like lets say I want an extension helper to check if a user is an admin
public static async Task<bool> IsAdmin(this ApplicationUser user)
...
if(user.IsAdmin()){...}
So pre-core I could just ignore DI and create a UserManager all inside IsAdmin do whatever I need to do. But now is there no way to get the UserManager in these helpers to just use? The only way I can see is to inject it into the Controller, then pass along into the method (which I find ugly). Then there's the issue of trying to do user.IsAdmin() in the Razor view, would I need to add the UserManager to the ViewData collection to get it into the view markup?
Am I just missing something here?
Thanks,
Steve
First of all if you just asked how to use static class with di, i would say that your question is duplicate of How to use DI inside a static Method in Asp.net Core rc1
But as i see, you have some other question?
But now is there no way to get the UserManager in these helpers to
just use?
Yes there is a way : Service Locator pattern. But it is an anti pattern (see this article) . As far as possible you should avoid to use this pattern. Also see discussion in github.
The only way I can see is to inject it into the Controller, then pass
along into the method (which I find ugly)
I think this way is better than you want. I would prefer this.
Then there's the issue of trying to do user.IsAdmin() in the Razor
view, would I need to add the UserManager to the ViewData collection
to get it into the view markup?
In Aspnet core you can inject a dependency into a view, so you don't need to use ViewData. Simply you can inject UserManager into your view and then pass it as parameter to method. Take a look at official docs
Well you are talking about a cross-cutting concern here and one way how I've seen cross cutting concerns solved in ASP.NET Core MVC is with attributes (like [Authorize] for example). Which I think is an elegant solution.
So, if I understand your question correctly I think you can solve this with an Action Filter. Damien Bod described a few days ago how to use ActionFilters: https://damienbod.com/2016/09/09/asp-net-core-action-arguments-validation-using-an-actionfilter/.
So in short, you inherit from ActionFilterAttribute and make your own curstom filter called MyCustomFilter or whatever. Have this MyCustomFilter request UserManager in its constructor via DI. Then above any action method in a controller you say:
ServiceFilter[typeof(MyCustomFilter)]
And in MyCustomFilter you ofcourse have logic to check if User is IsAdmin and then take action accordingly.
Now, I've always used Microsoft's Unity to handle cross cutting concerns via interception (you can read more about that here: https://dannyvanderkraan.wordpress.com/2015/09/30/real-world-example-of-adding-auditing-with-dependency-injections-interception/. But last time I checked there is no Unity container for asp.net core yet. But this guy has a great article about porting it to core: https://dzimchuk.net/post/bring-your-own-di-container-to-aspnet-5-unity. I would really like my Interception back! Very elegant solution to cross cutting concerns. They are working on it though: https://github.com/unitycontainer/unity/issues/66. Fingers crossed...

How does asp.net mvc convert post data to a model?

I am extracting some code from a project outside of the MVC-area to a HttpHandler while reusing as much code as possible.
To make things easy I would like to convert the posted data to the same Model that was already used in the Controller.
So, now I am looking for the logic MVC is using under the covers to magically convert posted data to a Model, but after several decompile attempts I am unable to find this.
Could anyone point me in the right direction?
Thanks!
SaphuA: Basically, I want to input the HttpContext and the type of the target Model and have the helper method do the rest of the mapping.
As Eric says, just extent the DefaultModelBinder.cs
The default model binder is here
The model binder is called from the controller action invoker
The overall flow is nicely outlined here so just scroll to check where the controller action invoker is called.
If you want to spin up some unit tests around your implementation of the binder, check out the MVC source tests
Using the github mirror because codeplex does not support line number linking yet :-)
HTH
I think this article explains it pretty well.
In a nutshell there is a DefaultModelBinder class that handles the conversion of POST data into a model object. You can even replace that model binder with your own if you like by extending it or implementing IModelBinder then registering your model binder with MVC in your Application_Start method.

ActionFilter or RenderAction in ASP.NET MVC?

I'm working on an ASP.NET MVC controller with several action methods, all of which need the same bit of data. This data requires a lookup that can only be done with the route values (so, I can't do the lookup in the constructor). I'm sure this has been discussed at length, but I've yet to find a satisfactory recommendation.
What's the best way to get this data without repeating myself in each action method? I'm working through:
Create an Action Filter, this seems like the best bet, but where do I store the object, in the action parameters? Is it appropriate to create a ViewModel object in an action filter and pass it directly to the action methods, for them to fill out the rest of the ViewModel object?
Create a child action (Html.RenderAction) to render this data, but this requires a second set of lookups since the child action requires another full cycle of instantiating the controller.
Helper method/property called in each action method.
Thought or opinions on a best approach here?
A filter is probably your best bet and you can store the object in ViewData.
Another option (not a better one) is to create your own controller base class that overrides the ExecuteCore method and does the lookup there.
I did this for logging since I want to log each page view and I didn't want to have to add a filter to each and every controller I made. In mvc 3 there will be a way to declare global filters which can fix this as well.
Ok, so given what you've told me, I would suggest using a custom ModelBinder.
It's the best fit for the situation. I would argue that using a filter is the wrong approach because a filter's job isn't to bind data - that's a job for a ModelBinder.
HTHs,
Charles
EDIT: I've just been thinking about it and I'm a little bit torn if you should use a model binder or not.
The general rule of thumb I just came up with is that if you need the ProjectDetails inside the action itself, use a ModelBinder but if you don't need the ProjectDetails inside the action, use an ActionFilter to just add it to your model / viewdata.
Maybe someone else could throw their 2c in.
You could override the OnActionExecuting() method in your controller and get the data there.

Mvc viewpages and custom data for all pages

I need some data to be available on all the viewpages inside the website. The data comes from an parameter supplied to all the routes, i want to get that param and get the according data for it and make it available for all the views (including the master pages).
It would be nice if it could be done in one place.
What do i need to do to get that functionality, can it even be done?
Greetz,
Richard.
The easiest (may not be the best) would be to write a base Controller class that
1) handles one of the following events to do the job:
OnActionExecuted
OnActionExecuting
OnResultExecuted
OnResultExecuting
2) Sets the data you want to have available in ViewData.
3) Use the ViewData from your views.
4) All your Controllers must inherit from your custom base Controller.
This might not be the nicest of all approaches as I usually try to avoid inheritance like the plague, but it will work. Other options.
1) Implement it in an ActionFilter and make sure add the attribute to all ActionMethods.
2) Use something like MVC Turbine to define ActionFilter's that trigger for all ActionMethods in you ASP.NET MVC Application.

asp.net mvc newbie question

I recently started to look into asp.net mvc. Here is my issue.
Say every page on an application needs a variable set by the user, e.g. a date. If the user starts from url I provide, it is all good as I ask for that date and save it for the session. How can I redirect the user to the first page if they save the some other url (to a different controller and action).
In other words, I guess I am looking for something like [Authorize] attribute but on an application level.
Thanks for any help.
I would probably create a base controller that all of my controllers derive from. In the base controller I'd override the OnActionExecuting method to check the session for the required variable. If the variable isn't present, I would set the ActionExecutingContext Result property to a RedirectToRouteResult to the appropriate controller/action to set the variable.
Another alternative is to create a custom FilterAttribute that you decorate the appropriate controllers/actions with that does basically the same thing. I would only do this if the filter was to apply only to certain controllers or actions and not all as you describe in your question.

Resources