What is the best practices to handle multiple form operation under same action in ASP.NET MVC4? - asp.net-mvc-4

I have the following scenario in my ASP.NET MVC4 project using Razor Engine:
I have a view with at least 4 different forms.
I would like to handle all form POST under same action mapping.
Actually, the 4 forms post to different Route Mapping, as follow:
POST: /User/FilterRolesInUse/15
POST: /User/RemoveRoles/15
POST: /User/FilterRolesNotInUse/15
POST: /User/AddRoles/15
I would like to know if is it possible to handle all 4 form under the same Route Mapping, something where all form post to /User/Roles/15 and then the controller can distinguish which form was submitted. The concept is something like:
class UserController : Controller {
//
// POST: /User/Roles/
public ActionResult Roles(int? id, object form) {
return DelegateToFormLogic(id, form);
}
}
I just want to know if is it possible because I really want to keep URL consistent.
Any advice or suggestion are welcome.

I do not see any advantage to having a single action that performs multiple functions. In fact it will be confusing to anyone that has to support the code. I would get away from submitting forms and use Ajax methods in your web client (using JQuery ajax) to get the data you need for this view and for update/insert/delete actions. This way you do not have to post back the whole page to perform actions that will probably take place on just portions of the view which will result in a better performing page and a better user experience. Change your controller to a ASP.NET Web API controller and make those methods a REST API that uses consistent URL naming convention and use HTTP verbs to indicate the type of action being performed. You will end up 3 methods that serve the 4 you have now and it could look something like this (they correspond to the same order listed in the question).
GET: /api/Role/15?InUse=True
DELETE: /api/Role/15
GET: /api/Role/15?InUse=False
POST: /api/Role
Your controller would look like this.
class RoleController : ApiController {
public List<Role> Get(int id, boolean InUse) { ... }
public void Delete(int id) { ... }
public void Post(List<Role> roles) { ... }
}
This maintains a clear separation of concerns while also keeping a consistent and understandable URL convention.

Related

ASP.NET Core Web API split controller based on aggregates

