asp.net webapi url to identify same resource - asp.net-mvc

I want to be able to identify a resource by different id types. for example:
GET http://example.com/customers/internalId=34 to go to
public Customer GetByInternalId(int internalId){...}
and
GET http://example.com/customers/externalId='JohnDoe' to go to
public Customer GetByExternalId(string externalId){...}
I know I can do this by having some parsing logic in a generic controller method but I don't want to do that. How do I achieve this using the routing feature of asp.net webapi if that is possible.

I would suggest that you try and avoid doing what you are suggesting. Creating two distinct URIs for the same resource will make it harder to use caching. Instead I would suggest using one URL to redirect to the other.
e.g.
> GET /customers/34
< 200 OK
> GET /Customers?name=JohnDoe
< 303 See Other
< Location: http://example.com/customers/34

Your methods do not make much of a sense, why would you return void from a method that begins with Get....?
Also, these routes:
http://example.com/customers/internalId=34
http://example.com/customers/externalId='JohnDoe
Are invalid from MVC/Web API perspective. This is how they should look like:
http://example.com/customers?internalId=34
http://example.com/customers?externalId=John
Default Web API routing should differentiate between the two and route it to different actions.
EDIT:
Create action with the following template:
[HttpGet]
public string InternalId(int id)
{
return id.ToString();
}
Define route for Web Api:
config.Routes.MapHttpRoute(
name: "Weird",
routeTemplate: "{controller}/{action}={id}",
defaults: new { id = RouteParameter.Optional }
);
This allows you to write:
http://localhost:7027/values/internalId=12
Try it...
Then you can just add another method:
[HttpGet]
public string ExternalId(string id)
{
return id;
}
And this:
http://localhost:7027/values/externalId=bob
Will work as well.
Clearly name of my controller is ValuesController as I've just tested this with default Web Api template.

Related

Difference between Web API (standalone) and Web API ASP.NET MVC

