Get route by name - c#

I'm working on an asp.net web api with hypermedia. Now I'm making a link creator that creates a link to a resource exposed by a controller. It should support attribute routes, which I've solved with reflection, but also mapped routes specified in Owin.AppBuilder:
public void Configuration(IAppBuilder appBuilder)
{
var config = new HttpConfiguration();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "{controller}/{id}",
defaults: new { controller = "Home", id = RouteParameter.Optional }
);
// ...
}
I could use the UrlHelper class for this, but it depends on the current request, and the link I'm creating could be to another controller, and thus have no relationship with the current request. So what I need is to load the route configuration data for the route named DefaultApi. Is it any way to do this?

If you can use Route Attribute, you can name your route via a name property, what I did is I defined my routes in RoutesHelper and when I define my controller route, I reference this constant, and when I want to use CreatedAtRoute for example I reference the same routeName and pass the parameters to construct the route.
So let's say that my controller is called PeopleController, then I would define my controller as:
[Route("api/people/{id:int:min(1)?}", Name = RoutesHelper.RouteNames.People)]
public class PeopleController : ApiController
{
// controller code here
}
where RoutesHelper is like this:
public static class RoutesHelper
{
public struct RouteNames
{
public const string People = "People";
// etc...
}
}
Now in my Post method for example I use CreateAtRoute like this:
[HttpPost]
[ResponseType(typeof(PersonDto))]
public async Task<IHttpActionResult> AddAsync([FromBody] personDto dto)
{
// some code to map my dto to the entity using automapper, and save the new entity goes here
//.
//.
// here, I am mapping the saved entity to dto
var added = Mapper.Map<PersonDto>(person);
// this is where I reference the route by it's name and construct the route parameters.
var response = CreatedAtRoute(RoutesHelper.RouteNames.People, new { id = added.Id }, added);
return response;
}
Hope this helps.

Related

No action was found on the controller ApiController

For my API, I want to be able to handle scenarios where the call is made using an incorrect URL (i.e. URL that does not match any controller or action.)
For this I have implemented routing so that it matches any route:
config.Routes.MapHttpRoute(
name: "CatchAll",
routeTemplate: "{*any}",
defaults: new { controller = "Error", action = "Handler" }
);
And I have implemented the controller - action as follows:
[Route("api/v1/Handler")]
[ActionName("Handler")]
public HttpResponseMessage Get() {...}
When I give an incorrect URL, I get the message:
No action was found on the controller 'Error' that matches the name 'Handler'
Not sure where the mistake is.
Simple approach:
You need to match the action name. So either rename Get to Handler in the ErrorController and remove attributes as they will conflict with conventions you mapped .
public HttpResponseMessage Handler(string any) {...}
or change action from Handler to Get in the Catch all MapHttpRoute
config.Routes.MapHttpRoute(
name: "CatchAll",
routeTemplate: "{*any}",
defaults: new { controller = "Error", action = "Get" }
);
Not so simple approach:
Here is how I handled the same thing in my web api using attribute routing and inheritance.
First I created a base api controller that will handler unknown actions.
public abstract class WebApiControllerBase : ApiController {
[Route("{*actionName}")]
[AcceptVerbs("GET", "POST", "PUT", "DELETE")]
[AllowAnonymous]
[ApiExplorerSettings(IgnoreApi = true)]
public virtual HttpResponseMessage HandleUnknownAction(string actionName) {
var status = HttpStatusCode.NotFound;
var message = "[Message placeholder]";
var content = new { message = message, status = status};
return Request.CreateResponse(status, content);
}
}
My Api controllers all inherit from this base controller.
[RoutePrefix("api/customers")]
public class CustomersApiController : WebApiControllerBase {...}
Given that your question included RouteAttribute you should already be using MapHttpAttributeRoutes in your web api config.
But there's more. To get the framework to recognize the inherited attributes you need to override the DefaultDirectRouteProvider as outlined here
and used in an answer here
WebAPI controller inheritance and attribute routing
public class WebApiCustomDirectRouteProvider : DefaultDirectRouteProvider {
protected override System.Collections.Generic.IReadOnlyList<IDirectRouteFactory>
GetActionRouteFactories(System.Web.Http.Controllers.HttpActionDescriptor actionDescriptor) {
// inherit route attributes decorated on base class controller's actions
return actionDescriptor.GetCustomAttributes<IDirectRouteFactory>(inherit: true);
}
}
and apply that to the web api configuration.
// Attribute routing. (with inheritance)
config.MapHttpAttributeRoutes(new WebApiCustomDirectRouteProvider());
Hope this helps

