I have read many links over the internet about web api versioning and every blog post or resource I have found is very clear about how to implement this feature.
However, unfortunately, I have to implement this feature for an API which is already in production and that does not respect any of the solutions I have read until now.
This API has a base address like http://api.server.com and handle more than one resource. These resource all have been routed by ASP.NET attribute routing like {resource}/{id}. For example
POST http://api.server.com/peoples/new
GET http://api.server.com/peoples/5
Actually the web api configuration is very simple like the following
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
I have been assigned the task to reenginer this api and would like to create and implement a new versioning scheme that sits side by side to the existing one.
I would like to use the url versioning method in a way that next realease will have the version in the url like
POST http://api.server.com/{version}/peoples
GET http://api.server.com/{version}/peoples/5
and implement a better adherence to restful recommendations in this new version (like removing the verb from the url for example).
How can I obtain this? Could you give some recommendations, examples or link to read which explain how to implement this feature and leave the actual version working?
Related
We have been using HttpConfiguration.Routes.MapHttpRoute with routes that use URLs as parameters and it has been working fine for years.
//load image from remote server
config.Routes.MapHttpRoute(
name: "remote-image",
routeTemplate: "rimage/{*path}",
defaults: new { controller = "WebApiClient", action = "rimage" }
);
Recently we upgraded few 3rd party NuGet packages that also upgraded dependency packages. After that this particular route template is not responding the requests that contain period.
For instance this works: https://my.domain.com/rimage/remote/domain/name/some-image
But this does not:
https://my.domain.com/rimage/remote/domain/name/some-image.jpg
What can I add to the template to make sure period can be used?
We have many web api solution with 2 projects. One project sets up the web api config stuff and the other project contains the controllers.
Each web api config is setup with this:
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
As we use Route/Prefix-Attributes on every controller/action I asked myself does the config.Routes.MapHttpRoute method call still take effect?
Actually as soon as I annotate a Route Attribute on the controller at least I overwrite the convention behavior of the web api. Thus the method call is obsolete.
Is that correct? Or is there still something to consider, because I want to remove this method call in every project.
The convention-based route will still be applied. Your attribute-based routes will take precedence (because they are configured first) but if an action method has both matching attribute routes and conventional routes, both routes will map to the action in question.
If you want to use exclusively attribute-based routing, removing the convention-based route mapping is probably a wise move in order to prevent unexpected behaviour (i.e. exposing actions under unintended routes).
Of course, you will want to be sure that you're not inadvertently relying on convention-based routing anywhere first!
I'm really new to WebApi and I've been reading information about it but I don't know how to start my application.
I already had a project with many WFC services with .Net 3.5. So, I updated my project to 4.5.1. Then I created a controller with the Visual Studio 2012 Wizard. Then, when the controller is created, I see a class as a template with the get, post, put, delete methods. So I place my post method and finally I want to test the service with a HttpClient.
I tried to apply the solution in green from the following forum:
How to post a xml value to web api?
I'm gonna receive a XML string with the structure of a Contract model.
I run my project into Visual Studio Development Server.
But I have troubles with URL to test my service.
I saw many pages where people do something like this http://localhost:port/api/contract. But I don't still know how it works. So how can I do to test my service? What is it my path or url to test my service?
WebApi, like MVC, is all based on the routing. The default route is /api/{controller}/{id} (which could, of course, be altered). This is generally found in the ~/App_Start/WebApiConfig.cs file of a new project, but given you're migrating you don't have it most likely. So, to wire it up you can modify your Application_Start to include:
GlobalConfiguration.Configure(WebApiConfig.Register);
Then, define that class:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
If you created a TestController controller and wanted to make a POST request to the instance running locally, you'd access http://localhost:12345/api/Test/ (with the appropriate verb). 12345 would be the local port that Visual Studio is using to host your service (and can be found by viewing the project's properties, then navigating to the "Web" tab).
Having said that, testing is probably best performed on the the project (without making an external call). There are several posts on the subject, but generally come down to something like the following:
[TestMethod]
public void Should_Return_Single_Product()
{
// Arrange
var repo = new FakeRepository<Widget>();
var controller = new WidgetController(repo);
var expected = repo.Find(1);
// Act
var actual = controller.GetWidget(1) as OkNegotiatedContentResult<Widget>;
// Assert
Assert.IsNotNull(actual);
Assert.AreEqual(expected.Id, actual.Content.Id);
}
In my MVC and WebAPI application I see two different ways of doing routes.
One for MVC which calls RegisterRoutes and passes RouteTable.Routes
One for Web API which calls CustomizeConfig and passes GlobalConfiguration.Configuration.
For WebAPI:
WebApiConfig.CustomizeConfig(GlobalConfiguration.Configuration);
public static void Register(System.Web.Http.HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: ApiControllerOnly,
routeTemplate: "api/{controller}");
}
For MVC:
RouteConfig.RegisterRoutes(RouteTable.Routes);
public static void RegisterRoutes(System.Web.Routing.RouteCollection routes)
{
routes.MapRoute("DefaultRedirect",
"",
new { controller = "Home", action = "Index" });
}
Can someone explain if there is any difference in me registering my routes in one or the other method calls? Also why is it done this way with one using.
MVC routes register with ASP.NET (system.web) route collection. Web API however is designed to run either in IIS on top of system.web or as a self host without changing the code.
Hence Web API has a different registration mechanism, where it can use the system.web routing under the hood, or it's own routing system when using self hosting (Either WCF self host, or Owin host are supported out of the box).
There is one other small difference, Web API routes require naming of the route, where MVC routes do not.
One of the significant differences in the web API compared to traditional ASP.NET MVC controllers is how the web API will route request into the action methods.
With the web API, the HTTP method being used plays a role. The HTTP method is the verb used in the HTTP message and the common verbs are get, post, put, and delete. Those are the verbs that the web API will route by default. You an also handle additional verbs if you need to do something like webDAV. You can do that and handle additional verbs by using an except verbs attribute.
What the web API will do is if there is a request for "/movies", the web API will look for a movie controller and then look for a method on that controller starting with the word "Get." So I could have an action called get movies and because this is an HTTP get message, the framework will invoke get movies. But you could also call it GET whatever is just because it starts with the letters G-E-T that's why that particular action method will receive the request.
The web API also registers these routes slightly differently since there is no action that's going to be in the URL for the routing engine to pick apart, it's using the verb instead. If you look at the default route configuration for a web API, it's done with an extension method MapHttpRoute.
For Web API it is:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
For MVC, it is:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Notice that for Web API there is no action in the URL template and it's also important to note that the path of this needs to start with "api".
After looking at this, the proper way to reach a controller (say Movies) is to use /api/movies as a URL on the browser. That will invoke the get method since we have a get request.
Note: Web API controllers inherit from System.Web.Http.Controller, but MVC controllers inherit from System.Web.Mvc.Controller. Both the libraries are different but acts in similar fashion. Web API's MapHttpRoute is defined as an extension method in System.Web.Http class. MVC's MapRoute is defined in System.Web.Mvc as an extension method.
I'd like to log the requests to my Web API application that results in 404 (which means the routing engine couldn't find a matching service).
How would that be possible?
For example, if I have the below mapping:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new
{
id = RouteParameter.Optional
}
);
then calling the below will return 404:
api_rootNotValid/controllername
I want to capture such requests and log them.
I've created a class inheriting from DelegatingHandler but only when the routing engine succeeds finding a service then it goes into the DelegatingHandler.
So I think there should be something else that can help?
I think this is a tricky thing to do...some info which you might need to consider:
Route matching happens at different stages when compared to WebHost(IIS) and SelfHost. Web API Stack Diagram for reference.
WebHost - happens even before any message handlers are run. So your
delegating handler wouldn't even be executed if a route doesn't
match.
SelfHost - happens after the message handlers are run and at
the HttpRoutingDispatcher. In this case your Delegating handler would
be executed and could probably check for the response's status code,
but even this is error prone due to the next point.
A 404 response could be due to route not being matched or if the route matched but some application level code has responded with 404 (ex: api/Products/10 can return 404 if product with id 10 was not found). So here the task is to find whether the route matching generated the 404 or the application level code.