How to Implement Method in Web API - asp.net-mvc

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.

Related

Dealing with multiple GET actions Web API

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

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.

MVC 3 Web API Routing Not Working

I'm fighting issues with routing in an MVC 3 Web API. It seems like it should be pretty simple, but I'm not making any headway.
My error is:
<Error>
<Message>The request is invalid.</Message>
<MessageDetail>
The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int32' for method 'Boolean NewViolationsPublished(Int32)' in 'BPA.API.Controllers.CacheManagementController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.
</MessageDetail>
</Error>
My RegisterRoutes is such:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapHttpRoute(
name: "NukeAllItemsFromCache",
routeTemplate: "api/CacheManagement/NukeAllItemsFromCache");
routes.MapHttpRoute(
name: "ControllerAndAction",
routeTemplate: "api/{controller}/{action}"
);
routes.MapHttpRoute(
name: "ControllerAndActionAndId",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional, action = "Get" },
constraints: new { id = #"^\d+$" } // Only integers
);
routes.MapHttpRoute(
name: "ControllerAndId",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional },
constraints: new { id = #"^\d+$" } // Only integers
);
}
And my controller is (i took out the code for ease of reading):
public class CacheManagementController : ApiController
{
public CacheManagementController()
[HttpGet]
public bool NewViolationsPublished(int id)
[HttpGet]
public bool IsCached(CachedItemLabels label, int clientId)
[HttpGet]
public void RemoveItemFromCache(int clientId, CachedItemLabels cacheLabel, string test)
[HttpGet]
public string NukeAllItemsFromCache()
}
I get the error when I try to call:
http://localhost/api/CacheManagement/NukeAllItemsFromCache
TIA
Are you sure you're defining your routes in the right place? Web API routes should be configured using System.Web.Http.GlobalConfiguration.Configuration.Routes.MapHttpRoute (normally in a WebApiConfig.cs file in App_Start), as shown on http://www.asp.net/web-api/overview/extensibility/configuring-aspnet-web-api, which is different from the route configuration used by vanilla MVC.
I think you may just be hitting the default Web API route, which is api/{controller}/{id}, with ID optional. The action is determined by the HTTP verb, which in this case is GET, and all of your methods match that by virtue of the [HttpGet].
Edit: example
Global.asax
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
GlobalModifiers.ApplyGlobalConfiguration(this, true);
}
WebApiConfig.cs
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "action-specific",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
I beleave your problem is in this route:
routes.MapHttpRoute(
name: "ControllerAndActionAndId",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional, action = "Get" },
constraints: new { id = #"^\d+$" } // Only integers
);
You specify id as an optional parameter and at the same time constrain it to actually be a non-nullable number. If /api/CacheManagement/NukeAllItemsFromCache is an exception, then add additional route for it, otherwise ease a constraint to something like #"^\d*$" if possible.
You are missing the default action for the route you configured:
routes.MapHttpRoute(
name: "NukeAllItemsFromCache",
routeTemplate: "api/CacheManagement/NukeAllItemsFromCache");
should be
routes.MapHttpRoute(
name: "NukeAllItemsFromCache",
routeTemplate: "api/CacheManagement/NukeAllItemsFromCache",
defaults: new { action = "NukeAllItemsFromCache" }
);
You tried to put parameter id as nullable like:
[HttpGet]
public bool NewViolationsPublished(int? id)
or
[HttpGet]
public bool NewViolationsPublished(Nullable<Int32> id)
Maybe this works...
Double check that your controller extends ApiController rather than Controller
public class CacheManagementController : ApiController
rather than
public class CacheManagementController : Controller

WebApi does not accept post (404 error)

I have a MVC4 web project with WebAPI enabled.
I want to post data to the API controller but the post is not working, I am getting a 404 every time (breakpoint inside the Add method is not hit)....
Here is the code:
public class IncidentSessionLogController : ApiController
{
[HttpPost]
public void Add(MyInputDTO inputData)
{
}
}
I use action based routing:
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "ControllerAndAction",
routeTemplate: "api/{controller}/{action}");
config.Routes.MapHttpRoute(
name: "ControllerAndActionAndId",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional });
}
I use Fiddler to post the data:
http://localhost:42901/api/IncidentSessionLog/Add
My site is hosted in IIS express for development currently, I add the JSON object literal in the request body in Fiddler.
Fixed it, thanks to my collegue !
The MVC routing(which I forgot to post originally), was interfering with the API routing, so I have to add a constraint to the MVC routing :
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Game", action = "Start", id = UrlParameter.Optional },
constraints: new { controller = #"^((?!(api)).)*$" });

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