Pass Parameters in OData WebApi Url

Using Web Api I have an OData EndPoint which can return Products from a database.
I have multiple databases with similar schemas, and want to pass a parameter in the URL to identify which database the Api should use.
Current Odata Endpoint:
http://localhost:62999/Products
What I want:
http://localhost:62999/999/Products
In the new Url, I pass in 999 (the database ID).
The database ID is intended to specify which database to load the product from. For example localhost:62999/999/Products('ABC123') would load product 'ABC123' from database 999, but the next request, localhost:62999/111/Products('XYZ789') would load the product 'XYZ789' from database 111.
The Url below works, but I don't like it.
localhost:62999/Products('XYZ789')?database=111
Here is the code for the controller:
public class ProductsController : ErpApiController //extends ODataController, handles disposing of database resources
{
public ProductsController(IErpService erpService) : base(erpService) { }
[EnableQuery(PageSize = 50)]
public IQueryable<ProductDto> Get(ODataQueryOptions<ProductDto> queryOptions)
{
return ErpService.Products(queryOptions);
}
[EnableQuery]
public SingleResult<ProductDto> Get([FromODataUri] string key, ODataQueryOptions<ProductDto> queryOptions)
{
var result = ErpService.Products(queryOptions).Where(p => p.StockCode == key);
return SingleResult.Create(result);
}
}
I use Ninject to resolve which implementation of IErpService to inject into the controller by binding to a service provider:
kernel.Bind<IErpService>().ToProvider(new ErpServiceProvider());
And the ErpServiceProvider inspects the url to identify the databaseId required by this request:
public class ErpServiceProvider : Provider<IErpService>
{
protected override IErpService CreateInstance(IContext context)
{
var databaseId = HttpContext.Current.Request["database"];
return new SageErpService(new SageContext(GetDbConnection(databaseId)));
}
}
The bit I am stuck on is how to define the Url parameter in the OData route config.
Normal WebApi routes can have parameters defined as follows:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
But how do I define the parameters in the OData route config?
ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<ProductDto>("Products");
builder.EntitySet<WorkOrderDto>("WorkOrders");
config.MapODataServiceRoute(
routeName: "ODataRoute",
routePrefix: null,
model: builder.GetEdmModel());
Is this even where I should be defining the Url parameters?
I have also thought about using a Message Handler but I am not certain how this can be implemented either.
UPDATE
This question is trying to do the same thing as me: How to declare a parameter as prefix on OData
But it is not clear how the parameter is to be read from the url.
var databaseId = HttpContext.Current.Request["database"]; returns null currently. Even after updating the route config to the following:
public static void Register(HttpConfiguration config)
{
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "ErpApi",
routeTemplate: "{database}/{controller}"
);
// Web API configuration and services
ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<ProductDto>("Products");
builder.EntitySet<WorkOrderDto>("WorkOrders");
config.MapODataServiceRoute(
routeName: "ODataRoute",
routePrefix: "{company}/",
model: builder.GetEdmModel());
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
I've encountered a solution to pass dynamic parameter on OData, not sure if is the right one.
I've used this solution on a certain context, where the dynamic parameter was just to authenticate the client, but I think you can solve your problem in a similar way.
Problem: You wan't to pass a dynamic value at the URL request example: http://localhost:62999/{dynamicValue}/Products('ABC123'), but the ODataRouting will never route correctly, because of that extra /{dynamicValue} and the ODataControler "will not hit".
Using ApiController you could made a custom routing, but at OData you can't (at least I didn't found an easy way to do it, probably you had to made your own or extend the OData routing convention).
So as alternative solution:
If every request will have a dynamicValue for example: "http://localhost:62999/{dynamicValue}/Products" do the following steps:
Before routing the request Extract the dynamicValue (In my case, I've used an IAuthenticationFilter to intercept the message before it was routed, since the parameter was related with authorization, but maybe for your case it makes more sense to use another thing)
Store the dynamicValue (somewhere on the request context)
Route the ODataController without the {dynamicValue}.
/Products('ABC123') instead of /{dynamicValue}/Products('ABC123')
Here is the code:
// Register the ServiceRoute
public static void Register(HttpConfiguration config)
{
// Register the filter that will intercept the request before it is rooted to OData
config.Filters.Add(CustomAuthenticationFilter>()); // If your dynamic parameter is related with Authentication use an IAuthenticationFilter otherwise you can register a MessageHandler for example.
// Create the default collection of built-in conventions.
var conventions = ODataRoutingConventions.CreateDefault();
config.MapODataServiceRoute(
routeName: "NameOfYourRoute",
routePrefix: null, // Here you can define a prefix if you want
model: GetEdmModel(), //Get the model
pathHandler: new CustomPathHandler(), //Using CustomPath to handle dynamic parameter
routingConventions: conventions); //Use the default routing conventions
}
// Just a filter to intercept the message before it hits the controller and to extract & store the DynamicValue
public class CustomAuthenticationFilter : IAuthenticationFilter, IFilter
{
// Extract the dynamic value
var dynamicValueStr = ((string)context.ActionContext.RequestContext.RouteData.Values["odatapath"])
.Substring(0, ((string)context.ActionContext.RequestContext.RouteData.Values["odatapath"])
.IndexOf('/')); // You can use a more "safer" way to parse
int dynamicValue;
if (int.TryParse(dynamicValueStr, out dynamicValue))
{
// TODO (this I leave it to you :))
// Store it somewhere, probably at the request "context"
// For example as claim
}
}
// Define your custom path handler
public class CustomPathHandler : DefaultODataPathHandler
{
public override ODataPath Parse(IEdmModel model, string serviceRoot, string odataPath)
{
// Code made to remove the "dynamicValue"
// This is assuming the dynamicValue is on the first "/"
int dynamicValueIndex= odataPath.IndexOf('/');
odataPath = odataPath.Substring(dynamicValueIndex + 1);
// Now OData will route the request normaly since the route will only have "/Products('ABC123')"
return base.Parse(model, serviceRoot, odataPath);
}
}
Now you should have the information of the dynamic value stored at the context of the request and OData should route correctly to the ODataController. Once your there at your method, you can access the request context to get information about the "dynamic value" and use it to choose the correct database
These APIs have likely changed quite a bit since this original post. But I was able to accomplish this by making the default data route prefix contain the parameter:
b.MapODataServiceRoute("odata", "odata/{customerName}", GetEdmModel());
In my scenario, I have a database per customer, so I want the route prefix to accept the name of the customer (database):
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
app.UseMvc(b =>
{
b.MapODataServiceRoute("odata", "odata/{customerName}", GetEdmModel());
});
}
Example controller (note customerName parameter on the action):
public class BooksController : ODataController
{
private IContextResolver _contextResolver;
public BooksController(IContextResolver contextResolver)
{
_contextResolver = contextResolver;
}
[EnableQuery]
public IActionResult Get(string customerName)
{
var context = _contextResolver.Resolve(customerName);
return Ok(context.Books);
}
}
Then you can hit the URL like: https://localhost/odata/acmecorp/Books

