Actions and QueryString parameters in Web API - asp.net-web-api

I want to have my generic route determine if a query string was passed in the Url like this
http://localhost/query/DailyLogs/1?dateOfLog='1/13/2013'
Here is my current Route definition:
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "query/{controller}/{id}",
defaults: new { id = RouteParameter.Optional}
);
I have read some answers that say to add the dateOfLog value as an optional action on the Route defintion:
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "query/{controller}/{id}/{dateOfLog}",
defaults: new { id = RouteParameter.Optional,
dateOfLog = RouteParameter.Optional }
);
This does not seem to work, maybe I am doing something wrong, I am not sure.
This is how I am currently handling the problem, but I would like to use the ModelBinding power of the Routing Engine:
var queryValue = Request.RequestUri.ParseQueryString();
string dateOfLog = queryValue["dateOfLog"];
Please tell me how to create a Route definition that will use ModelBinding correctly and map my custom url to the controller's parameters.

In the controller action, just include DateTime dateOfLog as a method parameter and continue to use the query string as it will get mapped just fine, Web API will use the correct method overload if it finds it.

Related

Should I follow WebApi route convention?

Should I follow WebApi route convention?
I want to change my default route config (api/{controller}/{id}) to api/{controller}/{action}/{id}
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Is it ok to add {action} placeholder to my route?
What is the pros/cons of default WebApi route?
Yes, you can have action names in your routes and use them.
First, you have to add the following to your WebApiConfig.cs
routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
Be sure to add this before the default route or else it will not work.
Then add ActionName attribute in your controller methods
public class ProductsController : ApiController
{
[HttpGet]
[ActionName("Thumbnail")]
public HttpResponseMessage GetThumbnailImage(int id);
}
Then your route will be /api/Products/Thumbnail
For more info check the Routing by Action Name from here
Be sure to marks as answer if this helped you :)
By using Custom Action Names, you can move beyond the HTTP verbs in your controllers and have customized methods available in the API

Visual Studio Web API: How to change the routing path

I am working on Web Api. By default, it uses the api/{controller}/{id} as a url. I am able to have it to route api/device/{controller}/{id} but this will affect to all Web API route to that path.
But I only want certain controller to be in api/device/{controller}/{id} and the rest will go to another path.
I saw something like RoutePrefix but it doesn't seem to work...
[RoutePrefix (api/data/abc)] where abc is the controller name.
Add the custom route mapping in WebApiConfig.cs file before default route map:
By adding this before, any requests which matches the custom route will be executed, else the other one.
config.Routes.MapHttpRoute(
name: "CustomRoute",
routeTemplate: "api/device/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
If you want to customize the routing with a per controller approach, then you have to use Attribute Routing instead of the standard convention-based routing.
Decorate your controllers with RoutePrefix attribute, and your actions with Route attribute in this way:
[RoutePrefix("api/device/mydevice")]
public class MyDeviceController : ApiController {
[Route("{id}")]
[HttpGet]
public IHttpActionResult Get(int id) {
//DoWork
//...
}
}
And remember to enable attribute routing on the HttpConfiguration object:
config.MapHttpAttributeRoutes();
You may also remove MapHttpRoute method calls if you do not want to allow access to your actions in the standard convention-based way.
More on attribute routing on the official documentation.

Web Api - Swagger documentation error 500

When I access to the swagger url: http://localhost:28483/swagger/ui/index, it generates this error:
500 : undefined http://localhost:28483/swagger/docs/v1
Any ideas?
UPDATED:
See this detail error in firebug:
Not supported by Swagger 2.0: Multiple operations
with path 'api/BimModel' and method 'GET'. See the config setting - \"ResolveConflictingActions\" for
a potential workaround
Swagger might be considering two actions as one operation (like in the case of this common scenario)...
GET api/Products
GET api/Products/{id}
It seems you can use attribute routing to fix this and use these attributes above your actions so swagger will recognize them separately.
[Route("api/Products")]
[Route("api/Products/{id:guid}")]
Have you tried enable this in you swagger config?
c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());
In the controller, it got two different GET operations and it is disallowed by Swagger.
I suggest to either have only single GET operation for each controller or modify the router in WebApiConfig
I had the same issue when mixing attribute routing with default routes.
When I removed the default route the problem went away. The downside is, without the default route defined, I had to add attribute routing to all my controllers.
So from my WebApiConfig I removed:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
And added attribute routing to my controller:
[Route("Session")] // Added this attribute
public async Task<IHttpActionResult> Get()
...
[Route("Session/{id}")] // Added this attribute
public async Task<IHttpActionResult> Get(int id)
In reality I use a [RoutePrefix("Session")] on my Controller and use [Route("")] on my methods, but the result should be the same.
I was getting this error due to the parameter names not matching between the Attribute Routing statement and the method signature.
[HttpGet("{id}")]
public IActionResult Get(string deviceNumber){
...
After changing "{id}" to "{deviceNumber}" it fixed the error.
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);