In my API project I have currently 1 controller with the following syntax:
Abstract base controller
[Produces("application/json")]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiController]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status406NotAcceptable)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public abstract class BaseApiController : ControllerBase
{
// ....
}
TestApiController
public class TestApiController : BaseApiController
{
// Parent action methods
[HttpGet]
[HttpPost]
...
// Child action methods
[HttpGet("{test:guid}/child")]
[HttpGet("{test:guid}/child/{id:guid}")]
...
}
This gives us the following urls:
http://localhost/api/v1.0/test
http://localhost/api/v1.0/test/1
http://localhost/api/v1.0/test/1/child
http://localhost/api/v1.0/test/1/child/1
Question
How can I split the controller into multiple files without braking the urls but to have a cleaner overview.
So a controller for all the test actions and another controller for all the child actions.
I'm not talking from an abundance of experience with controllers here (I have done some), but I think you'll find that the controller is the controller - in that there's no easy way to have a controller-route that isn't 1:1.
But, if you want to make controllers more manageable you could just keep them as lean as possible and put the bulk of code into classes that the controller calls.
Alternatively, rather than trying to split/decompose the controller and not the route, rework the overall API design/structure so that you have more controllers and routes. The Interface Segregation Principle comes to mind here (yes it's born out of OO but the principle is still applicable).
At the end of the day, you have calls coming into to API's defined by their routes, and off to the controllers - it doesn't really matter how that all looks (is structured) as long as the API is sufficiently sensible, intuitive and fit for purpose.
After some more searching on the internet I came across the following interesting Nuget package:
Nuget: Ardalis.ApiEndpoints.
Source code and examples about this can be found here:
Github: Ardalis.ApiEndpoints.
This allows us to separate controller into folders and for each action a separated class to give use exactly what we want.

How to properly do resource-based authorization in ASP.NET Core 2.2?

I have an ASP.NET Core 2.2 application where I am displaying some documents. Most documents are public, so anonymous access is fine. However, some documents are private (i.e. they require authentication/authorization), and in the future some documents might also require a valid subscription. All documents are retrieved using the same actions, so we only know the required permissions after the documents have been loaded. We also load some resources as static files (IApplicationBuilder.UseStaticFiles), but I guess that shouldn't really be an issue as StaticFileOptions.OnPrepareResponse can be used for custom authorization code.
The logic for who gets access to private documents is currently really simple. And at the moment, we only display documents, we don't allow any other kind of operation on them (editing, deletion etc.). To me, this sounds like a pretty standard case of resource-based authorization.
Anyway, I have found this article and from what I've understood, I need to define a policy (identified by a magic string - what's up with that?!) as well as a requirement and an AuthorizationHandler<MyRequirement, MyResource> which will perform the actual authorization logic. Then, inside my controller action, I will need to call IAuthorizationService.AuthorizeAsync and pass in the user, the resource and the policy name (the magic string) and, based on the result from that method, allow or deny access. That seems more than convoluted for what I'm trying to accomplish. It would probably be easier if I simply defined my own kind of "authorization service" and simply dropped the whole policy and requirement stuff. I also think it's less than ideal that I would have to replicate the if-else logic in all affected controller actions.
Surely I'm not the only one with this issue. Is there something I've missed?
If there are indeed good reasons for using policies and requirements, how would you name them in a case like this? I'm really feeling a little lost.
Maybe it would make sense to use the type of document (public, private, subscribers-only) as the policy name?
In the end, we didn't want to deal with this stuff and just wrote our own AuthorizationService, which is injected into the controller like any other service.
It loads the required permissions for all documents the first time it is used and caches them.
Our controller methods then look something like this:
[HttpGet("[action]")]
public async Task<Document> GetDocument(string documentId)
{
if (_authorizationService.MayAccess(User, documentId))
{
return _documentRepository.GetDocument(documentId);
}
else
{
Response.StatusCode = StatusCodes.Status403Forbidden;
return null;
}
}
I recommend the last approach explained in this article - https://www.red-gate.com/simple-talk/dotnet/c-programming/policy-based-authorization-in-asp-net-core-a-deep-dive/
Allows you to keep you controller clean, by just applying annotation with the name of the policy. In the handler you must implement the logic checking if person can access the resource - it can be based for example on checking a property ownerId in a resource(for example in database table column) or a member of a certain group in AD, or anything else.
EDIT:
Using Requirements and RequirementsHandlers - I have done something similiar.
I don't know how should your logic exactly work, so I am just going to assume some.
lets say you have a get endpoint: documents/documentId
You want to apply logic which will make this document accessible only to the document owner. Obviously, you need somewhere to store who is the owner of the document, so lets keep that in property of a document entity.
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, IsDocumentOwner requirement, DocumentRepository documentRepository)
{
if (context.Resource is AuthorizationFilterContext ctx)
{
var documentId = ctx.RouteData.Values["documentId"]?.ToString();
//here load document from repo and check if the property ownerId is equal to current user id
var userId = context.User.Claims.FirstOrDefault(x => x.ToString().Contains(oid))?.Value;
//if yes, make the request pass to the body of a controller with the attribute
context.Succeed(requirement);
}
}
implement IsDocumentOwner:
public class IsDocumentOwner : IAuthorizationRequirement
{
}
in your Startup.cs add:
services.AddAuthorization(options =>
{
options.AddPolicy(
nameof(IsDocumentOwner),
policyBuilder => policyBuilder.AddRequirements(
new IsDocumentOwner()));
});
then, last step, apply attribute on your controller method
[Authorize(Policy = "IsDocumentOwner")]
[HttpGet("{documentId}")]
public YourDocumentObjectResultClass GetDocument([FromRoute]string documentId)
{
//stuff you do when current user is owner of the document, probably just display the doc
}
To your IsDocumentOwner handler you can inject any service by constructor(visualised by repository above), for example, to check if the user is a member of a group on azure ad

ValidateInput(false) vs AllowHtml

