Dealing with multiple GET actions Web API - asp.net-mvc

I am new to WEB API and trying to set up routing for multiple GET actions.
Controller Code
// Get api/values
public IEnumerable<tblUser> Get()
{
//whatever
}
// Get api/values/action
[ActionName("GetByQue")]
public IEnumerable<tblQue> GetQue()
{
//whatever
}
// Get api/values/action
[ActionName("GetUserScore")]
public IEnumerable<tblScore> GetScore(string user)
{
//whatever
}
Config
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional}
);
config.Routes.MapHttpRoute(
name: "DefaultActionApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { action = "GetByQue" }
);
config.Routes.MapHttpRoute(
name: "DefaultStringApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { action = "GetUserScore" }
);
When I try with http://localhost:54118/api/remote/GetByQue URL getting this error
{
"Message": "The request is invalid.",
"MessageDetail": "The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int32' for method 'System.String Get(Int32)' in 'HydTechiesApi.Controllers.HydTechiesApiController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter."
}
is my routing wrong? Any help would be valuable as I am not able to find a solution.

You should add {action} to routeTemplate instead of {id} in the second configuration
config.Routes.MapHttpRoute(
name: "DefaultActionApi",
routeTemplate: "api/{controller}/{action}",
defaults: new { action = "GetByQue" }
);
also you can try to use route attribure on action :
[ActionName("GetByQue")]
[Route("api/remote/GetByQue")]
public IEnumerable<tblQue> GetQue()
{
//whatever
}
or change order(the second configuration and first configuration) of configuration in WebApiConfig.cs

You made a couple of mistakes in your route. As your example code below:
config.Routes.MapHttpRoute(
name: "DefaultActionApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { action = "GetByQue" });
//url: http://localhost:54118/api/remote/GetByQue
(1). routeTemplate: "api/{controller}/{id}". you specify your route has an id and it is not Optional. So your URL must have id. That's what your error showed.you can handle with the issue like below:
defaults: new { id = RouteParameter.Optional }
(2). defaults: new { action = "GetByQue" }); you did not say any thing about action in your routeTemplate. your defaults about action, which does not have any meaning.
(3). From your route, your URL should look like http://localhost:54118/api/remote/5 , you should not get mutiple get method by your route.
There are some solutions, which you may use:
(1). change route like below:
config.Routes.MapHttpRoute(
name: "DefaultActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
please add [HttpGet] in each method just in cause.
[HttpGet]
public IEnumerable<tblUser> Get()
{......}
[HttpGet]
[ActionName("GetByQue")]
public IEnumerable<tblQue> GetQue()
{......}
[HttpGet]
[ActionName("GetUserScore")]
public IEnumerable<tblScore> GetScore(string user)
{......}
Now you can use URL like http://localhost:54118/api/remote/GetByQue
Very Useful Tips: Using [Route("")] tag to specify parameters
**You also have to change Route like above (1)
in Controller, please specify request method to make sure the get method
[HttpGet]
[ActionName("GetUserScore")]
[Route("api/remote/GetScore/{user}")]
public IEnumerable<tblScore> GetScore(string user)
{.....}
//URL: http://localhost:54118/api/remote/GetScore/user

Related

How to Implement Method in Web API

I have a web API controller:
public List<mytable> Get(string filter1, string filter2){
...
}.
I need to create another method named
List<drpSubject> GetSubject(){
...
}.
My WebApiConfig.cs like this
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
// routeTemplate: "api/{controller}/{id}",
routeTemplate: "{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute("DefaultApiWithAction", "{controller}/{action}");
}
But now I get error message "Multiple actions were found that match the request..." . It only works when there is one Get(). How to create web api that can take multiple actions? Thanks.
There are multiple ways to configure routing.
You could change
"{controller}/{id}",
to
"{controller}/{action}/{id}",
More at Routing by Action Name.

Not understanding the correct way to route my action