Routing Controllers asp.net mvc

In our application users can create objects and give them a name. This data is stored in the database with a typeId. I have an api controller that can currently be called, with a name like
api/data
This controller has the basic Get, Put, Post and Delete methods.
Now i would like users to be able to call this controller by object type. So if they setup 3 different objects and call them company, contact and project, i would like them to be able to call the api/data controller, using these names, similar to this
api/company
api/contact
api/project
I do not know these object types unitl runtime, so i cannot code these manually.
Now i also have other controllers that i do not want effected by this, so if i have a normal controller called page, then i still want to be able to call it by
api/page
Is there a way i can do this? Almost intercept a call to a controller, see if the name is equal to a name i have in the database, and if so pass it to the data controller, else let it process as normal.
As far as you are using api controller, you may play with RoutePrefix and Route annotations. For example:
[RoutePrefix("api")]
public class DataController : ApiController
{
[Route("company")]
public IEnumerable<CompanyViewModel> GetCompanies()
{
....
}
}
But this may conflict with default route definition. If so, try to specifically define your routes for this controller:
config.Routes.MapHttpRoute(
name: "CompanyApi",
routeTemplate: "api/company/{id}",
defaults: new {controller = "DataController" , id = RouteParameter.Optional}
);
You would need to add a route for each object in your db, or rather each object not from the db, and have all others route to a default path. You may want to look at using regex with your routing, if you are able to prefix the object names at all.
I think you should
HomeController:
ActionResult Index(string name)
{
// check if name exists in DB
}
I didn't see other way to do what you want even by editing routes... or maybe if you do routes with DB but well...
Web API routing uses fall-through logic, so start off by declaring your known routes first, then have a generic handler to catch all other requests and handle them accordingly. For example, use the following code when setting up your HttpConfiguration:
// Define all known routes using attributes.
config.MapHttpAttributeRoutes();
// Generic route to handle all other requests
config.Routes.MapHttpRoute(
name: "DynamicRoute",
routeTemplate: "api/{dataType}",
defaults: new { controller = "data" }
);
Then in your DataController you can have actions that take the dataType as a parameter:
public async Task<IHttpActionResult> Get(string dataType, ...)
{ ... }
I have managed to do what i want by using RoutePrefix attribute on controller with an extra MapRoute, with a constraint applied to it.
Here is the code, if someone else is trying to do something similar
First add a routePrefix to the controller that you want to call
[RoutePrefix("api/{myCustomName}")]
public MyController : ApiController {
}
Now in my WebApiConfig file, i needed to add the following lines
// Web API routes
config.MapHttpAttributeRoutes();
// handle calling MyController using custom name
config.Routes.MapHttpRoute(
name: "ModuleDataApi",
routeTemplate: "api/{myCustomName}/{id}",
defaults: new { controller = "MyController" },
constraints: new { moduleType = new MyCustomRouteConstraint() }
);
Now create the constraint that checks if there is a valid match. In my code, i have populated a text file with the details if the file doesnt exist, and i will delete/refresh this when a new custom name is added. I believe this would perform better then a database query everytime, since i would probably have about 20 name/value pairs in here at the very most.
So create your constraint like so
public class MyCustomRouteConstraint : IHttpRouteConstraint
{
private DbContext _context;
private static object fileLockObject = new object();
public MyCustomRouteConstraint ()
{
_context = new DbContext();
}
public bool Match(System.Net.Http.HttpRequestMessage request, IHttpRoute route, string parameterName, IDictionary<string, object> values, HttpRouteDirection routeDirection)
{
var appDataPath = System.Web.Hosting.HostingEnvironment.MapPath("~/App_Data/CustomNameRouteCache.txt");
// lock variable so no 2 or more threads can try to create file at once
lock (fileLockObject)
{
var list = new List<MyRouteModel>();
// check if our cache file exists, if not, lets create it
if (!File.Exists(appDataPath))
{
using (var writer = new System.IO.StreamWriter(appDataPath))
{
// get all modules
var names = _context.CustomNames.ToList();
foreach (var item in names)
{
list.Add(Mapper.Map<MyRouteModel>(item));
}
// serialize the list to the file in json format
var json = JsonConvert.SerializeObject(list);
// write to file
writer.Write(json);
};
}
else
{
// open file and get json contents
using (var reader = new System.IO.StreamReader(appDataPath))
{
var json = reader.ReadToEnd();
list = JsonConvert.DeserializeObject<List<MyRouteModel>>(json);
}
}
// lets search our list and see if this value is in our modules
foreach (var item in list)
{
if (item.Name.Equals(values[parameterName].ToString()))
{
return true;
}
}
}
return false;
}
}
Now this seems to work perfectly, i can call MyController using the following paths, or any others that are created by the user at runtime
api/company/
api/contact/
api/project/
api/whateveryouwant/
Hope this help someone else trying to do something similar

