New in ASP.NET MVC4 Beta: Web API
This is a high level overview of new stuff that appeared within VS2011
release. So, let’s briefly look inside. Today will take a look on changes
appeared to Web API.
Web API
The biggest and most important change as for me is integration of Web API
into ASP.NET. Web API is most known as WCF Web API - an attempt of .NET team
build framework for creation of RESTfull services based on WCF
infrastructure. There was several releases of WCF Web API made and general
feedback from community was - “it’s good enough”. Since when, Microsoft did
a strategic change, merged WCF Web API and ASP.NET teams together, so both
products to be developed in sync. From now Web API is integral part of
ASP.NET (and does not have any WCF prefix any more) and called ASP.NET Web
API. It is not just re-branding, but it reflects changes both the
programming model and processing architecture.
Simple Start
If we create a new ASP.NET MVC4 application, we see the following change
into global.asax
file.
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
MapHttpRoute
is new extension method for
RouteCollection
. Similarly to ASP.NET MVC routing definition,
it maps HTTP request with matching URL into corresponding API controller
action. By default, the are all prefixed with “api” to do not “interfere” to
ASP.NET MVC routing. It’s not a problem to change that prefix to anything
you want, like “services”, “methods” etc.
/api/contacts/ <- Web API
/api/contacts/1 <- Web API
/contacts/1 <- ASP.NET MVC
The API controllers are really similar to ASP.NET MVC controllers. Instead
of inherit from Controller
class, they inherited from
ApiController
class. Instead of returning
ActionResult
from action, in API controllers you return either
IEnumerable
or concrete instance, like
Product
(there are some more dedicated scenarios with returning
types, we will see later).
By just looking into example from project template it is very easy to
understand, what’s going on. There are some small details you should know
before API controller usage.
Action methods naming
API controllers are “Convention over configuration” based. The actual HTTP
verb that action handles could be specified in method name.
public class ProductsController : ApiController
{
// Handles GET
public IEnumerable<Product> Get() { }
// Handles POST
public Product Post(Product product) { }
// Handles PUT
public Product Put(Product product) { }
// Handles DELETE
public void Delete(int id) { }
}
This convention applies only to GET, POST, PUT, and DELETE methods. You
might be more specific in methods names, like GetProducts
or
DeleteProduct
, the important is the prefix (Get, Post, Put
etc.) of method.
If you don’t want to rely of named based conventions, you can explicitly
specify target HTTP verb by decorating methods with HttpGet, HttpPut,
HttpPost, or HttpDelete attribute.
public class ProductsController : ApiController
{
[HttpGet]
public IEnumerable<Product> AllProducts() { }
[HttpPost]
public Product CreateProduc(Product product) { }
[HttpPut]
public Product UpdateProduct(Product product) { }
[HttpDelete]
public void DeleteProduct(int id) { }
}
One handler per one controller
API Controller are different from ASP.NET MVC controller. The main
difference is that API controller handles exactly one HTTP request belongs
to particular entity.
If you try to create the code like:
public class ProductsController : ApiController
{
public IEnumerable<Product> Get() { }
public IEnumerable<Product> GetAnother() { }
}
It would be build OK, but on a runtime if you try to access ‘/api/products’,
you will get an exception:
"Multiple actions were found that match the request:
\u000d\u000aSystem.Collections.Generic.IEnumerable`1[MvcApplication7.Api.Controllers.Product]
Get() on type MvcApplication7.Api.Controllers.ProductsController\u000d\u000aSystem.Collections.Generic.IEnumerable`1[MvcApplication7.Api.Controllers.Product]
GetAnother() on type MvcApplication7.Api.Controllers.ProductsController"
The less stuff is very similar to something we get used in ASP.NET MVC. And
you might think - why do we need this Web API at all, I can do exactly the
same stuff with good-n-old ASP.NET MVC controllers? Not exactly. Web API has
several killer features that makes it favorite for data oriented API’s.
Let’s go on.
Content Negotiation
Content Negotiation is the process for mutual agreement between client and
server about the received/sent data formats. Good news here, Web API does a
lot of job for you, leaving content negotiation internals behind the scene.
Let’s take a brief look of how things happen.
When client sends HTTP request to server, besides the query string and
payload it also provides different kind information. This information is
being placed into HTTP header and provides server with specific details for
better understanding each other. In particular, it contains
Accept
header field which specifies client preferences on
response format. I’m going use Fiddler to check how Web API behaves on
different conditions.
First, let’s do not specify the Accept
field at all. So, the
payload we going to send to server from our client (Fiddler) will be like:
GET /api/products HTTP/1.1
User-Agent: Fiddler
Host: localhost:5589
For that request, we will receive the response like:
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?RDpcRGV2ZWxvcG1lbnRcUHJvamVjdHNcZXhwZXJpbWVudHNcTXZjQXBwbGljYXRpb243XE12Y0FwcGxpY2F0aW9uN1xhcGlccHJvZHVjdHM=?=
X-Powered-By: ASP.NET
Date: Mon, 19 Mar 2012 17:31:23 GMT
Content-Length: 88
[{"Description":"iPad","Id":1,"Price":1000},{"Description":"iPhone","Id":2,"Price":500}]
As you can see, the default Content-Type is “application/json;
charset=utf-8”, there is a json string in HTTP response payload.
Let’s make our request a little more specific.
GET /api/products HTTP/1.1
User-Agent: Fiddler
Host: localhost:5589
Accept: text/xml
The corresponsing response:
HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: text/xml; charset=utf-8
Expires: -1
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
X-SourceFiles: =?UTF-8?B?RDpcRGV2ZWxvcG1lbnRcUHJvamVjdHNcZXhwZXJpbWVudHNcTXZjQXBwbGljYXRpb243XE12Y0FwcGxpY2F0aW9uN1xhcGlccHJvZHVjdHM=?=
X-Powered-By: ASP.NET
Date: Mon, 19 Mar 2012 17:33:34 GMT
Content-Length: 329
<?xml version="1.0" encoding="utf-8"?><ArrayOfProduct xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><Product><Id>1</Id><Description>iPad</Description><Price>1000</Price></Product><Product><Id>2</Id><Description>iPhone</Description><Price>500</Price></Product></ArrayOfProduct>
As you can see, without any code changes we made it “adapt” for particular
client needs. This is a very decent feature of Web API and available for
use, just from the box. You even don’t need to care about it at all. Leave
the content negotiation for the infrastructure.
IQueryable<T> Support
Next cool feature is IQueryable<T> support. If you need to, instead of
returning “plain” IEnumerable objects from the API action, you might return
IQueryable<T>. Why?
Remember the times we implemented paging & sorting with ASP.NET MVC
application. It was possible of cause, but it required a lot of manual job.
Actions had to be extended with additional parameters, code have to respect
those parameters and return exact portion of data we require to. The same
story with sorting. In Web API it much simpler.
Change the signature and return type to IQueryable.
public IQueryable<Product> Get()
{
return _storage.AsQueryable();
}
Now, if Web API sees the method like that, it will allow to access with with
Open Data Protocol (OData) query string parameters. OData provides support
for following queries: $filter, $orderby, $skip, $top.
Now, if I do the request:
http://localhost:5589/api/products?$top=3
I will receive, 3 top products. Or something like,
http://localhost:5589/api/products?$skip=2&$top=3
I will skip 2 and take rest 3. In short, having IQueryable and 4 OData query
parameters it’s much more easy to do the stuff required more time before.
Hosting
Hosting is a feature of Web API allow to run service inside the different
hosts. What does it mean?
We are much get used to ASP.NET hosting as default. I saw no problem with
that at all till working on one of my recent
projects. The
problem is for ASP.NET pipeline you need to have a web server (IIS), but web
server always have some limitations (permissions and stuff). For simple
things, that does not require any IIS benefits it is easier to host
application just in .exe assembly. It’s now possible with Web API.
We have several hosting providers out of the box: self-hosting and
web-hosting. Besides of that there are already some provided by community
Louis DeJardin created a host on top of OWIN
and
Pedro Félix host for Azure.
Also, there is a great article by Pedro Felix about
in-memory
hosting. This is very useful type of hosting for unit testing of services.
IoC improvements
Since Web API now is inside ASP.NET MVC, it uses the same strategy of
resolving types by
IDependencyResolver. That means that Web API classes are now supporting same IoC style as
ASP.NET MVC controller classes. Being setup once you can put different
dependencies inside API class constructor.
public class ProductsController : ApiController
{
public ProductsController(IServiceProvider service, IRepository<Product> productRepository)
{
// ..
}
Conclusions
This is not everything, but very light overview of new stuff coming within
ASP.NET MVC4. ASP.NET Web API is very nice addition to framework. There are
already some
good
feedback, so let’s hope it will be even more improved toward release.
This is a high level overview of new stuff that appeared within VS2011
release. So, let’s briefly look inside. Today will take a look on changes
appeared to Web API.
Web API
The biggest and most important change as for me is integration of Web API
into ASP.NET. Web API is most known as WCF Web API - an attempt of .NET team
build framework for creation of RESTfull services based on WCF
infrastructure. There was several releases of WCF Web API made and general
feedback from community was - “it’s good enough”. Since when, Microsoft did
a strategic change, merged WCF Web API and ASP.NET teams together, so both
products to be developed in sync. From now Web API is integral part of
ASP.NET (and does not have any WCF prefix any more) and called ASP.NET Web
API. It is not just re-branding, but it reflects changes both the
programming model and processing architecture.
Simple Start
If we create a new ASP.NET MVC4 application, we see the following change
into global.asax
file.
routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } );
MapHttpRoute
is new extension method for
RouteCollection
. Similarly to ASP.NET MVC routing definition,
it maps HTTP request with matching URL into corresponding API controller
action. By default, the are all prefixed with “api” to do not “interfere” to
ASP.NET MVC routing. It’s not a problem to change that prefix to anything
you want, like “services”, “methods” etc.
/api/contacts/ <- Web API /api/contacts/1 <- Web API /contacts/1 <- ASP.NET MVC
The API controllers are really similar to ASP.NET MVC controllers. Instead
of inherit from Controller
class, they inherited from
ApiController
class. Instead of returning
ActionResult
from action, in API controllers you return either
IEnumerable
or concrete instance, like
Product
(there are some more dedicated scenarios with returning
types, we will see later).
By just looking into example from project template it is very easy to understand, what’s going on. There are some small details you should know before API controller usage.
Action methods naming
API controllers are “Convention over configuration” based. The actual HTTP verb that action handles could be specified in method name.
public class ProductsController : ApiController { // Handles GET public IEnumerable<Product> Get() { } // Handles POST public Product Post(Product product) { } // Handles PUT public Product Put(Product product) { } // Handles DELETE public void Delete(int id) { } }
This convention applies only to GET, POST, PUT, and DELETE methods. You
might be more specific in methods names, like GetProducts
or
DeleteProduct
, the important is the prefix (Get, Post, Put
etc.) of method.
If you don’t want to rely of named based conventions, you can explicitly specify target HTTP verb by decorating methods with HttpGet, HttpPut, HttpPost, or HttpDelete attribute.
public class ProductsController : ApiController { [HttpGet] public IEnumerable<Product> AllProducts() { } [HttpPost] public Product CreateProduc(Product product) { } [HttpPut] public Product UpdateProduct(Product product) { } [HttpDelete] public void DeleteProduct(int id) { } }
One handler per one controller
API Controller are different from ASP.NET MVC controller. The main difference is that API controller handles exactly one HTTP request belongs to particular entity.
If you try to create the code like:
public class ProductsController : ApiController { public IEnumerable<Product> Get() { } public IEnumerable<Product> GetAnother() { } }
It would be build OK, but on a runtime if you try to access ‘/api/products’, you will get an exception:
"Multiple actions were found that match the request: \u000d\u000aSystem.Collections.Generic.IEnumerable`1[MvcApplication7.Api.Controllers.Product] Get() on type MvcApplication7.Api.Controllers.ProductsController\u000d\u000aSystem.Collections.Generic.IEnumerable`1[MvcApplication7.Api.Controllers.Product] GetAnother() on type MvcApplication7.Api.Controllers.ProductsController"
The less stuff is very similar to something we get used in ASP.NET MVC. And you might think - why do we need this Web API at all, I can do exactly the same stuff with good-n-old ASP.NET MVC controllers? Not exactly. Web API has several killer features that makes it favorite for data oriented API’s. Let’s go on.
Content Negotiation
Content Negotiation is the process for mutual agreement between client and server about the received/sent data formats. Good news here, Web API does a lot of job for you, leaving content negotiation internals behind the scene. Let’s take a brief look of how things happen.
When client sends HTTP request to server, besides the query string and
payload it also provides different kind information. This information is
being placed into HTTP header and provides server with specific details for
better understanding each other. In particular, it contains
Accept
header field which specifies client preferences on
response format. I’m going use Fiddler to check how Web API behaves on
different conditions.
First, let’s do not specify the Accept
field at all. So, the
payload we going to send to server from our client (Fiddler) will be like:
GET /api/products HTTP/1.1 User-Agent: Fiddler Host: localhost:5589
For that request, we will receive the response like:
HTTP/1.1 200 OK Cache-Control: no-cache Pragma: no-cache Content-Type: application/json; charset=utf-8 Expires: -1 Server: Microsoft-IIS/7.5 X-AspNet-Version: 4.0.30319 X-SourceFiles: =?UTF-8?B?RDpcRGV2ZWxvcG1lbnRcUHJvamVjdHNcZXhwZXJpbWVudHNcTXZjQXBwbGljYXRpb243XE12Y0FwcGxpY2F0aW9uN1xhcGlccHJvZHVjdHM=?= X-Powered-By: ASP.NET Date: Mon, 19 Mar 2012 17:31:23 GMT Content-Length: 88 [{"Description":"iPad","Id":1,"Price":1000},{"Description":"iPhone","Id":2,"Price":500}]
As you can see, the default Content-Type is “application/json; charset=utf-8”, there is a json string in HTTP response payload.
Let’s make our request a little more specific.
GET /api/products HTTP/1.1 User-Agent: Fiddler Host: localhost:5589 Accept: text/xml
The corresponsing response:
HTTP/1.1 200 OK Cache-Control: no-cache Pragma: no-cache Content-Type: text/xml; charset=utf-8 Expires: -1 Server: Microsoft-IIS/7.5 X-AspNet-Version: 4.0.30319 X-SourceFiles: =?UTF-8?B?RDpcRGV2ZWxvcG1lbnRcUHJvamVjdHNcZXhwZXJpbWVudHNcTXZjQXBwbGljYXRpb243XE12Y0FwcGxpY2F0aW9uN1xhcGlccHJvZHVjdHM=?= X-Powered-By: ASP.NET Date: Mon, 19 Mar 2012 17:33:34 GMT Content-Length: 329 <?xml version="1.0" encoding="utf-8"?><ArrayOfProduct xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><Product><Id>1</Id><Description>iPad</Description><Price>1000</Price></Product><Product><Id>2</Id><Description>iPhone</Description><Price>500</Price></Product></ArrayOfProduct>
As you can see, without any code changes we made it “adapt” for particular client needs. This is a very decent feature of Web API and available for use, just from the box. You even don’t need to care about it at all. Leave the content negotiation for the infrastructure.
IQueryable<T> Support
Next cool feature is IQueryable<T> support. If you need to, instead of returning “plain” IEnumerable objects from the API action, you might return IQueryable<T>. Why?
Remember the times we implemented paging & sorting with ASP.NET MVC application. It was possible of cause, but it required a lot of manual job. Actions had to be extended with additional parameters, code have to respect those parameters and return exact portion of data we require to. The same story with sorting. In Web API it much simpler.
Change the signature and return type to IQueryable.
public IQueryable<Product> Get() { return _storage.AsQueryable(); }
Now, if Web API sees the method like that, it will allow to access with with Open Data Protocol (OData) query string parameters. OData provides support for following queries: $filter, $orderby, $skip, $top.
Now, if I do the request:
http://localhost:5589/api/products?$top=3
I will receive, 3 top products. Or something like,
http://localhost:5589/api/products?$skip=2&$top=3
I will skip 2 and take rest 3. In short, having IQueryable and 4 OData query parameters it’s much more easy to do the stuff required more time before.
Hosting
Hosting is a feature of Web API allow to run service inside the different hosts. What does it mean?
We are much get used to ASP.NET hosting as default. I saw no problem with that at all till working on one of my recent projects. The problem is for ASP.NET pipeline you need to have a web server (IIS), but web server always have some limitations (permissions and stuff). For simple things, that does not require any IIS benefits it is easier to host application just in .exe assembly. It’s now possible with Web API.
We have several hosting providers out of the box: self-hosting and web-hosting. Besides of that there are already some provided by community Louis DeJardin created a host on top of OWIN and Pedro Félix host for Azure.
Also, there is a great article by Pedro Felix about in-memory hosting. This is very useful type of hosting for unit testing of services.
IoC improvements
Since Web API now is inside ASP.NET MVC, it uses the same strategy of resolving types by IDependencyResolver. That means that Web API classes are now supporting same IoC style as ASP.NET MVC controller classes. Being setup once you can put different dependencies inside API class constructor.
public class ProductsController : ApiController { public ProductsController(IServiceProvider service, IRepository<Product> productRepository) { // .. }
Conclusions
This is not everything, but very light overview of new stuff coming within ASP.NET MVC4. ASP.NET Web API is very nice addition to framework. There are already some good feedback, so let’s hope it will be even more improved toward release.