I have a form that is used to create a memo, to do that I am using a rich text editor to provide some styling, this creates html tags in order to apply style. When I post that text, the mvc throws an error to prevent potentially dangerous scripts, so I have to specifically allow it.
I have found 2 ways of doing this, one is to decorate the controller method with [ValidateInput(false)] and the other is to decorate the ViewModel attribute with [AllowHtml]. To me, [AllowHtml] looks much nicer, but I have only found that approach used 1 time and the [ValidateInput(false)] seems to be the preferred way.
Which method should I use and what are the differences between the two?
ValidateInput and AllowHTML are directly connected with XSS security issues.
So let us first try to understand XSS.
XSS (cross-site scripting) is a security attack where the attacker injects malicious code while doing data entry. Now the good news is that XSS is by default prevented in MVC. So if any one tries to post JavaScript or HTML code he lands with the below error.
But in real time there are scenarios where HTML has to be allowed, like HTML editors. So for those kind of scenarios you can decorate your action with the below attribute.
[ValidateInput(false)]
public ActionResult PostProduct(Product obj)
{
return View(obj);
}
But wait, there is a problem here. The problem is we have allowed HTML on the complete action which can be dangerous. So if we can have more granular control on the field or property level that would really create a neat, tidy and professional solution.
That’s where AllowHTML is useful. You can see in the below code I have decorated “AllowHTML” on the product class property level.
public class Product
{
public string ProductName { get; set; }
[AllowHtml]
public string ProductDescription { get; set; }
}
So summarizing “ValidateInput” allows scripts and HTML to be posted on action level while “AllowHTML” is on a more granular level.
I would recommend to use “AllowHTML” more until you are very sure that the whole action needs to be naked.
I would recommend you to read the blog post Preventing XSS Attacks in ASP.NET MVC using ValidateInput and AllowHTML which demonstrates step by step about the importance of these two attributes with an example.
if use Bind Include best way is AllowHtml otherwise
you can use ValidateInput(false) to disable all Validaton in controll

Searching with WebAPI

I have made a web API class for my Customer model. I have the standard methods (GET, POST, PUT, DELETE). The problem is, I want to implement another GET method which is a search. Something like this:
[HttpGet]
public IEnumerable<Customer> Search(string id)
{
var customers = customerRepository.Search(id);
return customers;
}
The search method performs a search based on the account number of my customers, using the .Contains() method.
The problem is, when I navigate to: mySite.com/api/Customers/Search/123 I get a 404. What am I doing wrong here?
While Darin's answers are always of top quality this question would actually benefit from an answer that explains how searching, paging and filtering should actually be done in any API and how it should be done using the most current version of Web API (v2).
This is a post which I consider a good resource on the matter (technology indenpendent):
http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api
The answer should also reflect what's new in ASP.NET Web API v2 because Darin's answer is quite old.
Since this question comes up at the top when doing Google search for "asp.net web api searching" I will try to explain few things here.
To get as close as possible to REST principles with the latest version of ASP.NET Web API (v2) one should take a serious look at attribute routing that was introduced in the latest version. It is very hard to achieve RESTful routing with the old, classic, convention based routing (in global.asax.cs or RouteConfig.cs).
You should read more about that here
http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2
Now, to go into details how to implement the specifics you ask about.
The most common practice is to expose these types of functionality through query string parameters.
Per REST principles, you should have one endpoint for your Customers resource, for instance
/api/customers
To achieve this you would decorate your GetCustomers() action in your Web API controller like this
[HttpGet]
[Route("/api/customers")]
public HttpResponseMessage GetCustomers(string q="", string sortBy="", string sortDirection="", bool active=true, ...)
{
// q = being optional search query
// sortBy = optional sort by column/property
// sortDirection = optional sort direction
// active = filter on 'active' column/property
// ... other filters may be applicable
}
You would implement this action closely to what you did in classic MVC if you wanted to provide filtered Views.
I would only introduce new controllers and custom actions if really needed, for some custom edge cases.
with regards to a comment about SearchFilter strongly typed object, let's explain that this won't work out of the box because the default model binder will not bind to this class when using GET requests.
So I'd either take those properties out of SearchFilter class and put them on the action itself so they'd bind via query string binder or use the [FromBody] binder if you wanted to bind from the request body. As per http://www.asp.net/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api
HTH
As per the default route setup only the standard controller action names are allowed (the RESTful ones and the dispatching is done based on the HTTP verb). If you want to violate the RESTful conventions and use some custom action names then you will have to modify your route setup in order to include the action name in the url: api/{controller}/{action}/{id}. Now you can send a request to /api/Customers/Search/123 which will invoke the Search action on the Customers API controller.