Adding custom method to WebAPI

I am new to the WebAPI and I got stuck in a problem. I am using API controller which contains two Get methods. One is used for GetAll while other retrieves data on the basis of ID. Now what I want to do is to implement another get method which takes string and returns records. I have made that method and call that it does not work as my route was default API route which was like:
config.Routes.MapHttpRoute(name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } );
When I added a new route to call my custom GetMethod:
config.Routes.MapHttpRoute(name: "Custom", routeTemplate: "api/{controller}/{action}/{id}", defaults: new { id = RouteParameter.Optional } );
I was able to call my custom GetMethod but now I lose the restful method with id parameters. How can I use both methods?
Have you tried using your second route only and call as
api/customer/get/1
api/customer/getall/
api/customer/getmethod/one
In the above, customer is the controller name. You have to replace with yours.
Please check whether you have the routes in the webapiconfig.cs file. Please refer this article for more help.
Better not to change routes.
You can use Action Name to differentiate the calls and can add more functions. Take a look at this answer:
How to add custom methods to ASP.NET WebAPI controller?

Web API route mapping to root

I am setting up an ASP.NET MVC 4 Web API to accept requests from a 3rd party server, and I simply can't figure out how to set the route mappings.
Assume the 3rd party server expects to get responses for:
http://[my_server]/authorize?user=[user name]&session=[session token]&item=[item]
http://[my_server]/release?user=[user name]&session=[session token]
Alternatively, the requests can use a dedicated path, i.e.:
http://[my_server]/***api***/authorize?user=[user name]&session=[session token]&item=[item]
http://[my_server]/***api***/release?user=[user name]&session=[session token]
I would like to be able to support both alternatives.
Additional requests, following the more traditional /controller/id form, should be implemented too, but I'd like to focus on the above (I'm not even sure that Web API is the way to go here).
I have written the following controller:
public class MyController : ApiController
{
[HttpGet]
[ActionName("authorize")]
public string Authorize(string user, string session, string item)
{
...
// return "OK" or "DENY";
}
[HttpGet]
[ActionName("release")]
public string Release(string user, string session)
{
...
return "OK";
}
}
and tried everything I could find in SO and elsewhere in WebAppConfig.Register, but I keep getting a 404 error when I try the request in the browser:
http://localhost:22332/api/authorize?user=ury&session=token&item=an_item
My question is, what do I have to do - specifically in WebAppConfig.Register and in the controller - in order to serve the above requests (assuming my test URL is correct...)?
You're getting tripped up by Web API conventions for matching a controller name in a URL to a controller class.
If the name of your controller is "MyController", then the URL to request is:
http://localhost:22332/api/my/authorize?user=ury&session=token&item=an_item
To support a request w/o the "api" bit in the URL, simply add a second route definition in the Register method of WebApiConfig.cs. Since you're also using "actions" in your URL's, you'd need these two routes:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "RootWebApiRoute",
routeTemplate: "{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
If you want to omit the "api" prefix as well as the controller parameter, I think you need a different route definition. Something like:
public static void Register(HttpConfiguration config) {
config.Routes.MapHttpRoute(
name: "ActionOnlyRoute",
routeTemplate: "{action}",
defaults: new { controller = "My" }
);
}
Here, we'll only look for an "action" in the URL and route everything to your "MyController" class.
After a few hours working on this and with a lot of help from the Route Debugger - Thanks Phil Haack! - I've found both the problem and the solution.
The problem: Route mapping matching is ordered, and RouteTable.Routes, from which the app's route mapping is initialized, contains quite a few of them. The request pattern I was looking for also matched some of these mappings ("authorize" was matched as a controller, for example).
The solution: add "my" route mappings before the default mappings.
Yeah, right...
Since most operations are not supported on HttpRouteCollection, the resulting code is a bit ugly, but it works:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
routesCopy = new RouteBase[RouteTable.Routes.Count];
RouteTable.Routes.CopyTo(routesCopy, 0);
config.Routes.Clear();
config.Routes.MapHttpRoute(name: "AuthorizeWebApiRoute", routeTemplate: "authorize", defaults: new { controller = "My", action = "authorize" });
config.Routes.MapHttpRoute(name: "ReleaseWebApiRoute", routeTemplate: "release", defaults: new { controller = "My", action = "release" });
foreach (var route in routesCopy)
{
RouteTable.Routes.Add(route);
}
}
}
As long as "my" route mappings don't match the default mappings (I made them specific enough not to), I'm all good - I think...

Resources