ASP.NET MVC Web.Api Routing - Real world example - asp.net-mvc

I have been looking at routing in the Web.Api and looking at various ways of representing endpoints. I came across Instagrams REST endpoints. (which has some lovely documentation) Using the Web.Api what would be the best way to set up the routing and controllers for a sitution like Instagrams user endpoints?
User Endpoints
GET/users/user-id Get basic information about a user.
GET/users/self/feed See the authenticated user's feed.
GET/users/user-id/media/recent Get the most recent media published by a user.
GET/users/self/media/liked See the authenticated user's list of liked media.
GET/users/search Search for a user by name.
If I wanted to replicate these endpoints in my app, how would I go about it. Would I just need one controller 'Users' with 5 methods, what kind of routing would I need to direct the REST calls to those methods?

I would structure this in a different way.
GET/user
GET/user/user-id
GET/user?q=user-name
GET/media
GET/media/user-id
GET/media/user-id?selection=recent
GET/media/user-id?selection=liked
GET/feed
GET/feed/user-id
This way you keep your Controllers for a specific target, much like keeping your classes for a single responsibility.
User
Media
Feed
When you use this approach it's much easier for a user to 'guess' the path. And I think you could already guess what each path does without any explanation. For me that's the most important when I'm designing a API.
GET/controller - always returns a item list
GET/controller/id - always returns a specific item
GET/controller?q= - always queries the controller
GET/controller?selection= - always selects a subset from the list off items
Ofcourse this is open for interpretation but it gives you an idea about how I would solve this particular problem and maybe some ideas to think about. Also have a look at this great book from Apigee - Web Api Designs
http://info.apigee.com/Portals/62317/docs/web%20api.pdf
Edit:
To make the routes you named I think you've got 2 (not very ideal) options.
Map a specific route for each url
Make a wildcard route
Option 1
I have not tried, or used this myself but you can find more info here:
Single controller with multiple GET methods in ASP.NET Web API
Option 2
If you go the wildcard route all requests with additional parameters will be routed to your default Get() method. In your get you have to look at the parameters using ControllerContext.Request.RequestUri.AbsolutePath or something like it and choose your actions on it.
config.Routes.MapHttpRoute(
name: "MyApi",
routeTemplate: "api/{controller}/{id}/{*wildcard}",
defaults: new { controller = "index", id = RouteParameter.Optional }
);

Related

Using custom routes instead of /controller/action/{id}

I have to make vanity urls for an already existing site. The requirements are:
The existing controllers are a kind of hierarchical structure and can't go away or be overridden in any way. Examples: domain.com/Dog/vanity and domain.com/Cat/vanity.
Keep existing actions. Any existing actions must take priority so that the page for that action is not stomped on by the vanity url.
take future pages and actions into account so that the above requirement is met (a new vanity url is ignored and the action/view executed instead)
To date, I have tried various solutions with routing that allow me to have domain.com/vanity which is nice but the marketing guys don't like because of the different departments within the company. I've tried routing which will override the existing actions and treats them all as vanities (also not feasible). I've a solution in place that programmatically deals with the url that was requested and redirects to a page that actually exists but this is not scalable in any way.
So far, I know that the vanity portion can be treated as a parameter to the action so that I can fire off the default page in the route (Index) but this is, so far, doesn't preserve the structure.
TL;DR: I need to have a solution that allows for domain/controller/vanity structure while also allowing domain/controller/action
Using AttributeRouting for MVC4 you can accomplish a working solution until you ramp up the replacement project. It'll allow you to keep existing routes while adding new, custom ones with little impact.
[Route("my-vanity/is-cool/post/{id}")]
public ActionResult Index(int id)
{
}
The important part is to remember priority, so you write routes that don't overwrite/are overwritten by existing routes. You can steer this to some degree with properties on the attribute. Below is just an example that will put the added route last in priority for the entire site.
[Route("my-vanity/is-cool", SitePrecedence = -1)]
public ActionResult Index()
{
}
ASP.NET WebApi2 have built in support for attribute routing. With it you can define URL's in whatever way you like (instead of following the /controller/action pattern)
http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2

Strategy for Hierarchical MVC Routing