I've created both an ASP.NET MVC project which includes the Web API and I created an empty project where only Web API was selected and I'm trying to understand why they are behaving differently.
Both of them from what I can tell have the routing definition defined as follows:
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
But I'd like to understand why the functions that are defined in the controller are behaving differently.
In my ASP.NET MVC Solution, I can create multiple functions under the one controller:
public IEnumerable<Product> GetAllProducts()
{
return products;
}
public IHttpActionResult GetProduct(int id)
{
var product = products.FirstOrDefault((p) => p.Id == id);
if (product == null)
{
return NotFound();
}
return Ok(product);
}
I don't have to add any attributes, well at least for gets and I can call individual function as:
http://localhost/api/test/GetProduct/1
http://localhost/api/test/GetProducts
In the Web API only solution, I had to define it as:
public IHttpActionResult HelloWorld()
{
return Ok("Hello World");
}
but the above will not work unless I add the [HttpGet] attribute to my HelloWorld function.
Also I don't seem to be able to define multiple functions within the one controller as it is done the in ASP.NET MVC Solution. Defining the same function as above just does not work or even if I define the functions below
[HttpGet]
public IHttpActionResult HelloWorld()
{
return Ok("Hello World");
}
[HttpGet]
public IHttpActionResult GetTime()
{
return Ok(DateTime.Now.ToString());
}
I get the following error:
<Error>
<Message>An error has occurred.</Message>
<ExceptionMessage>
Multiple actions were found that match the request: HelloWorld on type
MyProject.Controllers.TestController GetTime on type
MyProject.Controllers.TestController
</ExceptionMessage>
<ExceptionType>System.InvalidOperationException</ExceptionType>
<StackTrace>
at
System.Web.Http.Controllers.ApiControllerActionSelector.
ActionSelectorCacheItem.SelectAction(HttpControllerContext
controllerContext) at System.Web.Http.ApiController.
ExecuteAsync(HttpControllerContext controllerContext, CancellationToken
cancellationToken) at System.Web.Http.Dispatcher.
HttpControllerDispatcher.<SendAsync>d__15.MoveNext()
</StackTrace>
When I define a single function, it works but contrarily to a Web API defined in my ASP.NET MVC solution, when I call the below
http://localhost/api/test/HelloWorld
It doesn't seem to care what the function name is called as it will work if I call any of the following:
http://localhost/api/test
http://localhost/api/test/GoodBye
and will only fail if I provide an invalid controller name i.e.
http://localhost/api/mytest
Can you explain the difference between the 2 and ideally what do I have to do to get a stand-alone Web API to behave the same way as it does when defined in Asp.net MVC
Thanks.
UPDATE-1
Below are 2 good examples where the behavior is clearly different but I can't see why as both are called "Web API with ASP.NET MVC".
Creating Simple Service with ASP.NET MVC Web API to be accessed by Windows Store and Windows Phone Application
Getting Started with ASP.NET Web API 2 (C#)
Edit:
I'm sry at first I misunderstood your enquiry and therefore gave you the wrong answer to your question.
In Web Api you can't define two functions that serve the exact same path. But you can overload them like you would with Functions.
public IEnumerable<Product> GetAllProducts() {}
public IHttpActionResult GetProduct(int id) {}
These two function differ in so far that they serve different paths:
localhost/api/products/
localhost/api/products/{id}/
The following functions on the other hand both serve localhost/api/test/
[HttpGet]
public IHttpActionResult HelloWorld()
{
return Ok("Hello World");
}
[HttpGet]
public IHttpActionResult GetTime()
{
return Ok(DateTime.Now.ToString());
}
Your second concern has been the naming and mostly why you can't define HelloWorld() without [HttpGet]. That is because Web Api automatically maps functions that have a keyword such as Get or Delete as part of their name to the corresponding Http Function.
Adding the Http-Attribute is only needed if the function name does not conform to these naming conventions or shall serve multiple Http-Methods. It's described here
I hope your questions are now answered.
Orig:
The Default Routing and subsequent decision on what Function is to be executed is different between MVC and Web API
Default Web Api:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Default Mvc:
config.Routes.MapHttpRoute(
name: "DefaultMvc",
routeTemplate: "/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
For Web Api the Routing is dependant on the controller name and further which Http-Method has been selected as well as the controller actually having a function the has been tagged with this method. Hence the and further Tags
Mvc on the other hand routes via the controller name and the action name to the desired functionality.
Web Api 2 adds further possibilies in letting you define AttributeRoutes. You may want to look further on this topic here and here

Nested resources in ASP.net MVC 4 WebApi

Is there a better way in the new ASP.net MVC 4 WebApi to handle nested resources than setting up a special route for each one? (similar to here: ASP.Net MVC support for Nested Resources? - this was posted in 2009).
For example I want to handle:
/customers/1/products/10/
I have seen some examples of ApiController actions named other than Get(), Post() etc, for example here I see an example of an action called GetOrder(). I can't find any documentation on this though. Is this a way to achieve this?
Sorry, I have updated this one multiple times as I am myself finding a solution.
Seems there is many ways to tackle this one, but the most efficient I have found so far is:
Add this under default route:
routes.MapHttpRoute(
name: "OneLevelNested",
routeTemplate: "api/{controller}/{customerId}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
This route will then match any controller action and the matching segment name in the URL. For example:
/api/customers/1/orders will match:
public IEnumerable<Order> Orders(int customerId)
/api/customers/1/orders/123 will match:
public Order Orders(int customerId, int id)
/api/customers/1/products will match:
public IEnumerable<Product> Products(int customerId)
/api/customers/1/products/123 will match:
public Product Products(int customerId, int id)
The method name must match the {action} segment specified in the route.
Important Note:
From comments
Since the RC you'll need to tell each action which kind of verbs that are acceptable, ie [HttpGet], etc.
EDIT: Although this answer still applies for Web API 1, for Web API 2 I strongly advise using Daniel Halan's answer as it is the state of the art for mapping subresources (among other niceties).
Some people don't like to use {action} in Web API because they believe that in doing so they will be breaking the REST "ideology"... I contend that. {action} is merely a construct that helps in routing. It is internal to your implementation and has nothing to do with the HTTP verb used to access a resource.
If you put HTTP verb constraints on the actions and name them accordingly you're not breaking any RESTful guidelines and will end up with simpler, more concise controllers instead of tons of individual controllers for each sub-resource. Remember: the action is just a routing mechanism, and it is internal to your implementation. If you struggle against the framework, then something is amiss either with the framework or your implementation. Just map the route with an HTTPMETHOD constraint and you're good to go:
routes.MapHttpRoute(
name: "OneLevelNested",
routeTemplate: "api/customers/{customerId}/orders/{orderId}",
constraints: new { httpMethod = new HttpMethodConstraint(new string[] { "GET" }) },
defaults: new { controller = "Customers", action = "GetOrders", orderId = RouteParameter.Optional, }
);
You can handle these in the CustomersController like this:
public class CustomersController
{
// ...
public IEnumerable<Order> GetOrders(long customerId)
{
// returns all orders for customerId!
}
public Order GetOrders(long customerId, long orderId)
{
// return the single order identified by orderId for the customerId supplied
}
// ...
}
You can also route a Create action on the same "resource" (orders):
routes.MapHttpRoute(
name: "OneLevelNested",
routeTemplate: "api/customers/{customerId}/orders",
constraints: new { httpMethod = new HttpMethodConstraint(new string[] { "POST" }) },
defaults: new { controller = "Customers", action = "CreateOrder", }
);
And handle it accordingly in the Customer controller:
public class CustomersController
{
// ...
public Order CreateOrder(long customerId)
{
// create and return the order just created (with the new order id)
}
// ...
}
Yes, you still have to create a lot of routes just because Web API still can't route to different methods depending on the path... But I think it is cleaner to declaratively define the routes than to come up with a custom dispatching mechanisms based on enums or other tricks.
For the consumer of your API it will look perfectly RESTful:
GET http://your.api/customers/1/orders (maps to GetOrders(long) returning all orders for customer 1)
GET http://your.api/customers/1/orders/22 (maps to GetOrders(long, long) returning the order 22 for customer 1
POST http://your.api/customers/1/orders (maps to CreateOrder(long) which will create an order and return it to the caller (with the new ID just created)
But don't take my word as an absolute truth. I'm still experimenting with it and I think MS failed to address properly subresource access.
I urge you to try out http://www.servicestack.net/ for a less painful experience writing REST apis... But don't get me wrong, I adore Web API and use it for most of my professional projects, mainly because it is easier to find programmers out there that already "know" it... For my personal projects I prefer ServiceStack.
Since Web API 2 you can use Route Attributes to define custom routing per Method, allowing for hierarchical routing
public class CustomersController : ApiController
{
[Route("api/customers/{id:guid}/products")]
public IEnumerable<Product> GetCustomerProducts(Guid id) {
return new Product[0];
}
}
You also need to initialize Attribute Mapping in WebApiConfig.Register(),
config.MapHttpAttributeRoutes();
I don't like using the concept of "actions" in the route of an ASP.NET Web API. The action in REST is supposed to be the HTTP Verb. I implemented my solution in a somewhat generic and somewhat elegant way by simply using the concept of a parent controller.
https://stackoverflow.com/a/15341810/326110
Below is that answer reproduced in full because I'm not sure what to do when one post answers two SO questions :(
I wanted to handle this in a more general way, instead of wiring up a ChildController directly with controller = "Child", as Abhijit Kadam did. I have several child controllers and didn't want to have to map a specific route for each one, with controller = "ChildX" and controller = "ChildY" over and over.
My WebApiConfig looks like this:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "ChildApi",
routeTemplate: "api/{parentController}/{parentId}/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
My parent controllers are very standard, and match the default route above. A sample child controller looks like this:
public class CommentController : ApiController
{
// GET api/product/5/comment
public string Get(ParentController parentController, string parentId)
{
return "This is the comment controller with parent of "
+ parentId + ", which is a " + parentController.ToString();
}
// GET api/product/5/comment/122
public string Get(ParentController parentController, string parentId,
string id)
{
return "You are looking for comment " + id + " under parent "
+ parentId + ", which is a "
+ parentController.ToString();
}
}
public enum ParentController
{
Product
}
Some drawbacks of my implementation
As you can see, I used an enum, so I'm still having to manage parent controllers in two separate places. It could have just as easily been a string parameter, but I wanted to prevent api/crazy-non-existent-parent/5/comment/122 from working.
There's probably a way to use reflection or something to do this on the fly without managing it separetly, but this works for me for now.
It doesn't support children of children.
There's probably a better solution that's even more general, but like I said, this works for me.

Value cannot be null. Parameter name: String in asp.net mvc C#

I am pretty new to asp.net mvc. I want to get the parameter string in my url
http://localhost/Item/ItemSpec/3431?dep=62&cat=129&tab=2
How can I get the value=3431?
I tried to used HttpContext.Current.Request.QueryString["id"], but it's not work. 3431 is the id of the item that display in my page, that's why I used ["id"].
Thanks you so much.
The 3431 is part of the path of the request, not part of the query string. You could use HttpRequest.Path to get at the path, but MVC routing should allow you to simply write a controller method which accepts the ID as a parameter. I suggest you read up on how to configure routing. (Just searching for ASP.NET routing or MVC routing will give you lots of articles.)
Assuming the default route is configured in Global.asax ({controller}/{action}/{id}) you could have your controller action take an id parameter and the default model binder will automatically set its value:
public ActionResult Foo(string id)
{
...
}
If you want to fetch this id value from some other portion of your code that does have access to an HttpContext you need to fetch it from the RouteData:
var id = HttpContext.Request.RequestContext.RouteData["id"];
RouteData is available in all standard MVC locations. In your example you have used the static HttpContext.Current property which is something that you should never use. I suspect that you are trying to fetch this id from a portion of your code where you are not supposed to have access to the HttpContext. So you'd better fetch this id using standard techniques and then pass it as parameter to other parts of your code.
If Item is your controller, and ItemSpec is action, you can get the Id just by
public ActionResult ItemSpec(int id) { }
You routing have to be setup to:
context.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index" }
);
If you haven't changed your routing so it's still defined as it was when you created Asp.net MVC Web project then this should be one of your controllers:
public class ItemController : ControllerBase
{
...
public ActionResult ItemSpec(int id, int dep, int cat, int tab)
{
// implementation that uses all four values
}
...
}
This is of course just one of the actions in it. There may be others as well. Most likely the Index one that's generated by default and is also used by default routing...

ASP.NET MVC Areas or Defined Routes?

I'd like to achieve the following (with ASP.NET MVC 3):
A controller called "apps" with the following actions:
/apps/my
/apps/agency
/apps/new
Within the last action I really want some sub-actions, e.g.:
/apps/new/product
/apps/new/tariff
I could write the New() action to take some kind of parameter to say which view I should render (i.e. product or tariff) but that feels a bit dirty.
What I really want is separate action methods for product and tariff.
What's the best way to go about this?
I think I could use Areas but this seems overkill for what I want - is the solution just to write a custom route?
Many thanks!
Sam
You could use Areas but for this small amount I agree that it is probably overkill. I'd say making the New action take in a parameter is fine for what you need. It may get more complicated if you want to pass more information in but its still do able. If you want to keep the code clean(er) you can have the action do all the complicated bits in separate private methods.
However, doing it with a custom route way (and with a separate controller as well) ...
Global.asax.cs
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute("newControllerRoute",
"apps/new/{action}",
new {controller = "NewApps"});
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
The add a controller (NewAppsController).
public class NewAppsController : Controller
{
public ActionResult Product()
{
/* used as example */
return Content("NewApps controller - Product");
}
public ActionResult Tariff()
{
/* used as example */
return Content("NewApps controller - Tariff");
}
}
Hope this helps.
If you have two distinct things that you want to be able to add then that is two distinct actions on your controller. It sounds like you are just wanting to control your URL schema so I think some sort custom routing is the answer.
Why not have separate controllers for tariffs and products? That will lead to a natural URL schema.

ASP.NET MVC 3 Reroute to other controller and action & Restful API versioning

My original problem is I am looking what is the best practise to do versioning in Restful API. Not much people talk about this, dont have a good answer or I can't found exactly the solution at this moment.
(1) At first I am thinking to use Tag or Branch for each version http://domain.com/API/{version}. So if new API released, I Tag it, export and publish into the respective URL but seems hard to mix the different revision of source in one web application.
(2) Then I am thinking to use this way, one controller for one version:
(Just like this question Versioning of REST API Built With ASP.NET MVC 3 - Best Practices)
http://domain.com/API/1.0/{AnAction} => will go to APIV1Controller.{AnAction}
http://domain.com/API/2.0/{AnAction} => will go to APIV2Controller.{AnAction}
but it need to write a route for each version.
(3) Third way I get the idea from PayPal API which is the version is not in the URL but in the POST parameter. So the URL fixed to http://domain.com/API/ but user must specify the Version parameter to have "1.0" or "2.0".
The solution for this: The (2) is ok for me, and currently I use this way but I want to mixed the (2) and (3) so I have a APIController which only have one Index action to check this Version parameter and transfer the request to the respective controller and action either APIV1Controller.{AnAction} or APIV2Controller.{AnAction}.
After Googling and Stackoverflowing about how to transfer, invoke or call another controller and action without redirection. Seems there is no good answer and good practise. Someone answer .NET MVC Call method on different controller by simply creating new instance of the controller. Suddenly I got the idea how about to reroute!
The question:
Is it possible to reroute the the other controller and action from another action without redirection and how to do that?
Or a specific question, when user request http://domain.com/API/{AnAction} with Version="2.0", how can I reroute from APIController.Index to APIV2Controller.{AnAction}?
I am not using IoC.
This can be done via routing constraints. Firstly implement IRouteConstraint:
public class RequestParameterConstraint : IRouteConstraint
{
public string ParameterName { get; private set; }
public string ParameterValue { get; private set; }
public RequestParameterConstraint(string parameter, string value)
{
ParameterName = parameter;
ParameterValue = value;
}
public bool Match(HttpContextBase httpContext, Route route, string parameterName,
RouteValueDictionary values, RouteDirection routeDirection)
{
var value = httpContext.Request[ParameterName] ?? "";
return value.Equals(ParameterValue);
}
}
Then register routes:
routes.MapRoute(
"Version10",
"API/{action}/{id}",
new { controller = "APIV1", action = "Index", id = UrlParameter.Optional },
new { header = new RequestParameterConstraint("Version", "1.0") }
);
routes.MapRoute(
"Version20",
"API/{action}/{id}",
new { controller = "APIV2", action = "Index", id = UrlParameter.Optional },
new { header = new RequestParameterConstraint("Version", "2.0") }
);
That's all. This will do the trick.
http://coderjournal.com/2010/09/simple-rest-api-versioning-using-mef-and-mvc/
This seems to do exactly what you want, but it takes a very different approach by using a completely different set of controllers, rather than rerouting to them.
Hope this helps.

Resources