When I was working with Web API 2, I found that there is slight change in routing of web api.
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
I understand the working of routing. Also I found that, by default there is no method name in routing although we can modify the routing and can add add pattern for action/method. But my question is what was the idea behind creating such routing.
Thank you
Susheel
There are no actions because the idea is that you can have a method for each HTTP verb. e.g.
public IHttpActionResult Get()
public IHttpActionResult Get(int id)
public IHttpActionResult Post()
public IHttpActionResult Put(int id)
The verb used determines which method is called.
The idea is that controllers are supposed to be very specific about what they relate to.
So you may have a "PersonController" which deals with adding and updating people, and an "OrdersController" which deals with orders.
Obviously this doesn't work out quite as straightforward in practice because controllers end up being more wide ranging than CRUD operations for an entity type.
Related
I added the AspNet.WebAPI to my existing MVC project with help from this post:
How to add Web API to an existing ASP.NET MVC (5) Web Application project?
It basically adds the following controller:
public class APIController : ApiController
{
// GET api/<controller>
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
...
And this in the App_Start:
namespace WebApplication1
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// TODO: Add any additional configuration code.
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
// WebAPI when dealing with JSON & JavaScript!
// Setup json serialization to serialize classes to camel (std. Json format)
var formatter = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
formatter.SerializerSettings.ContractResolver =
new Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver();
}
}
}
This works, but for just for mysite/api/API
How do I expand this to work with the rest of my existing controllers (for example mysite/api/CLIENTs)?
From looking at the code, it seems it should just work. I even tried putting "Get" functions in the other controllers but no luck. Actually I don't know why /API works--is it reading the file name of the controller?
So basically, I am looking for advice on where to go next--do I put code in each individual controller, or put many calls in my existing "APIController", or neither.
The controller it added is just an example of a WebAPI controller. You can add more. For example:
public class ClientsController : ApiController
{
public IEnumerable<Client> Get()
{
// return a collection of clients
}
// etc.
}
Since they inherit from different base classes, you'll need to keep your MVC controllers and API controllers separate. So just add API controllers for your API methods.
When writing Web Api method in .Net is it necessary to have the method name prefix with HTTP code like GET, POST etc?
Example:
public IEnumerable<Product> GetAllProducts();
public IHttpActionResult GetProduct(int id);
public IHttpActionResult PostProduct(Product prod);
No, it's not necessary, but it's one of several conventions to map HTTP verbs to action methods. You could, for example, do this:
[HttpGet]
public IHttpActionResult AllProducts();
or this:
public IHttpActionResult GetAllProducts();
and they would both handle GET requests.
If you utilise attribute routing you don't need to.
Have a read of the docs
eg:
The following example maps the CreateBook method to HTTP POST requests.
[Route("api/books")]
[HttpPost]
public HttpResponseMessage CreateBook(Book book) { ... }
Hi I've 2 different get methods with the below signature and my routing is as follows:
[ActionName("Data")]
public Dictionary<int, string> GetData(int ID)
{
}
[ActionName("Name")]
public Dictionary<int, string> GetName(int ID)
{
}
var route = routes.MapRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
But api/{controller}/Data/9 and api/{controller}/Name/10 are both calling the first method GetData.
Can someone please guide me to fix this.
You will have to decorate your controllerclass with the [RoutePrefix("api/ControllerName")] as well as do the following:
[Route("Data")]
public Dictionary<int, string> GetData(int ID)
{
}
[Route("Name")]
public Dictionary<int, string> GetName(int ID)
{
}
var route = routes.MapRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
You can get a little more fancy with the Route attribute.
[Route("Name/{ThisIsMyIdVariableName}"), HttpGet]
public HttpResponseMessage GetName(int ThisIsMyIdVariableName) { /*code here*/ }
Now you can keep the idea of a pretty url without having to use routes.MapRoute and can call the url like domain.com/Name/19. You can use multiple variable names wrapped with {} in the route and keep your urls pretty and restful when passing multiple variables.
This approach is also very useful if you have the need to version your api controller.
I know this should be a comment instead of an answer, but I don't have the required reputation to comment yet, and I'd like to help. Are both of the above methods returning data based on their id (PK) value? The reason both methods are calling the first method in your Web API server is because they're both appending the same URL extension to the server. A quick and easy way to differentiate these methods could be to change one of the 2 to accept a string that you could then convert back to an int. For instance:
//api/Customer/5
public Dictionary<int, string> GetData(int ID)
{
}
//api/Customer?CustID={custID]
public Dictionary<int, string> GetName(string custID)
{
//convert string to int and continue code
}
This might not be best practice, but this is what I've done in the past when needing to differentiate between GET/POST methods in my web api server. For deserializing, you could use a class like JsonConvert if you're using Json, etc. Then your calls to your controller will add the appropriate URL extensions as defined in the comments I left above the methods. Let me know if you'd like additional explanation.
I'm using MongoDB with an ASP.NET Web Api (2) application, and want to accept ObjectId arguments in the Web Api methods.
I've written a custom model binder for the ObjectId type, and it when adding it to the Get method of a controller, everything works.
[Route("{id}")]
public HttpResponseMessage Get(String type, [ModelBinder(typeof(ObjectIdModelBinder))]ObjectId id) {
But I need to do this in several methods and controllers, so I would rather put it somewhere central. I've read that I can register the binder centrally like this:
public static void Register(HttpConfiguration config) {
var provider = new SimpleModelBinderProvider(typeof(ObjectId), new ObjectIdModelBinder());
config.Services.Insert(typeof(ModelBinderProvider), 0, provider);
}
But that's not working!
Any ideas? Not really sure what the config.Services collection should contain, but I'm having a hard time locating the binder I insert.
With WebAPI even if you register a model binder, you still need to attach the [ModelBinder] to the input parameter, you just don't have to specify the type anymore so your method now looks like this:
[Route("{id}")]
public HttpResponseMessage Get(String type, [ModelBinder]ObjectId id) {
If you want to skip adding the attribute every time you declare a parameter of this type, then you have to look at writing a custom IActionValueBinder (which can be made very simple if you just extend the DefaultActionValueBinder) which is the default implementation. You might want to look at this post for pointers:
http://www.strathweb.com/2013/04/asp-net-web-api-parameter-binding-part-1-understanding-binding-from-uri/
I am trying to map /{Controller}/{Variable1}/{Variable2}/{Variable3} to a GET method in controller
public TestController{
public ActionResult Get([FromUrl] Entity instance){}
}
So I need to map variables to the entity.
To put it into an example
/Product/{category}/{filter1}/{filter2}/
Entity
public class ProductSearchRequest
{
public string Category{get;set;}
public string filter1 {get;set;}
public string filter2 {get;set;}
}
Controller
public ProductController: Controller {
public ActionResult GET([FromUri] ProductSearchRequest productSearchRequest){
}
}
[EDITED]
Had to do following changes to get this working
Instead of RouteCollection.MapHttpRoute use HttpConfiguration.Routes.MapHttpRoute as this is API routing not MVC routing.
Inherit controller from ApiController rather than Controller which I was before.
Basically you are not going to be able to do that. Complex types are not compatible with the routing mechanism.
Take a read of this article. But this paragraph explains why the routing mechanism cannot do what you are asking.
A complex type can only bind to the URI through a custom binding. But
in that case, the framework cannot know in advance whether the
parameter would bind to a particular URI. To find out, it would need
to invoke the binding. The goal of the selection algorithm is to
select an action from the static description, before invoking any
bindings. Therefore, complex types are excluded from the matching
algorithm.
Therefore the basic rule is:
For every parameter of the action, if the parameter is taken from the
URI, then the parameter name must be found either in the route
dictionary or in the URI query string. (Optional parameters and
parameters with complex types are excluded.)
Which means you need to define your action like so:
public ActionResult GET(string Category, string filter1, string filter2){
}
And your route template:
/{controller}/{category}/{filter1}/{filter2}/