I am sure this has been answered somewhere else but I cannot seem to find a definitive posting anywhere.
Most of the postings regarding hierarchical routing talks about when you want an unlimited number of tokens in the Url. My question deals more with when it does not make sense for a given Entity to exists without being associated in the context of another Entity.
For example, I have a Contract entity along with the expected Contract controller. It has the standard Actions like Index, Edit, Create, and so on. My Urls look like
/Contracts/ ' list all contracts
/Contracts/Create/ ' display form to create new contract
/Contracts/Edit/87Y5r3/ ' display form to edit contract 87Y5r3
Now imagine that I Order entity that must be associated with a given Contract. Using the (almost) default routing I would have Urls of
/Orders/ ' display all orders across all contracts
/Orders/Index/87Y5r3 ' display all orders for contract 87Y5r3
/Orders/Create/87Y5r3 ' display form to create new order for contract 87Y5r3
/Orders/Edit/87Y5r3/45 ' display form to edit order 45 under contract 87Y5r3
I can of course leave the almost default routing while tweaking it to support the additional parameters like contract number and order number.
Or I can change my routing to show that Orders comes under Contracts in the hierarchy. As I see it, I have several paths to pursue:
1) Have a single controller that handles both Contracts and Orders along with numerous custom routes for mapping actions to methods. This gives me Urls along the lines of
/Contracts/ ' maps to Index action in the Contract controller
/Contracts/Create/ ' maps to Create action in the Contract controller
/Contracts/Orders/ ' maps to IndexOrders action in the Contract controller
/Contracts/Orders/Index/87Y5r3 ' maps to IndexOrders action in the Contract controller
/Contracts/Orders/Edit/87Y5r3/45 ' maps to EditOrders action in the Contract controller
While I cannot imagine any good argument for having just a single controller, I am guessing that this good also be split into a Contracts controller and an Orders controller with an appropriate route(s).
The main point to take in is that the Contract Number is coming towards the end of the Url.
2) Another option is the separate controllers but with the following Urls. This seems a bit more natural (logical) to me.
/Contracts/ ' maps to Index action in the Contract controller
/Contracts/Create/ ' maps to Create action in the Contract controller
/Contracts/?????/87Y5r3/Orders/Index/ ' maps to Index action in the Order controller
/Contracts/?????/87Y5r3/Orders/Edit/45 ' maps to Edit action in the Order controller
/Contracts/?????/All/Orders/ ' maps to Index action in the Order controller
In this case the contract number comes after the Contract token in the Url with the order number coming towards the end. I have identified a few issues/concerns
How to handle Order data that goes across all Contracts. As you can see handled that with a special "All" token.
What action do I use for the Contracts portion of the Url. By default, routing in Mvc is /{controller}/{action}/{id}.
3) The third option I have seen posted (but do not understand enough to evaluate pros and cons) is to use RESTful API. I believe this would (could ??) resolve my second concern about what action to use for the Contract when working with Orders. The basic idea is that the action is replaced with an HTTP verb like DELETE or PUT which would only need to apply to the entity at the end of the Url.
In this case I would end up with something like
GET /Contracts/ ' maps to Index action in the Contract controller
POST /Contracts/Create/ ' maps to Create action in the Contract controller
GET /Contracts/87Y5r3/Orders/ ' maps to Index action in the Order controller
PUT /Contracts/87Y5r3/Orders/45 ' maps to Edit action in the Order controller
GET /Contracts/All/Orders/ ' maps to Index action in the Order controller
While RESTful may be the way to go I definitely do not know enough about it and my initial reaction is that it adds complexity and limitations (how many verbs are there???) that may limit its usefulness.
Based on my quick reading, going with a RESTful approach (including ASP.NET Web API as suggested by #Robotsushi below) does not really answer my question. RESTful seems to be something to be considered if my pages were requesting data via AJAX and JSON. In that sense (requesting data only) it provides an approach to Urls. However, my question is focused more on the standard MVC model where the action passes a Model to a View. At the end of the day I still need to present web pages to my users...
Did I sum this up clearly? Any other strategies that I am missing? This has to be a fairly common scenario so I am surprised I have not found a ton of articles.
I simplified the examples a bit but in my case I actually need to take this to a third level --- Contracts / Orders / Projects.
Thanks
This is purely my opinion.
I would recommend that you use a web service. If you can't you can still use HTTP POST. It will be easier to send a complex data structure without cluttering the URL with alot of unstructured key/value pairs.
If you used this strategy you would be able to send XML or JSON as your data structure, and get the complex entity representation you probably need.
If you are unfamiliar with HTTP based web services, then check out ASP.NET Web API.
Good Luck
EDIT
You can HTTP POST data to your MVC controller. If you do this then you can use a complex serialization format such as XML or JSON to send up your data. This will allow for the hierarchical nesting that your entities require.
I should have been more clear about web services. The type of operations you are performing seem like they might be better off in a web service. However regardless of whether or not you choose to use a web service your mvc controller can accept data can be correctly represented using HTTP POST actions.
I hope this helps.

ASP.Net MVC url design & structure guidelines

I wanted to get some expert suggestions on designing urls for our web app. This is not a public domain website, it is a supplychain intranet based web-app used only by a group of authenticated users.
Here're some examples -
/Claim/12/Manage
FORMAT: controller/{ID}/action
The url that points to a "Claim Entry" wizard. Here "12" is the ClaimID. It is further divided into tabs for sub-data entry.
Example: /Claim/12/Print, /Claim/12/FileDetails, ...
/Users/List
FORMAT: controller/action
Display's a list of existing users in Grid. Shud this be shortened to "/Users" ? Likewise we've some other entities as well like "Roles, Organizations, etc..."
/Master/Manage/FileType
FORMAT: controller/action/{argument}
We've a page which allows he user to manage different master table data. Need to know which master table is selected (i.e. sent as argument). Is it better to simplify it as "/Manage/{argument}" instead and then map that url as required above?
Is it sensible in MVC to hide default actions like "Claim/21/Manage" shud be "Claim/21", "/Users/List" shud be "/Users" ...
Arguments - are they better as embedded in url or good to append as query-string
Any generic guidelines or references would also be great.
Ref: Web services url - (Section: Designing the URI Templates)
http://msdn.microsoft.com/en-us/library/dd203052.aspx
You can use Regular Expressions to represent various routes you have. For example
protected void Application_Start()
{
RouteTable
.Routes
.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL template
new { controller="Mycontroller", action="Myaction", id=UrlParameter.Optional },
new { action = #"\d{2}-\d{2}-\d{4}" }
);
}
Well, I conclude that this one is the best (and probably the most detailed) explanation I can find on MSDN - http://msdn.microsoft.com/en-us/library/dd203052.aspx
And that shud be sufficient :-)