This is driving me crazy. This is my controller action:
[HttpGet]
public List<Product> RetrieveByIds(int[] ids)
{
Product p = new Product();
//return p.RetrieveByIds(ids);
return new List<Product>();
}
This is my routing file:
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "ActionApiMultipleIds",
routeTemplate: "api/{controller}/{action}/{ids}",
defaults: new { ids = RouteParameter.Optional }
);
}
And I keep getting this error when I try to test it using the RestConsole:
{
"Message": "The request is invalid.",
"MessageDetail": "The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int32' for method 'System.Collections.Generic.List`1[GrownHome.Web.Models.Product] RetrieveAllByUserId(Int32)' in 'GrownHome.Web.Controllers.Api.ProductController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter."
}
I keep starring at my screen fiddling around with the code and I am getting nowhere. I don't have to explain to you guys how frustrating this is :)))
I run into these kind of routing issues every now and again and I can't seem to get my head around these kind of issues. Some insight would be appriciated very much!
Thanks!
The framework tries to match a URL to a route based on the order the routes are listed in your Register method, so your third route will actually never be used because the first two routes are the default "catch-all" routes. This is why you are receiving the error saying that parameter id is null.
The general rule of thumb is to place more specific routes before more general routes to ensure the routing framework matches a URL to a route correctly. So in your case your custom route needs to be moved ahead of the two default routes:
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "ActionApiMultipleIds",
routeTemplate: "api/{controller}/{action}/{ids}",
defaults: new { ids = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
Your third route will never be reaced because the one before is catching all the request that have the same formatting. You probably need to add some constraints.

ASP.NET Web API Routing in ApiController

I've been struggling with my routing for some time now and after a few days of trying to Google the solution without luck, I'm hoping someone may be able to shine some light on my problem.
I have the following routes in my WebApiConfig:
config.Routes.MapHttpRoute(
name: "AccountStuffId",
routeTemplate: "api/Account/{action}/{Id}",
defaults: new { controller = "Account", Id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "AccountStuffAlias",
routeTemplate: "api/Account/{action}/{Alias}",
defaults: new { controller = "Account", Alias = RouteParameter.Optional }
);
and the following controller methods:
[HttpGet]
public Account GetAccountById(string Id)
{
return null;
}
[HttpGet]
public Account GetAccountByAlias(string alias)
{
return null;
}
If I call:
/API/Account/GetAccountById/stuff then it properly calls GetAccountById.
But if I call /API/Account/GetAccountByAlias/stuff then nothing happens.
Clearly order matters here because if I switch my declarations of my routes in my WebApiConfig, then /API/Account/GetAccountByAlias/stuff properly calls GetAccountByAlias, and /API/Account/GetAccountById/stuff does nothing.
The two [HttpGet] decorations are part of what I found on Google, but they don't seem to resolve the issue.
Any thoughts? Am I doing anything obviously wrong?
Edit:
When the route fails, the page displays the following:
<Error>
<Message>
No HTTP resource was found that matches the request URI 'http://localhost:6221/API/Account/GetAccountByAlias/stuff'.
</Message>
<MessageDetail>
No action was found on the controller 'Account' that matches the request.
</MessageDetail>
</Error>
You should be able to just have the following route:
config.Routes.MapHttpRoute(
name: "AccountStuffId",
routeTemplate: "api/Account/{action}/{Id}",
defaults: new { controller = "Account", Id = RouteParameter.Optional }
);
and do the following for your actions:
[HttpGet]
public Account GetAccountById(string Id)
{
return null;
}
[HttpGet]
public Account GetAccountByAlias([FromUri(Name="id")]string alias)
{
return null;
}
Is there a reason you need to be declaring two different routes?
Look at the guide at: http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api
They have one default route and going by the example all you need in your config is
routes.MapHttpRoute(
name: "API Default",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);

MVC 4 Web API Routing issues

This is my first rodeo with MVC Web API and I'm having some issues understanding the routing aspects. I would like to have a uri template similar to thise:
http://google.com/api/AzureQueue - GET for all items in the queue
http://google.com/api/AzureQueue/DeviceChart/ - GET returns devices and processing time for agent
http://google.com/api/{controller}/{id} <-- default
http://google.com/api/{controller}/{chartType}/{id} where ID is optional
where I'm struggling is:
1. what the french toash do I put in the WebApiConfig.cs file
2. do I need to do anthing special in my controller eg. specifiy NonActions & Actions, Action Names, etc
Any help is appreciated
You are almost there. The default route (in WebApiConfig.cs looks like this:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
There's one very important caveat: the routes are examined in the order that they are declared with the first matching one being used, so the default route needs to go last.
With that out of the way, you need to make a decision, do you want the calls for various chart types to go to one action, or many?
For one action:
WebApiConfig.cs
config.Routes.MapHttpRoute(
name: "AzureQueue",
routeTemplate: "api/AzureQueue/{chartType}/{id}",
defaults: new { controller = "AzureQueue", id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
AzureQueueController.cs
public class AzureQueueController : ApiController
{
public string Get(string chartType)
{
return "chart = " + chartType;
}
public string Get(string chartType, int id)
{
return "chart = " + chartType + ",id = " + id.ToString();
}
}
There are two things to notice here. In the anonymous class assigned to defaults, the value for controller decides which controller to route the request to. This can either be in the route template, or simply defined in the class. Also, a request of type Get is automatically sent to an action that starts with Get and has the arguments in the Url that match the template (there are two different cases since id is optional).
This would be my preferred way to go unless the business logic for various charts is different.
On the other hand you could specify this:
WebApiConfig.cs
config.Routes.MapHttpRoute(
name: "AzureQueue",
routeTemplate: "api/AzureQueue/{action}/{id}",
defaults: new { controller = "AzureQueue", id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Since I'm using the word action is the template, this will get interpreted as an action name.
AzureQueueController.cs
[HttpGet]
public string DeviceChart()
{
return "chart = DeviceChart" ;
}
[HttpGet]
public string DeviceChart(int id)
{
return "chart = DeviceChart" + ",id = " + id.ToString();
}
Here there is no string argument, that part of the url is being used to decide which action (public method) to use. Also, since the action names don't start with Get, I need to add an attribute [HttpGet] for each method to mark them as being able to receive GET requests.
Good luck with your project.

ASP.NET MVC 4 - API Routing - Specifying Action in the URL

I have a web service written in the ASP.NET MVC 4 framework. Its basic CRUD operations map well to REST verbs, but I have one action that I need to add that does not.
What is the correct way to specify the ability to sometimes pass an ID at the end of the URL that you know will be an Integer type and sometimes have it be an "action" a String type followed by a slash and an int ID? Need help with correct Router map.
Thoughts? Note: I'm more concerned with WebAPI then being 100% REST.
example URLs
// GET list of widgets
http://somedomain.com/api/widget
// GET specific widget
http://somedomain.com/api/widget/1
// POST - take special action on a specific widget - promote
http://somedomain.com/api/widget/promote/1
Would this be the correct way to handle it?
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
routes.MapHttpRoute(
name: "DefaultApiExtended",
routeTemplate: "api/{controller}/{action}/{id}"
);
I'd try putting the second pattern (an "action" a String type followed by a slash and an int ID) in at a higher priority than (pass an ID at the end of the URL that you know will be an Integer type).
routes.MapHttpRoute(
name: "ActionWithId",
routeTemplate: "api/widget/{action}/{id}"
);
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Your action and your ID are not optional as you're always expecting them. If there is no match, the DefaultApi rule should take over processing.
EDIT:
Just thought, if you do want it to be "RESTy" then you could always switch the ID, so for this widget, with this id, perform this action.
routes.MapHttpRoute(
name: "ActionWithId",
routeTemplate: "api/widget/{id}/{action}"
);
Take a look at http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api - specifically section "Routing by Action Name"
Slight change recomended from
http://somedomain.com/api/widget/promote/1
To
http://somedomain.com/api/widgets/1/promote
(Use the plural if possible widget*s*)
Your Controller will need to look like this:
public class WidgetsController : ApiController
{
[HttpPost]
/// POST api/widgets/1/promote
public void Promote(int id)
{
}
// GET api/widgets/1
public string Get(int id)
{
return "value";
}
}
And the routes:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
routes.MapHttpRoute(
name: "DefaultPromoteActionApi",
routeTemplate: "api/{controller}/{id}/{action}",
defaults: new { action = "Promote" }
);
}
Take a look at AttributeRouting; it should be able to accomplish this with ease.

Resources