Web Api 2 set route to ApiController method dynamically at runtime - c#

I would like to set the route to a method within an ApiController dynamically. The below shows my TokenController:
public class TokenController : ApiController
{
[Route("api/token/{grantType}")]
[RequireHttps]
public IHttpActionResult Post(string grantType)
{}
}
I am thinking of using dependency injection as follows:
public class TokenController : ApiController
{
public TokenController(ITokenService tokenService)
{
//configure route "api/token/{grantType}" using tokenService?
}
[Route("api/token/{grantType}")]
[RequireHttps]
public IHttpActionResult Post(string grantType)
{}
}
Or do I need to do this in App_Start using the HttpConfiguration object?
How would I do this?

Found my answer. I will configure the endpoint route with HttpConfiguration:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "API TokenEndpoint",
routeTemplate: "services/newtoken/{grantType}",
defaults: new { controller = "Token" action="Post"},
constraints: null);
}
}

Related

Attribute Routing for querystring

I have below route URL:-
www.domanname.com/subroute/GetInfo?param1=somestring&param2=somestring
I have function in webapi as:-
public class HomeController : ApiController
{
public object GetInfo(string param1,string param2)
{}
}
To apply route:-
[RoutePrefix("subroute")]
public class HomeController : ApiController
{
[Route("GetInfo?param1={param1:string}&param2={param2:string}")]
public object GetInfo(string param1,string param2)
{}
}
But after applying above URL:-
www.domanname.com/subroute/GetInfo?param1=somestring&param2=somestring
It is not able to find that URL
How can I design this particular route?
You need to modify the routes a bit as query string are not normally used in attribute routes. They tend to be used for inline route parameters.
[RoutePrefix("subroute")]
public class HomeController : ApiController {
//Matches GET subroute/GetInfo?param1=somestring&param2=somestring
[HttpGet]
[Route("GetInfo")]
public IHttpActionResult GetInfo(string param1, string param2) {
//...
}
}
Also
Enabling Attribute Routing
To enable attribute routing, call MapHttpAttributeRoutes during configuration. This extension method is
defined in the System.Web.Http.HttpConfigurationExtensions class.
using System.Web.Http;
namespace WebApplication
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API routes
config.MapHttpAttributeRoutes();
// Other Web API configuration not shown.
}
}
}
Reference Attribute Routing in ASP.NET Web API 2
[RoutePrefix("subroute")]
public class HomeController : ApiController {
[HttpGet]
[Route("GetInfo/{param1}/{param2}")]
public IHttpActionResult GetInfo(string param1, string param2) {
//...
}
}
Calling
//Matches GET subroute/GetInfo/Hello/World

ASP.NET Web API Attribute Routing To Wrong Action

I have applied attribute routing on my controller and it'srouting to wrong action. I don't know where I am getting it wrong.
Here is my controller:
using System.Collections.Generic;
using System.Web.Http;
using System.Web.Http.Description;
using System.Linq;
using System;
namespace Iboo.API.Controllers
{
public class ClientsController : ApiController
{
private readonly IClientRepository _repository;
public ClientsController(IClientRepository repository)
{
_repository = repository;
}
// GET: api/Clients
[Route("api/v1/clients")]
public IEnumerable<Client> Get()
{
//code
}
// GET: api/Clients/5
[HttpGet]
[ResponseType(typeof(Client))]
[Route("api/v1/clients/get/{id}")]
public IHttpActionResult GetClientById(int id)
{
//code
}
// GET: api/Clients/5
[HttpGet]
[ResponseType(typeof(string))]
[Route("api/v1/clients/{id}/emailid")]
public IHttpActionResult GetClientEmailId(int id)
{
//code
}
}
}
I am specifically interested in the GetClientEmailId method. Below is my WebApiConfig
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
var container = new UnityContainer();
container.RegisterType<IClientRepository, ClientRepository>(new
HierarchicalLifetimeManager());
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/v1/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
My Global.asax.cs is as follows
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
In the browser If I type http://localhost:54919/api/v1/clients/?id=1/getemailid it's taking me to http://localhost:54919/api/v1/clients which is not what I want.
If I try http://localhost:54919/api/v1/clients/1/getemailid I am getting a 404 error.
I am not sure as to what I'm getting wrong.
You are calling the wrong URLs according to routes on the actions. you get 404 because the URL you call does not match to any of the route templates you have on your actions
[RoutePrefix("api/v1/clients")]
public class ClientsController : ApiController {
//...other code removed for brevity
[HttpGet]
[Route("")] //Matches GET api/v1/Clients
public IHttpActionResult Get() {
//code
}
[HttpGet]
[ResponseType(typeof(Client))]
[Route("{id:int}")] //Matches GET api/v1/Clients/5
public IHttpActionResult GetClientById(int id) {
//code
}
[HttpGet]
[ResponseType(typeof(string))]
[Route("{id:int}/emailid")] //Matches GET api/v1/Clients/5/emailid
public IHttpActionResult GetClientEmailId(int id) {
//code
}
}
Take note of the expected URLs in the comments
You should also read up on Attribute Routing in ASP.NET Web API 2 to get a better understanding of how to do attribute-routing.
You can try using the route prefix on the controller.
[RoutePrefix("api/v1/clients")]
public class ClientsController : ApiController
{
// GET: api/Clients/5
[ResponseType(typeof(string))]
[Route("{id:int}/emailid"),HttpGet]
public IHttpActionResult GetClientEmailId(int id)
{
//code
}
}
You said:
In the browser If I type http://localhost:54919/api/v1/clients/?id=1/getemailid it's taking me to http://localhost:54919/api/v1/clients which is not what I want.
From the way your routes are set up, it looks like you need to go to http://localhost:54919/api/v1/client/1/emailid to get to the route you want
To explain the difference, when you call http://localhost:54919/api/v1/clients/?id=1/getemailid the route that would match that is something like:
[Route("api/v1/clients")]
public IHttpActionResult GetClientEmailId(string id)
{
//code
}
because you've added the id parameter as a querystring parameter. In this case, the id argument would have a value of 1/getemailid which doesn't make much sense.
by using the route parameters (by replacing ?id=1/getemailid with 1/emailid) you will actually match the route you want to