ASP.NET MVC - CMS Questions

I'm looking at developing an application that will include a CMS. I'm a seasoned web forms developer but only really just moving into MVC.
I have a couple of questions that I hope some of you guys can answer:
First, my current web forms CMS allows users to create a page, and then "drop" any number of user controls onto that page they have created. The way I do this is to create an entry in the DB together with the path and then use the LoadControl method.
I can see I can do this with partial views, but partial views have no code behind. If I've potentially got 100 controls that people can drop onto a page, does this mean that the ViewBag in the controller needs to cater for all 100 controls just in case they are used on the view? For example, a web forms user control will contain logic: rptItems.DataSource = blah; rptItems.DataBind()
With MVC, I'm assuming that logic will be in the view controller and the view would access it by the ViewBag? I'm a little confused at how to do this.
Secondly, how would you handle deep routing?
EG:
Store/Products/Category is fine, but what about Store/Products/Category/Delivery/UK ? Would I need to set up a route in global.asax for each route I need? In web forms, I just called the ReWritePath method and handled the routing myself using regular expressions.
Thanks for the time to read this, and hopefully answer some of my queries
For your second question, (ie, "deep routing"), you can handle this within your controller instead of adding real routes. Each part of the url is available via the RouteData.Values collection inside of your controller action. So, your route may look like
~/Store/Products/Category/{*params}
Assuming typical route configuration, this would call the Category(...) action method on ~/areas/store/controllers/storeController, which could then grap delivery and uk from the RouteData.Values collection.
There are a lot of other approaches to this - storing routes in a database and using associated metadata to find the correct controller and method - but I think this is the simplest. Also, it may be obvious, but if you really only need two parameters beyond 'Category' in your example, you could just use
public ActionResult Category(string category, string region)
{
...
}
and a route:
~/store/{controller}/{action}/{category}/{region}/{*params}
Delivery and UK would be mapped to the the category and region parameters, respectively. Anything beyond uk would still be available via the RouteData.Values collection. This assumes that you don't have more specific routes, like
~/store/{controller}/{action}/{category}/{region}/{foo}/{bar}/{long_url}/{etc}
that would be a better match. ({*params} might conflict with the second route; you'll have to investigate to see if it's a problem.)
For your first question:
You can dynamically generate the view source and return it as a string from the controller, eliminating the need to pass a lot of stuff via ViewBag. If a virtual page from your CMS database requires inclusion of partial views, you would add the references to those components when generating the page. (This may or may not address your problem - if not, please provide more information.)

