Checking permissions in controller and in view - c#

I want to create permissions on some actions like creating or updating information (but not for displaying).
For this I'm adding attributes before all the necessary methods e.g.
[Permissions(Permissions.Admin)]
public ActionResult Create()
{
//...
}
Besides I wouldn't like to leave the links active on the index page. So I have to add some checks inside the views.
#if (checking...)
{
#Html.ActionLink("Create New", "Create")
}
The more checks I add, the more boring, and the more things I have to keep in my mind.
How to do it right?

Your could create a HtmlHelper for this.
Something like #Html.ActionLinkUsingPermissions("Create New", "Create",Permissions.Admin)
The HtmlHelper would decide whether or not to display the link depending on the current users permissions.

You can create a read-only version of views and letting the controller decides which version it should return (based on your permission).
Shared UI can be then externalized in partial views.
It's recommended to keep your view free from business logic as much as possible.

You could also create two different view models. One for read/write and one for just read. Then using the View Templates feature you can have one view automatically pick the right template to show using this line:
#Html.DisplayForModel()

Related

ASP.NET Core 6 MVC : how to implement layout logic?

I need to implement some logic to all views globally. I have a _layout view. How can I run logic (via a controller) for the shared layout?
I'm looking to implement the following logic:
Check if the user can perform an action of controller (if not, return to the previous page)
Check if the user account is active (after login, a user account can be disabled) (if not, log out and return to the login page)
Update the user log (update a table to save the IP and the time of the last user request to the server)
Hide controls for the user (prevent the user from seeing a control or changing any data)
In ASP.NET Web Forms 4, I created a class called Users, and this class was called inside of the master page's .cs code behind.
How can I accomplish something similar in ASP.NET Core MVC?
Welcome to MVC! The transition from ASP.NET Web Forms can be a bit frustrating as the programming models are so different. But once you get past the conceptual hurdles, you'll find a lot of tasks are much cleaner and easier to maintain.
I struggled with a similar challenge myself when migrating from ASP.NET Web Forms to, first, the ASP.NET MVC Framework and, later, ASP.NET Core MVC. There are a few common strategies for addressing problems like this, which I'll summarize below.
Custom Filter
For most of what you're talking about, the textbook solution is to create an custom filter, which is a piece of code that can be applied to multiple actions or controllers, and will be called at various stages of execution (e.g., before or after an action). You can also wrap filters in attributes, which makes it easy to apply them as an annotation to any action or controller. You can also register filters globally so they apply to all actions on all controllers. Filters are especially useful when you want to validate a request based on the current request—including potentially the current user.
View Component
Another approach, introduced with ASP.NET Core, is a View Component, which is a bit like a partial view, except that it has code behind it that operates a lot like a controller. This is useful if you need reusable components that share their own model on every page of your site, as it allows that to be constructed independently of any one action, while being shared across multiple views.
The textbook example of a view component is a login control, which receives a view model with information about the currently authenticated user, and uses that to conditionally display e.g., a sign-in or sign-out link.
Aside: I often use view components for centralizing my navigation, so I don't need to relay the data for the navigation down to every single view model. That's beyond what you're asking about here, but may help further conceptualize when a view component is useful.
Business Object
From a code organization perspective, you can still maintain much of the logic inside a custom User class, or even as extension methods off of e.g. the ASP.NET Core ClaimsPrincipal class (which is returned from the HttpContext.User property). Determining what the current state of the user is for the purposes of e.g. authorization makes sense in a separate class; handling the ASP.NET Core response to that (such as redirection to a previous page) belongs in the filter or view component.
And, of course, you can also relay (properties from) this class to your view via your action's or view component's view model if you need to rely upon them to customize the user interface.
Conclusion
There are obviously a number of approaches here—as well as some I haven't mentioned, such as implementing a base controller—but this should help get you started in thinking about how to centralize site-wide business logic without needing to repeat it in every action of every controller, or polluting your _Layout.cshtml with logic that would normally reside in a controller.
First I wouldn't recommend a Controller for the _layout page as this is not how the MVC architecture works.
You could create a view using the layout page, like UserView.cshtml.
Then you can create a UserController.cs and your actions in it.
I hope I could help.
Check if the user can perform an action of controller (if not, return
to the previous page) Check if the user account is active (after
login, a user account can be disabled) (if not, log out and return to
the login page) Hide controls for the user (prevent the user from
seeing a control or changing any data)
You can use #if(User.IsInRole("Admin")) to determine the identity of the user in the layout, and then if the user can't perform an action of controller, use class="disabled" to disable the action.
If is the user you want to show a control , you can contains this control after it is judged to be true. So hide controls for the user.
For example, if it is admin, you can add a role or view the current role.
Demo as below:
_layout:
#inject SignInManager<AppUser> SignInManager
#inject UserManager<AppUser> UserManager
#using Microsoft.AspNetCore.Identity
#using Identity.Models
<!DOCTYPE html>
<style>
.disabled {
pointer-events: none;
color: grey;
text-decoration: none;
}
</style>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>#ViewBag.Title</title>
<link href="~/twitter-bootstrap/css/bootstrap.css" rel="stylesheet" />
<p>Login</p>
#if(User.IsInRole("Admin"))
{
<p>Admin</p>
<p>Role</p>
<p>Claims</p>
<li >
<a asp-area="" asp-controller="Role" asp-action="Create" >Hello #UserManager.GetUserName(User)! Create a Role</a>
</li>
}
else
{
<p><a class="disabled"href="/Admin" target="_blank" >Admin</a></p>
<p><a class="disabled" href="/Role" target="_blank" >Role</a></p>
<li >
<a asp-area="" asp-controller="Role" class="disabled"asp-action="Create" >Hello #UserManager.GetUserName(User)! Create a Role</a>
</li>
}
</head>
<body class="m-1 p-1">
#RenderBody()
</body>
</html>
result:
Update the user log (update a table to save the IP and the time of the
last user request to the server)
Use javascript to get the URL of the current request and the time of the current request, and then use ajax to call the action of the back-end controller, and record the time of the obtained request into the database.