asp.net mvc without entity framework

I am learning asp.net mvc and went through a great tutorial that demonstrated it. The tutorial also used Entity Framework.
We have our own data access class which I have to use.
I am a little bit confused as to what I need to do to bridge the gap between our class and MVC framework.
For example, in the tutorial, inside of MovieController.cs file, there is a Edit method, that looks like this:
[HttpPost]
public ActionResult Edit(Movie movie)
{
if (ModelState.IsValid)
{
db.Entry(movie).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(movie);
}
If I don't use the Entity framework, what would it look like? Will I still need to use ModelState.IsValid and save the state like it's done
db.Entry(movie).State = EntityState.Modified;
Please advise. A clearly written example of using asp.net mvc without the use of Entity framework would be great.
What I need to know is what role does state play here and whether it is mandatory to use or is it just a part of how the Entity framework operates.
I would re-write this as:
[HttpPost]
public ActionResult Edit(Movie movie)
{
myDBObject.SaveChanges();
return RedirectToAction("Index");
}
Where myDBObject is my custom database access object.
The examples you see out there where controllers use directly some data access framework such as Entity Framework are bad examples. The whole internet is polluted with such stuff. I can hardly look at it without having my eyes hurt. I consider those as bad practices. Data access should be separated and abstracted in a repository. So for example:
public interface IMoviesRepository
{
Movie Get(int id);
void Save(Movie movie);
}
then you could have some implementation of this interface using plain ADO.NET, EF, NHibernate, a remote web service call, some custom ORM or really whatever:
public class MyCustomFrameworkMoviesRepository: IMoviesRepository
{
...
}
and the controller will take this repository interface as constructor argument:
public class MoviesController: Controller
{
private readonly IMoviesRepository _repository;
public MoviesController(IMoviesRepository repository)
{
_repository = repository;
}
public ActionResult Index(int id)
{
var movie = _repository.Get(id);
return View(movie);
}
[HttpPost]
public ActionResult Index(Movie movie)
{
if (!ModelState.IsValid)
{
return View(movie);
}
_repository.Save(movie);
return RedirectToAction("Success");
}
}
and the last part is to configure your dependency injection framework to pass the correct implementation of the repository into the controller. Now as you can see the way the data is fetched is completely decoupled from the controller logic. It is the way it should be. Always try to avoid the strong coupling between the different layers of your application.
And to answer your question about the State property : this is something completely specific to EF, seeing something like this in a controller is a really pity.
And to bring this even further and improve it you would introduce view models. View models are classes which are specifically designed to meet the requirements of a given view. So for example Movie is a domain model. Domain models should never be directly passed to views. Controller actions should never take domain models as action arguments. You should define view models which will contain only what is required by the given view and then perform the mapping between the view models and the domain models. Frameworks such as AutoMapper make this very simple.
hmm.
MVC and entity framework really have nothing to do with each other; they just work well together.
the if (ModelState.IsValid) validates your view model. If you are not using view objects with validators, it's a little pointless; if you are, then it's quite valuable.
inside the if (ModelState.IsValid) brackets, you would take the post data from your web page (usually a view model) and apply it to the object that will persist it to the database. EF is often used because once it's set up, it's fairly easy to maintain, and a lot less code to write.
db.Entry(movie).State = EntityState.Modified;
db.SaveChanges();
are both EF-related. These would need to be replaced by your repository class methods and objects.
return RedirectToAction("Index");
is MVC. Upon successful persistence to your data store, return the control to the index page.
return View(movie);
is used to redirect back to the original view, because something failed validation.
You would still check ModelState.IsValid, but otherwise your code would look like what you have.
This assumes that your model has DataAnnotations attributes on it, though, which is what ModelState.IsValid is using to check. Those attributes can be used on any C# class' properties - not just Entity Framework.
You might end up creating specific view models for this purpose.
You need to make some connection between the Movie object (passed in on the http POST) and your database methods (myDBObject).
Maybe you want to say myDBObject.SaveChanges(movie) and assuming your db code knows how to handle the object Movie then you'll be fine.