WebAPI No action was found on the controller by passing parameters

HomeController.cs
class HomeController : ApiController
{
[HttpGet]
public IHttpActionResult GetData(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentNullException("username can not be empty");
}
return Ok("Test Done");
}
}
StartUp.cs
public void Configuration(IAppBuilder appBuilder)
{
HttpConfiguration config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "GetData",
routeTemplate: "V2/{controller}/{name}",
defaults: new { controller = "Home", action = "GetData" }
);
appBuilder.UseWebApi(config);
}
Getting error:
{
"Message": "No HTTP resource was found that matches the request URI 'http://localhost:4057/V2/Home/GetData/'.",
"MessageDetail": "No type was found that matches the controller named 'Home'."
The problem here is simple.
The access modifier of your class is very restrictive
class HomeController : ApiController
{
The default access modifier is internal, meaning it is not publicly accessible.
Try to change the access modifier to public to expose the service publicly.
public class HomeController : ApiController
{
I can replicate your issue using postman and my existing web API.
Error from my postman:
Try changing the method name to Get (more about http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api).
Also setting the attribute routing might help if you do not want to change the method name (http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2).
As Sherlock mentioned, the API Controller must be public. However, consider using attribute routing over convention routing since its much less error prone:
HomeController.cs:
[RoutePrefix("V2/Home")]
public class HomeController : ApiController
{
[HttpGet]
[Route("{name}", Name = "GetData")]
public IHttpActionResult GetData(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentNullException("username can not be empty");
}
return Ok("Test Done");
}
}
Startup.cs:
public void Configuration(IAppBuilder appBuilder)
{
HttpConfiguration config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
appBuilder.UseWebApi(config);
}

ASP.Net Web API RoutePrefix partially ignored

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 }
);
}

No action was found on the controller 'Tickets' that matches the name 'CloseTicket'

I am writing a webapi project and am receiving the error No action was found on the controller 'Tickets' that matches the name 'TestMethod' whenever I try any of the methods in the controller.
None of the actions in the below controller are working.
I have been googling and have already setup WebApiConfig.cs to add action to the routing.
Is there something else I am missing.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional });
config.EnableSystemDiagnosticsTracing();
}
}
Controller
public class TicketController : ApiController
{
[HttpPost]
public static ServiceResponse<string> IssueTicket([FromBody]ServiceRequest<TicketRequest> request)
{
return ServiceResponse<string>.WithPayload(ticketID);
}
[HttpPost]
public static ServiceResponse<bool> CheckTicketExist([FromBody]ServiceRequest<string> request)
{
return ServiceResponse<bool>.WithPayload(doesExist);
}
[HttpPost]
public static ServiceResponse<bool> CloseTicket([FromBody]ServiceRequest<string> request)
{
return ServiceResponse<bool>.WithPayload(result);
}
[HttpPost]
public static bool TestMethod([FromBody]string test)
{
return true;
}
}
Web API doesn't consider static methods while selecting actions.
Reference
Which methods on the controller are considered "actions"? When selecting an action, the framework only looks at public instance methods on the controller.
Remove the static keyword from your controller.

Categories