ASP.Net MVC: Check if URL is Authorized

I'd like to simply check from a Controller whether another URL is authorized.
So for example, I'd like to call into a Controller like so:
[HttpPost]
public ActionResult IsUrlAuthorized(string url)
{
bool isAuthorized = // What do I put here?
return Json(isAuthorized);
}
So I'd like to know what I could call to check on whether the current user is authorized for the passed-in URL or not. I'm guessing the answer has something to do with Routes, which sit a little bit outside MVC?
This is a somewhat similar question but not quite the same thing:
ASP.NET MVC. Check if user is authorized from JavaScript
Since the user may or may not be authorized in general, but may not have the right permissions or role assignments to see a specific URL.
Ideas?
Update: I use standard MVC authorization attributes to lock down my app, so I'll just give an example of what that looks like here. In MVC Routes map to Controllers. A single method on a Controller can be restricted to one or more Roles:
public class HomeController : Controller
{
[Authorize(Roles = "User, Moderator")]
public ActionResult ListRecentPosts()
{
. . .
}
}
Or, an entire Controller can be restricted to one or more roles:
[Authorize(Roles = "Admin")]
public class AdminController : Controller
. . .
The actual URL that any of these controller methods responds to is based on a default mapping in a standard MVC app:
routes.MapRoute("Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
But, you can be nice to your users and make URLs guessable by adding a lot more Routes - as a result, a Controller method can have many names that point to it. You can't just assume and infer the controller name from the URL (even if it maps out that way for half the URLs in the site).
So presumably I either need a way to ask the Routing engine directly whether a URL is authorized for the current user, or a 2-step of asking the Routing engine for which Controller and Method, then ask if those are authorized - hopefully not by using Reflection and matching Roles directly as that again would appear to assume too much.
Update 2: The way this came up is I have an Account strip at the top of my app. Its state can change by selecting one of several accounts you're authorized as. Depending on where you are in the app, the account you chose might have authorization to view this page - and you might be in the middle of filling out a form you don't want to lose. So the naive approach - just refresh when they pick another account - is harmful, and a waste of the user's time even if there is no form and they're just reading a page that's all text.
While that convenience to the user is nice, the user is going to fairly assume that pages they can't see as a user who shouldn't have permission really are denied (and, it would be harmful to leave them on a page that's forbidden - actions taken from it will fail). So I need to know whether to redirect away based on their new permissions.
One of the things I love about .Net is the way many of its best libraries decompose so well, so you can easily recompose things that are part of its normal functionality, or a new twist. Both the Routing module and MVC appear to be very well constructed, so I have to suspect this can be done.
The cheap hack is to ensure that my authorization module returns a consistent redirect status code when a user isn't authorized, and when the user changes their account in the account strip, fire 2 AJAX calls: One to change account, and then a second to the current page over AJAX just to check the HTTP Status Code. 200 OK means leave the page as is, Redirect means follow the redirect. Obviously this is a little ugly, involves an extra HTTP call, creates a false hit in the logs, and makes an assumption about how authorization is handled across the app.
There could be a secondary concern - the page might be authorized, but just change how it works or looks. This particular app has no change in look based on account (besides the account strip itself), and I can handle functionality changes by just providing a custom event that forms listen to - they can reload any relevant data from the server in response to it.
Using UrlAuthorization.CheckUrlAccessForPrincipal only works if you're only using URL authorization. But for MVC using Routing, we highly recommend that you don't use URL authorization to secure an app.
Instead, we recommend using Authorization attributes on the controller class. The reason is there could be multiple URLs that call the same controller action. It's always better to secure the resource at the the resource and not just at the entry ways.
In this particular case, you'd have to get an instance of the controller given the URL. THat's a little tricky as you'll basically have to run the MVC pipeline from the point where you have the URL to the point where you have the controller. It's possible, but seems heavyweight.
I wonder if there isn't a better and simpler way to accomplish your goals. What is it you're really trying to do?
UPDATE: Based on your scenario, it sounds like this is an initial check just for UI purposes. Perhaps all you need to do is make an asynchronous Ajax request to the URL and check the HTTP Status code. If it's a 401 status code, you know the user is not authorized. That seems like the safest bet.
How about UrlAuthorizationModule.CheckUrlAccessForPrincipal method.
UrlAuthorizationModule.CheckUrlAccessForPrincipal Method (System.Web.Security)

Resources