Single controller with multiple GET methods in ASP.NET Web API
This is the best way I have found to support extra GET methods and support the normal REST methods as well. Add the following routes to your WebApiConfig:
routes.MapHttpRoute("DefaultApiWithId", "Api/{controller}/{id}", new { id = RouteParameter.Optional }, new { id = @"\d+" });
routes.MapHttpRoute("DefaultApiWithAction", "Api/{controller}/{action}");
routes.MapHttpRoute("DefaultApiGet", "Api/{controller}", new { action = "Get" }, new { httpMethod = new HttpMethodConstraint(HttpMethod.Get) });
routes.MapHttpRoute("DefaultApiPost", "Api/{controller}", new {action = "Post"}, new {httpMethod = new HttpMethodConstraint(HttpMethod.Post)});
I verified this solution with the test class below. I was able to successfully hit each method in my controller below:
public class TestController : ApiController
{
public string Get()
{
return string.Empty;
}
public string Get(int id)
{
return string.Empty;
}
public string GetAll()
{
return string.Empty;
}
public void Post([FromBody]string value)
{
}
public void Put(int id, [FromBody]string value)
{
}
public void Delete(int id)
{
}
}
I verified that it supports the following requests:
GET /Test
GET /Test/1
GET /Test/GetAll
POST /Test
PUT /Test/1
DELETE /Test/1
Note That if your extra GET actions do not begin with 'Get' you may want to add an HttpGet attribute to the method.
How to use multiple GET action in a WebApi?
Either change template to routeTemplate: "api/{controller}/{action}/{studentName}"
, and leave methods as it
public static void Register(HttpConfiguration config) {
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{studentName}"
);
}
OR
leave template as is, ie: "api/{controller}/{action}/{id}"
and change method parameter to (string id,.......)
[HttpGet]
public List<Student> UpdateEmail(string id, string Email) { ... }
OR
You could also forego convention-based routing and use attribute routing
[RoutePrefix("api/EmployeeApi")]
public class EmployeeApiController : ApiController
{
//GET api/EmployeeApi
[HttpGet]
[Route("")]
public List<Student> GetAllStudents() { ... }
//GET api/EmployeeApi/EmailChange/foo/foo@email.com
[HttpGet]
[Route("EmailChange/{studentName}/{email}")]
public List<Student> EmailChange(string studentName, string email) { ... }
//GET api/EmployeeApi/AddressChange/foo/China
[HttpGet]
[Route("AddressChange/{studentName}/{address}")]
public List<Student> AddressChange(string studentName, string Address) { ... }
}
Routing with multiple Get methods in ASP.NET Web API
From here Routing in Asp.net Mvc 4 and Web Api
Darin Dimitrov has posted a very good answer which is working for me.
It says...
You could have a couple of routes:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "ApiById",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional },
constraints: new { id = @"^[0-9]+$" }
);
config.Routes.MapHttpRoute(
name: "ApiByName",
routeTemplate: "api/{controller}/{action}/{name}",
defaults: null,
constraints: new { name = @"^[a-z]+$" }
);
config.Routes.MapHttpRoute(
name: "ApiByAction",
routeTemplate: "api/{controller}/{action}",
defaults: new { action = "Get" }
);
}
}
How to put multiple GET methods in Web API2 controller?
Enable attribute routing in WebApiConfig.cs before convention-based routes.
config.MapHttpAttributeRoutes();
Next update controller to use routing attributes. (note the route prefix)
[RoutePrefix("api/NCT_ProcessSettings")]
public class NCT_ProcessSettingsController : ApiController {
//GET api/NCT_ProcessSettings
[HttpGet]
[Route("")]
public IEnumerable<Process_Settings> Get() { ... }
//GET api/NCT_ProcessSettings/5
[HttpGet]
[Route("{id:int}")]
public HttpResponseMessage Get(int id) { ... }
//GET api/NCT_ProcessSettings/GetGlobalSettings
[HttpGet]
[Route("GetGlobalSettings")]
public IEnumerable<NCT_Process_Settings> GetGlobalSettings() { ... }
}
Read up more documentation here Attribute Routing in ASP.NET Web API 2
Multiple get methods in a .Net WebApi OData Controller
OData Routes do not support overloads OOTB which is effectively what you are describing here, two GET Collection
methods, but you want one of them to have the same route as the GET Resource
method.
The first question is why do you want to have the same route pointing to two different methods, especially when one of the routes returns a single resource, and the other will return a collection.
Built-In OData Routing Conventions
The built in conventions will route known URLs to methods that match the expected signatures. All other routes need to be registered with the Edm Model either as discrete Functions or Actions. You could also implement your own routes
The arguments are more or less the same if you had intended the
Get(string key)
to return a single item or a collection. There is still a clash between a default route and 2 different methods to handle the request for that route. There is a direct duplicate isssue on SO for this type of issue: OData route exception
In OData we make a distinction between the key
of a resource, that fits into the default ~/Controller(key)
route and effectively all other routes. For all other non-default routes, we simply need to declare them in the OData EdmModel to make them valid
There are other possible solutions like Implementing your own version of the DefaultODataPathHandler as explained here or a custom IODataRoutingConvention as explained here.
However I find it best to try and stick to the OData standard conventions as much as possible, so when faced with a routing issue where there can be defined a simple mapping between the OData Conventional way and our business need, AND you want to support this syntax in a global sense, then you can use a simple url rewrite module.
- Declare your custom method endpoint as a separate Function in the Edm Model.
- Create a Url Rewrite to map between legacy routes and the OData routes.
Declare a Custom Function in the Edm Model:
To access your custom function via the Edm Model, there are two changes we need to make:
change the method name to something other than
Get
. In this example we will use the termSearch
[HttpGet]
[EnableQuery]
public IQueryable<Product> Search(string key)
{
return _service.GetByFilter(key);
}modify your builder fluent notation:
ODataModelBuilder builder = new ODataConventionModelBuilder();
var products = builder.EntitySet<Product>("Products");
products.EntityType.Collection.Function(nameof(ProductsController.Search))
.ReturnsCollectionFromEntitySet<Facility>("Products")
.Parameter<string>("key");
config.Routes.MapODataServiceRoute("odata", "api/odata", builder.GetEdmModel());NOTE: The name of the route must match the name of the method for this configuration to work, to enforce this
nameof(ProductsController.Search)
was used however,"Search"
is all that is necessary.
The final URL that will match this template:
GET: ~/api/odata/Products/Search(key='Foo')
Url Rewrite Module
public class ODataConventionUrlRewriter : OwinMiddleware
{
/// <summary>
/// Create a Url Rewriter to manipulate incoming OData requests and rewrite or redirect to the correct request syntax
/// </summary>
/// <param name="next"></param>
public ODataConventionUrlRewriter(OwinMiddleware next)
: base(next)
{
}
/// <summary>
/// Process the incoming request, if it matches a known path, rewrite accordingly or redirect.
/// </summary>
/// <param name="context">OWin Request context to process</param>
/// <returns>Chains the next processor if the url is OK or rewritten, otherwise the response is to redirect</returns>
public override async Task Invoke(IOwinContext context)
{
// Match ANY /Controller(NonNumeric)
// Rewrite to /Controller/Search(key='NonNumeric')
var regex = new System.Text.RegularExpressions.Regex(@"\(([^\d=\'\(]+)\)$");
match = regex.Match(context.Request.Path.Value);
if (match != null && match.Success)
{
// We have to use redirect here, we can't affect the query inflight
context.Response.Redirect($"{context.Request.Uri.GetLeftPart(UriPartial.Authority)}{regex.Replace(context.Request.Path.Value, $"/Search(key='{match.Groups[1].Value}')")}");
}
else
await Next.Invoke(context);
}
}
Finally, in the StartUp.cs or where your OWin context is configured add this module into the pipeline before the OData config:
public void Configuration(IAppBuilder app)
{
...
// Rewrite URLs
app.Use(typeof(ODataConventionUrlRewriter));
...
// Register routes
config.MapHttpAttributeRoutes();
ODataModelBuilder builder = new ODataConventionModelBuilder();
var products = builder.EntitySet<Product>("Products");
products.EntityType.Collection.Function(nameof(ProductsController.Search))
.ReturnsCollectionFromEntitySet<Facility>("Products")
.Parameter<string>("key");
config.Routes.MapODataServiceRoute("odata", "api/odata", builder.GetEdmModel());
...
// Start Web API
app.UseWebApi(config);
}
Now the final Url that is supported for our custom Function is this:
GET: ~/api/odata/Products(Foo)
Try to use the conventions when you can, you have chosen OData for a reason, if you do use a rewrite module, try to be as specific in your conditional logic as you can be to avoid breaking other standard routes that might be in use.
I try to reserve rewrite solutions for common legacy query routes.
Multiple GET methods in a Web API Controller
I would need to see the calling code and route config to be certain but my guess is that you may be using restful routing. Switch to using a query string with named parameters and all of your methods should work:
http://api/yourcontroller?id=something
http://api/yourcontroller?mode=somethingelse
http://api/yourcontroller?department=adepartment&location=alocation
The default route template configuration understands id. You may see this in the App_Start folder in the WebApiConfig static class method Register.
This is the default:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Based on this default, the action method parameter (id) is set as part of the route data which is why the second action method in the controller code you listed above would work. You would not be able to use template routing or attribute routing to set the value in get for multiple single paramter get methods in the same controller because it would create an ambiguous condition.
You may want to review the details on parameter binding at the following link. Binding can be a little tricky at times in Web Api 2 because the model binders and formatters included by default do a lot of work behind the scenes.
http://www.asp.net/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api
Related Topics
How to Hide Close Button in Wpf Window
Notify Observablecollection When Item Changes
How to Pass Ienumerable List to Controller in MVC Including Checkbox State
How to Ignore Unknown Enum Values During JSON Deserialization
Determine the Number of Lines Within a Text File
Observablecollection That Also Monitors Changes on the Elements in Collection
What How to Use for Good Quality Code Coverage for C#/.Net
How to Store User Settings for a .Net Application
Way to Have String.Replace Only Hit "Whole Words"
Does C# 8 Support the .Net Framework
Word Wrap for a Label in Windows Forms
Optimal Linq Query to Get a Random Sub Collection - Shuffle
Shouldserialize*() VS *Specified Conditional Serialization Pattern
C# Get Thumbnail from File via Windows API
Detect Target Framework Version at Compile Time
Wait Until File Is Completely Written
I Want Await to Throw Aggregateexception, Not Just the First Exception