How to use one razor view for two action methods but with slightly different contents in some portions only? [duplicate]

I have set up a menu-controller to drive the top menu links based on which other controller is being used. Each other controller has a separate nested master page for each of its views.
so, i have a menu-controller with several methods that return viewresults, one per each controller, or "section" of the site. So currently each of these methods has its own view to render the menu. but each view to render the menu is the same code, the only thing that changes is the logic in the controller methods based on which links to render.
is there any way to have all of these controller actions target the same view? since the view is the same for all?
thanks
Yes, that is a common practice.
return View("Menu");
Create a strongly typed view that takes a container specifying your menu content. Pass this as a parameter on your return statement.
var thisMenu = CreateMenuForThisRequest();
return View ("Menu", thisMenu);
it depends on what version of ASP MVC you're using; with MVC 2, you can create an ascx control and use RenderAction
in your view you'll put something like
Html.RenderAction("Menu", "Navigation");
and have a navigation controller with a Menu actionresult
public class NavigationController : Controller
{
[ChildActionOnly]
public ActionResult Menu()
{
Menu model;//your menu
return PartialView("YourMenuAscxControlName", model);
}
}
I think if you're using MVC 1, the MVC Future project has the RenderAction but i'm not sure.
For my menu I use the RenderAction method
I'm also using the ActionOutputCacheAttribute from Steve Sanderson
http://blog.stevensanderson.com/2008/10/15/partial-output-caching-in-aspnet-mvc/
you will greatly increase your site loading time with this caching

Sending data to header of _Layout.cshtml regardless of what page the user is on

I have a notifications badge in the header of my _Layout.cshtml file. This is so I can constantly show the user their notifications regardless of what page they are on.
Notifications <span class="badge">#ViewData["NotificationCount"]</span><br>
However, I am wondering if there are better ways to do this. It seems like I need to do the following at the top of every single one of my views:
#{
ViewData["ReminderCount"] = Model.Notifications.Count();
}
This also means I need to have a list of notifications as part of every single page ViewModel throughout my application.
Am I missing something here? I feel like there has to be a better way to handle things like this in ASP.NET MVC.
The ideal thing would be to load the user's notifications one time as soon as they login and go to their dashboard and then continue to show that number across all pages unless they check the notifications, then the number clears.
What you can do is create an action and against that action create a partial view and in the _Layout.cshtml call it, this will save you from code duplication and ViewData :
You action code will look like:
public ActionResult Notifications()
{
var model = Model.Notifications.Count();
return View("_NotificationsPartial",model);
}
your partial view _NotificationsPartial.cshtml would be like:
#model System.Int32
<a href="#">Notifications
<span class="badge">#Model</span>
Now you just need to call it in your _Layout.csthml:
#Html.Action("Notifications","SomeController")

Is it bad practice for views to create partial(child) views?

