My WebApi.config looks like this:
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
which means it is configured to support both attribute based routing and conventional routing.
And the sample controller created:
[RoutePrefix("v1")]
public class Values1Controller : ApiController
{
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
[Route("products")]
public string Get(int id)
{
return "value";
}
}
When I invoke http://localhost.domain/api/values1/v1/ the response is value1, value2
But, when I try to invoke http://localhost.domain/api/values1/v1/products/1 (notice the products attribute route on the public string Get(int id) method, the break point is not hit.
In other words, how to call the public string Get(int id) method with the attribute routing Route(products) on top of it?
You use /v1/products?id=10 to hit the action public string Get(int id) or change your route template to be [Route("products/{id}")] to make request like /v1/products/10
Note that attributed controllers/actions cannot be reached by requests which match conventional routes, so that means you cannot do something like api/values/1 to reach public string Get(int id) action
Related
Added a second controller in my WebAPI project and it is nor working completely but the first controller is working as expected
The default URI works for the first controller to return all records:
http://localhost:59654/api/TidalBatch
The second controller does not work and returns the error in question:
http://localhost:59654/api/TidalBatchConsolidated
However, if I pass in {id} for it, it does work for when I use the id (example shown):
http://localhost:59654/api/TidalBatchConsolidated/BAM
Tried modifying the routing addresses
WebAPI config:
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "TidalBatchApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "TidalBatchConsolidatedApi",
routeTemplate: "api/TidalBatchConsolidated/{id}",
defaults: new { id = RouteParameter.Optional }
);
I have 2 controllers, TidalBatchController.cs and TidalBatchConsolidatedController.cs. Both inherit from ApiController class.
Here's an example of my second controller that is not working as expected:
public class TidalBatchConsolidatedController : ApiController
{
public TidalBatchConsolidated GetAll(string id)
{
using (BDW_ProcessingEntities_TidalBatch entities = new BDW_ProcessingEntities_TidalBatch())
{
return entities.TidalBatchConsolidateds.FirstOrDefault(e => e.CompanyAbbr == id);
}
}
}
When I navigate to the base controller in the address it should return the List results in JSON format based on which entity data model is being passed in.
First, the order you register routes is important where more generic routes need to be registered after more specific routes. Secondly you more specific route needs controller in order for it to match.
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "TidalBatchConsolidatedApi",
routeTemplate: "api/TidalBatchConsolidated/{id}",
defaults: new { controller ="TidalBatchConsolidated", id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "TidalBatchApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
The controller also needs to ensure that there is a matching action
For example
public class TidalBatchConsolidatedController: ApiController {
[HttpGet]
public IHttpActionResult Get() {
//...
}
[HttpGet]
public IHttpActionResult Get(string id) {
//...
}
}
Since you have attribute routing enabled with config.MapHttpAttributeRoutes();, you could forego convention based route and use attribute routing on the controller instead
[RoutePrefix("api/TidalBatchConsolidated")]
public class TidalBatchConsolidatedController: ApiController {
//GET api/TidalBatchConsolidated
[Route("")] //Default route
[HttpGet]
public IHttpActionResult GetAll() {
//...
}
//GET api/TidalBatchConsolidated/BAM
[Route("{id}")]
[HttpGet]
public IHttpActionResult Get(string id) {
//...
}
}
Reference Attribute Routing in ASP.NET Web API 2
Working in ASP.NET 4.6 here.
I have a controller:
public class ComputerController : ApiController
{
...
[HttpGet]
[Route("api/computer/ping")]
public IHttpActionResult Ping(int id)
{
return Ok("hello");
}
...
}
Going mostly from this answer (look at MSTdev's answer), I have this in my WebApiConfig.cs:
// So I can use [Route]?
config.MapHttpAttributeRoutes();
// handle the defaults.
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
The route doesn't work. I always get
No HTTP resource was found that matches the request URI
'http://localhost:29365/api/computer/ping'.
This seems like such a simple problem, yet I remain stumped. Any help?
Your route is missing the {id} parameter.
Ex.
[Route("api/category/{categoryId}")]
public IEnumerable<Order> GetCategoryId(int categoryId) { ... }
Your controller should look like this:
public class ComputerController : ApiController
{
...
[HttpGet]
[Route("api/computer/ping/{id}")]
public IHttpActionResult Ping(int id)
{
return Ok("hello");
}
...
}
I'm trying to use RoutePrefix, but ASP.NET is partially ignoring it. Given the following controller (method bodies and class fields removed):
[RoutePrefix("api/users/sharepoint")]
public class SharePointController : ApiController
{
public SharePointController(ISharePointUserRepository repo, IAzureUserRepository otherRepo)
{
}
[HttpGet]
public ExternalUser Get(int id)
{
}
[HttpGet]
public ExternalUser Get(Guid guid)
{
}
[HttpGet]
public IEnumerable<ExternalUser> Get()
{
}
[HttpGet]
public ExternalUser Get(string username)
{
}
[HttpGet]
public IEnumerable<ExternalUser> GetByPersonalEmail(string email)
{
}
[HttpGet]
[Route("GetWithDifferences")]
public IEnumerable<ExternalUser> GetWithDifferences()
{
}
[HttpGet]
[Route("GetUnique")]
public IEnumerable<ExternalUser> GetUnique()
{
}
[HttpPost]
[Route("search")]
public IEnumerable<ExternalUser> Search([FromBody] ExternalUserSearchModel model)
{
}
I get the following API (via host/help):
SharePoint
GET api/users/sharepoint/GetWithDifferences
GET api/users/sharepoint/GetUnique
POST api/users/sharepoint/search
GET api/SharePoint/{id}
GET api/SharePoint?guid={guid}
GET api/SharePoint
GET api/SharePoint?username={username}
GET api/SharePoint?email={email}
Which, when tested, works as advertised.
As you can see the RoutePrefix is ignored when I don't specify a [Route(..)] for the exposed methods. However I want default actions on GET, so I don't want to specify any additional routing for these.
How can I enforce the RoutePrefix, or, alternatively, how can I maintain default GET and POST behavior with [Route(..)]?
RoutePrefixAttribute alone does not define any route, it will just add the chosen prefix to any route defined in the class decorated with such attribute.
You need to make a choice then: use only attribute routing (and define a route for each method) so you may benefit from RoutePrefix, or keeping the code as is, and define another convention routing which complies with your URIs.
Sample using attribute routing:
[HttpGet]
[Route("{id:int}"]
public ExternalUser Get(int id)
{ }
[HttpGet]
[Route(""]
public ExternalUser Get(Guid guid)
{ }
[HttpGet]
[Route("")]
public IEnumerable<ExternalUser> Get()
{ }
[HttpGet]
[Route("")]
public ExternalUser Get(string username)
{ }
And here it is another example for the second approach:
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "MyCustomApi",
routeTemplate: "api/users/sharepoint/{id}",
defaults: new { controller = "SharePoint", id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
I have the following routes mapped in my WebApiConfig:
config.Routes.MapHttpRoute(name: "WithActionApi", routeTemplate: "api/{controller}/{action}/{id}");
config.Routes.MapHttpRoute(name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { action = "DefaultAction", id = RouteParameter.Optional });
An in my controller I have:
[HttpGet]
public ProspectAddressResult Addresses(int id)
{
...
return result;
}
[ActionName("DefaultAction")]
public ProspectDetail Get(int id)
{
...
return prospect;
}
I'm finding that i'm getting the Get route mapped twice once as api/prospect/1 and api/prospect/Get/1. What am I doing wrong as I would expect the route to only be mapped once i.e. api/prospect/1 or is that not possible (or relevant)?
Why not just install web api 2 through nuget. Then you can use the Route and RoutePrefix properties on your actions/controllers to specify your routes.
You should then never get duplicate mapping
Here's an example of how your api controller would be set up:
[RoutePrefix("api/prospect")]
public class ProspectController: ApiController
{
[Route("{id}")]
public ProspectDetail Get(int id)
{
...
return prospect;
}
}
Your route for that would then be api/prospect/1
I am using the default routing setup in WebApiConfig (MVC 4) but for some reason I am getting unexpected results.
If I call /api/devices/get/ it hits the Get function but the Id is "get" rather than 1. If I call /api/devices/get/1 I get a 404. I also want to be able to support multiple parameters i.e.
public Device[] Get(int? page, int? pageSize) // for multiple devices
The route
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional });
}
And my API:
public class DevicesController : ApiController
{
EClient client = new EClient();
// GET api/devices/5
public Device Get(string id)
{
return client.GetDeviceBySerial(id);
}
}
id in the controller parameter should be integer:
public Device Get(int id)
{
return client.GetDeviceBySerial(id);
}
if you need to pass in string, or other prams, just use quesry string:
public Device Get(int id, string pageSize)
{
return client.GetDeviceBySerial(id);
}
the above can be called as:
/api/devices/1
or
/api/devices/?id=1&pageSize=10
Note: you do not need to specify method name. Web API will judge that on the basis of HTTP Verb used. If its a GET request, it will use the Get method, if its a POST request, then it will use Post method ... and so on.
You can change the above behavior, but I guess you mentioned that you want to keep usign the default Routing ... so I am not covering that.