how to configure this route for webapi?

I have a controller to GET a resource(say, Employee), which has two properties (say, CategoryId and DepartmentId). I need to configure the routes to support the following URLs:
~/api/employees/1234 [to get the employee with employeeId=1234]
~/api/employees [to get all the employees]
~/api/employees?departmentid=1 [to get all the employees with departmentId=1]
and the controller code looks like this:
public IEnumerable<Employee> Get()
{
....
}
public IEnumerable<Employee> Get(int employeeId, int departmentId = -1, int categoryId = -1)
{
.....
}
How to configure routes for this controller?
Thanks
for any querystyring parameter, there's nothin to do on the routing side: just have the controller method parmater matching with the qs parameter name(case insensitive.)
If instead your method parameter refers to a uri segment, method paramenter name has to match with route parameter/segment between curly brackets
routes.MapHttpRoute(
name: "API Default",
routeTemplate: "/api/{controller}/{employeeId}",
defaults: new { id = RouteParameter.Optional }
);
that means you need a controller with following method
public class EmployeesController : ApiController
{
public IEnumerable<Employee> Get(int employeeId){...}
}
keep in mind that, unless you use action, on your controller you can only one method per http verb.
In other word your sample having 2 method for the verb get won't work unless you use explicit action for both of them.
Have you looked at using Attribute routing? We use Attribute routing extensively now, to the point that we have completely got rid of the default /controller/action type routes that we had with MapHttpRoute.
Instead we decorate our controllers as follows. Firstly we create a route prefix for the controller so that we know what the base route is that we require
/// <summary> A controller for handling products. </summary>
[RoutePrefix("api/purchasing/locations/{locationid}/products")]
public class ProductsController : PurchasingController
{
Then for each action in the controller we decorate it as follows:
[Route("", Name = "GetAllProducts")]
public IHttpActionResult GetAllProducts(int locationid, ODataQueryOptions<FourthPurchasingAPI.Models.Product> queryOptions)
{
IList<Product> products = this.GetProducts(locationid);
and
[Route("{productid}", Name = "GetProductById")]
public IHttpActionResult GetProduct(int locationid, string productid)
{
Product product = this.GetProductByID(locationid, productid);
so a call to api/purchasing/locations/1/products/ will resolve to the route named "GetAllProducts"
and a call to api/purchasing/locations/1/products/1 will resolve to the route named "GetProductById"
You could then create another GetProduct action in your controller with the same signature, just set the attribute route appropriately, e.g.
[Route("/department/{departmentId}", Name = "GetAllProductsForDepartment")]
public IHttpActionResult GetAllProductsWithDeptId(int locationid, int departmentId)
{
IList<Product> products = this.GetProducts(locationid, departmentId);
Now a call to api/purchasing/locations/1/products/department/1234 will resolve to the route named "GetAllProductsForDepartment"
I know that this example is using Web Api 2, but look at this link for Attribute Routing in Web Api. It should be exactly the same instead you will be returning something other than an IHttpActionResult.

Custom method names in ASP.NET Web API

I'm converting from the WCF Web API to the new ASP.NET MVC 4 Web API. I have a UsersController, and I want to have a method named Authenticate. I see examples of how to do GetAll, GetOne, Post, and Delete, however what if I want to add extra methods into these services? For instance, my UsersService should have a method called Authenticate where they pass in a username and password, however it doesn't work.
public class UsersController : BaseApiController
{
public string GetAll()
{
return "getall!";
}
public string Get(int id)
{
return "get 1! " + id;
}
public User GetAuthenticate(string userName, string password, string applicationName)
{
LogWriter.Write(String.Format("Received authenticate request for username {0} and password {1} and application {2}",
userName, password, applicationName));
//check if valid leapfrog login.
var decodedUsername = userName.Replace("%40", "#");
var encodedPassword = password.Length > 0 ? Utility.HashString(password) : String.Empty;
var leapFrogUsers = LeapFrogUserData.FindAll(decodedUsername, encodedPassword);
if (leapFrogUsers.Count > 0)
{
return new User
{
Id = (uint)leapFrogUsers[0].Id,
Guid = leapFrogUsers[0].Guid
};
}
else
throw new HttpResponseException("Invalid login credentials");
}
}
I can browse to myapi/api/users/ and it will call GetAll and I can browse to myapi/api/users/1 and it will call Get, however if I call myapi/api/users/authenticate?username={0}&password={1} then it will call Get (NOT Authenticate) and error:
The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int32' for method 'System.String Get(Int32)' in 'Navtrak.Services.WCF.NavtrakAPI.Controllers.UsersController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.
How can I call custom method names such as Authenticate?
By default the route configuration follows RESTFul conventions meaning that it will accept only the Get, Post, Put and Delete action names (look at the route in global.asax => by default it doesn't allow you to specify any action name => it uses the HTTP verb to dispatch). So when you send a GET request to /api/users/authenticate you are basically calling the Get(int id) action and passing id=authenticate which obviously crashes because your Get action expects an integer.
If you want to have different action names than the standard ones you could modify your route definition in global.asax:
Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { action = "get", id = RouteParameter.Optional }
);
Now you can navigate to /api/users/getauthenticate to authenticate the user.
This is the best method I have come up with so far to incorporate extra GET methods while supporting 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.
I am days into the MVC4 world.
For what its worth, I have a SitesAPIController, and I needed a custom method, that could be called like:
http://localhost:9000/api/SitesAPI/Disposition/0
With different values for the last parameter to get record with different dispositions.
What Finally worked for me was:
The method in the SitesAPIController:
// GET api/SitesAPI/Disposition/1
[ActionName("Disposition")]
[HttpGet]
public Site Disposition(int disposition)
{
Site site = db.Sites.Where(s => s.Disposition == disposition).First();
return site;
}
And this in the WebApiConfig.cs
// this was already there
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
// this i added
config.Routes.MapHttpRoute(
name: "Action",
routeTemplate: "api/{controller}/{action}/{disposition}"
);
For as long as I was naming the {disposition} as {id} i was encountering:
{
"Message": "No HTTP resource was found that matches the request URI 'http://localhost:9000/api/SitesAPI/Disposition/0'.",
"MessageDetail": "No action was found on the controller 'SitesAPI' that matches the request."
}
When I renamed it to {disposition} it started working. So apparently the parameter name is matched with the value in the placeholder.
Feel free to edit this answer to make it more accurate/explanatory.
Web Api by default expects URL in the form of api/{controller}/{id}, to override this default routing. you can set routing with any of below two ways.
First option:
Add below route registration in WebApiConfig.cs
config.Routes.MapHttpRoute(
name: "CustomApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Decorate your action method with HttpGet and parameters as below
[HttpGet]
public HttpResponseMessage ReadMyData(string param1,
string param2, string param3)
{
// your code here
}
for calling above method url will be like below
http://localhost:[yourport]/api/MyData/ReadMyData?param1=value1&param2=value2&param3=value3
Second option
Add route prefix to Controller class and Decorate your action method with HttpGet as below.
In this case no need change any WebApiConfig.cs. It can have default routing.
[RoutePrefix("api/{controller}/{action}")]
public class MyDataController : ApiController
{
[HttpGet]
public HttpResponseMessage ReadMyData(string param1,
string param2, string param3)
{
// your code here
}
}
for calling above method url will be like below
http://localhost:[yourport]/api/MyData/ReadMyData?param1=value1&param2=value2&param3=value3
In case you're using ASP.NET 5 with ASP.NET MVC 6, most of these answers simply won't work because you'll normally let MVC create the appropriate route collection for you (using the default RESTful conventions), meaning that you won't find any Routes.MapRoute() call to edit at will.
The ConfigureServices() method invoked by the Startup.cs file will register MVC with the Dependency Injection framework built into ASP.NET 5: that way, when you call ApplicationBuilder.UseMvc() later in that class, the MVC framework will automatically add these default routes to your app. We can take a look of what happens behind the hood by looking at the UseMvc() method implementation within the framework source code:
public static IApplicationBuilder UseMvc(
[NotNull] this IApplicationBuilder app,
[NotNull] Action<IRouteBuilder> configureRoutes)
{
// Verify if AddMvc was done before calling UseMvc
// We use the MvcMarkerService to make sure if all the services were added.
MvcServicesHelper.ThrowIfMvcNotRegistered(app.ApplicationServices);
var routes = new RouteBuilder
{
DefaultHandler = new MvcRouteHandler(),
ServiceProvider = app.ApplicationServices
};
configureRoutes(routes);
// Adding the attribute route comes after running the user-code because
// we want to respect any changes to the DefaultHandler.
routes.Routes.Insert(0, AttributeRouting.CreateAttributeMegaRoute(
routes.DefaultHandler,
app.ApplicationServices));
return app.UseRouter(routes.Build());
}
The good thing about this is that the framework now handles all the hard work, iterating through all the Controller's Actions and setting up their default routes, thus saving you some redundant work.
The bad thing is, there's little or no documentation about how you could add your own routes. Luckily enough, you can easily do that by using either a Convention-Based and/or an Attribute-Based approach (aka Attribute Routing).
Convention-Based
In your Startup.cs class, replace this:
app.UseMvc();
with this:
app.UseMvc(routes =>
{
// Route Sample A
routes.MapRoute(
name: "RouteSampleA",
template: "MyOwnGet",
defaults: new { controller = "Items", action = "Get" }
);
// Route Sample B
routes.MapRoute(
name: "RouteSampleB",
template: "MyOwnPost",
defaults: new { controller = "Items", action = "Post" }
);
});
Attribute-Based
A great thing about MVC6 is that you can also define routes on a per-controller basis by decorating either the Controller class and/or the Action methods with the appropriate RouteAttribute and/or HttpGet / HttpPost template parameters, such as the following:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc;
namespace MyNamespace.Controllers
{
[Route("api/[controller]")]
public class ItemsController : Controller
{
// GET: api/items
[HttpGet()]
public IEnumerable<string> Get()
{
return GetLatestItems();
}
// GET: api/items/5
[HttpGet("{num}")]
public IEnumerable<string> Get(int num)
{
return GetLatestItems(5);
}
// GET: api/items/GetLatestItems
[HttpGet("GetLatestItems")]
public IEnumerable<string> GetLatestItems()
{
return GetLatestItems(5);
}
// GET api/items/GetLatestItems/5
[HttpGet("GetLatestItems/{num}")]
public IEnumerable<string> GetLatestItems(int num)
{
return new string[] { "test", "test2" };
}
// POST: /api/items/PostSomething
[HttpPost("PostSomething")]
public IActionResult Post([FromBody]string someData)
{
return Content("OK, got it!");
}
}
}
This controller will handle the following requests:
[GET] api/items
[GET] api/items/5
[GET] api/items/GetLatestItems
[GET] api/items/GetLatestItems/5
[POST] api/items/PostSomething
Also notice that if you use the two approaches togheter, Attribute-based routes (when defined) would override Convention-based ones, and both of them would override the default routes defined by UseMvc().
For more info, you can also read the following post on my blog.
See this article for a longer discussion of named actions. It also shows that you can use the [HttpGet] attribute instead of prefixing the action name with "get".
http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api
Web APi 2 and later versions support a new type of routing, called attribute routing. As the name implies, attribute routing uses attributes to define routes. Attribute routing gives you more control over the URIs in your web API. For example, you can easily create URIs that describe hierarchies of resources.
For example:
[Route("customers/{customerId}/orders")]
public IEnumerable<Order> GetOrdersByCustomer(int customerId) { ... }
Will perfect and you don't need any extra code for example in WebApiConfig.cs.
Just you have to be sure web api routing is enabled or not in WebApiConfig.cs , if not you can activate like below:
// Web API routes
config.MapHttpAttributeRoutes();
You don't have to do something more or change something in WebApiConfig.cs. For more details you can have a look this article.
Just modify your WebAPIConfig.cs as bellow
Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { action = "get", id = RouteParameter.Optional });
Then implement your API as bellow
// GET: api/Controller_Name/Show/1
[ActionName("Show")]
[HttpGet]
public EventPlanner Id(int id){}

Categories

Resources