I'm creating a little view framework. I'm not trying to stick to strict MVC adherence, but I am definitely trying to not get in the way of MVC practices.
Anyway, one of my questions is this: Is it bad for a view to create it's own child views?
For instance, in pseudo-ish C# code:
/*BlogEntryView*/
<h1>my blog entry...</h1>
<div class="Comments">
{# //code
foreach(var comment in Comments){
Write(new CommentView(comment));
}
#}
</div>
Is this bad practice for an MVC style? Would the proper thing to do be to provide a "placeholder" in BlogEntryView where the model populates it with CommentViews?
(also, please do not tag asp.net-mvc, this is similar, but in no way uses ASP.Net MVC technologies)
For comparison, the opposite of this, would be adding views with some placeholder in the model code:
/*BlogEntryView*/
<h1>my blog entry...</h1>
<div class="Comments">
{# SomePlaceholder #}
</div>
/*In the model code for BlogEntry*/
//v is a BlogEntryView
foreach(var comment in Comments){
v.PlaceHolder.Add(new CommentView(comment));
}
Both ASP.NET MVC and Ruby on Rails facilitate the approach I think your referring to through the use of partial views.
Using your example would typically result in a view that called a partial for comment record. In ASP.NET MVC C# this would look like the following: -
<h1>my blog entry...</h1>
<div class="Comments">
<% foreach (var comment in Model.Comments) { %>
<% Html.RenderPartial("Comment", comment); %>
<% } %>
</div>
Following current MVC philosophies and design principals this sort of decomposition into small "atomic" portions of view code is actively encouraged in many circles. However, there is always a balance to be sought between this decomposition and the maintainability.
No. This is actually how the ASP.NET MVC Templates features work in MVC. However, a potential pitfall in ASP.NET MVC is a slight performance cost to searching the file structure for the views. This can be avoided by specifying the full view path explicitly.
http://vishalswami.blogspot.com/2007/11/design-patterns-in-mvc_30.html discusses MVC architecture. The Gang of Four also advises that one of MVC's greatest advantages is that it facilitates a Composite UI (which is what you are describing).
In traditional MVC, there's one view to each controller and model, which is call the "MVC Triad". I think what you what is the view's template to be able to embed other templates for re-usability (think partials).
One piece of tech that gets this correct with mustache. It uses a view model, coupled with a template. The template can request other partials to reuse chunks of other templates.
The problem with many web MVC frameworks is that they treat the view as a template, which is the wrong way to view it (no pun intended). Once you have a class representing the view, this all becomes much easier.
Personally, I think the specific example you posted is bad form, because a template should never have that sort of access to objects and instantiating them like that. Templates should get their data from outside sources (the view model), which cane make those instantiations cleaner.
Is it bad for a view to create it's own child views? My answer is "NO". In face creating partial views gives you more power to change the UI contents in a modular way.
There are always multiple ways of achieving results. I personally think ascx files are a good clean way of creating reusable modules which can inherit from customized user controls. Its modularized approach keeps things very organized for me.
Just my 2 cents...
I understand that there are communities both pro and against views rendering child views.
Personally I consider the use of RenderPartial to still be a view concern. I don't have an issue with view depending on another view, providing it assumes the same model offered by the controller action's model.
RenderAction on the other hand is less of a view concern because it winds up invoking a controller action, which then renders a view. It's an entire request lifecycle unto itself. However, it has a lot of benefits, particularly for cross-cutting concerns such as site navigation, user account state, ads, or other page features that are completely independent of the page's primary goal.

Passing ViewData to ViewPage in ASP.NET MVC

I'm trying to build a custom control in ASP.NET MVC. The thing that I want to avoid is mixing the view (html) and the code (C#) in these controls.
After hours of googling and struggling with this problem I've finally thought of creating helper methods that use the ASP view Engine thought the ViewPage class.
I'm able to create an instance of the class and even load an ascx template using LoadControl and LoadTemplate methods but the problem is that when I pass a ViewData object to an instance of ViewPage class I cannot see the ViewData in the ViewPage and I'm getting this error:
The ViewUserControl '~/Components/Controls/EditableLabel/Views/EditableLabel.ascx' cannot find an IViewDataContainer. The ViewUserControl must be inside a ViewPage, ViewMasterPage, or another ViewUserControl.
Generally the final effect that I want to achieve is loading controls with some parameters, these parameters should be readable in the template (ascx).
example:
<% Html.EditableLabel(Writer, "memberName", "Name", "Antonio", "/identity/updateProperty", "memberName"); %>
Is there a way to pass the ViewData to the ViewPage instance that I create?
Is there a smarter way to create custom controls/widgets in ASP.NET MVC?
Thanks in advance
You can pass VD to the VP from controller via "return View(...)" or via "ViewData[name] = value".
It's better to use ascx controls which are inherited from ViewUserControl in order to create a custom control. In this way you easily can pass data to it:
<% Html.RenderPartial("FooControl", new FooViewData(){ Name="aa", Type="awesome", Id = 2}); %>
<% Html.RenderPartial("FooControl", new FooViewData(){ Name="aa", Type="awesome", Id = 2}, ViewData); %>
And all necessary parameters you can define in your FooViewData class.
If you really want to write an extension to the HtmlHelper which will load some ascx controls you should look at a Repeater class in MvcFutures (sources of MVC are here). But I'm sure that in a common case you'll not need this.
I am tracking with zihotki on this one. With #2 I will add a caveat, however. In the default implementation, which uses neutered ASPX and ASCX as views, creating MVC controls (use the template to ensure your dog is neutered) is the best option.
Now for the caveat. ASPX and ASCX do not have to be the views. If you look at Phil Haack's blog, you will see some other view engines mentioned, like the Spark View Engine (ASP.NET MVC Northwind Demo Using the Spark View Engine). In fact, Phil has set up a sample of using alternative view engines in this entry: Rendering A Single View Using Multiple ViewEngines.
I am not suggesting that you move to other view engines, only that there is an option, if you find one that better fits your problem domain. Sometimes it is better to stick with the out of the box implementation. In this case, keep things simple with ASCX controls.
By the way, if you are seriously getting into MVC as your UI story, you should look at the codeplex projects for MVC. The most important here is probably MVC.Contrib, but there are a few other gems in the list.

Categories