Session Title That is Really Long and Covers Two Lines

Download Report

Transcript Session Title That is Really Long and Covers Two Lines

This session is available at:
http://bit.ly/devreach-2012-idof
Embracing HTTP with
ASP.NET Web.APIs
Ido Flatow | Senior Architect | Sela
Group
@idoFlatow | http://bit.ly/flatowblog
About Me
• Senior architect, Sela Group
• Co-Author of Microsoft’s official
WCF 4 course
• Microsoft MVP for Connected
Systems Development
• Focus on: ASP.NET, WCF
Window Azure, and IIS 7.5
• Manager of the Israeli Web
Developers User Group
• Microsoft certified trainer
www.devreach.com
The History of ASP.NET Web
API
March 2012
ASP.NET is
Open Source
February 2012
6 Preview Versions
ASP.NET Web API
(Beta)
October 2010
WCF Web API
on CodePlex
April 2010
WCF REST Starter Kit
(Preview 2)
WCF WebHttp
Binding (.NET 4)
Nov. 2007
WCF WebHttp
Binding (.NET 3.5)
Why a New Stack?
• WCF doesn’t support HTTP headers that
well
• There is a need for more than just POX
and JSON support
• Chance to incorporate common design
concepts that are missing from WCF, such
as IoC and convention over configuration
www.devreach.com
What You Write is What You Get
• Convention over configuration for actions
– URI + Verb = Controller + Action
– Customization supported through routing rules and
attributes
• Parameter binding “MVC” style
– Send parameters via headers, body, or query string
– Let Web API figure it out
– For complex scenarios, customization is supported
through attributes and custom binders
• Content negotiation
– Client can use Accept-* headers to control
response type
– Web API automatically selects best-matching
formatter
– Custom formatters are supported
www.devreach.com
Demo
POST api/hello/world
GET api/answers/meaningOfLife
3, 2, 1, Actions!
• Actions are matched by HTTP verb names
and the existence of parameters
public class ProductsController : ApiController
{
public IEnumerable<Product> GetProducts() {...}
public Product GetProductById(int id) {...}
public HttpResponseMessage PostProduct(Product product) {...}
}
GET api/products
GET api/products/42
POST api/products
www.devreach.com
DELETE api/products/42
3, 2, 1, Actions!
• Add new actions using the REST style
[HttpPost]
public HttpResponseMessage SaveProduct(Product product) {...}
[AcceptVerbs("GET", "HEAD")]
public Product LocateProduct(int id) {...}
• Or the RPC-style with URL routing
routes.MapHttpRoute(
name: "RpcStyleActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional });
[HttpGet]
public IEnumerable<Product> TopProducts(int count) {...}
www.devreach.com
Hosting Web API Services
• IIS is the natural hosting environment for the
ASP.NET web stack, Web API included
• When IIS is not an option or unwanted, use a
self-hosted Web API
• Just follow three basic steps:
– Install the “Microsoft ASP.NET Web API Self
Host” NuGet package
– Create host configuration and routing rules
– Start the self-hosted server
• Under the covers, Web API self-hosting is
handled by WCF
www.devreach.com
JavaScript + Web API
As Easy as it Sounds
• Response type is determined by the
request’s Accept header
• Web API supports both XML and JSON
• All you need is a simple Ajax call
$.ajax({
type: "GET",
url: "api/products",
dataType: "json",
success: function(data) {
alert(data);
}
});
www.devreach.com
.NET Clients + Web API
Easier Than You Think
• Clients cannot add a service reference to
Web API services
• Are WebClient and HttpWebRequest the
only way? Seriously?
• A new client stack is available - HttpClient
– Part of the System.Net.Http assembly
– Supports sending requests and handling
responses
– Can handle various content types (both
directions)
– Inheritable for customizations and extensibility
www.devreach.com
– Uses Task parallelism for asynchronous
.NET Clients + Web API
Easier Than You Think
using (var proxy = new HttpClient()
{ BaseAddress = new Uri(serverAddress) })
{
proxy.GetAsync("api/products/7").ContinueWith((r) =>
{
HttpResponseMessage response = r.Result;
response.EnsureSuccessStatusCode();
response.Content.ReadAsAsync<Product>().ContinueWith(
(task)=>
{
Product p = task.Result;
Console.WriteLine(p.Name + "," + p.Price);
});
});
Demo
CREATE, HOST, CONSUME
A SERVICE’S LIFE-CYCLE
What Real Services are Made Of…
Handling Exceptions
• Unhandled exceptions are translated to
HTTP 500 responses
• Manually throw HttpResponseException
to control response code and content
throw new HttpResponseException(
new HttpResponseMessage(HttpStatusCode.NotFound)
{
Content = new StringContent("Product not found"),
ReasonPhrase = "Missing"
});
www.devreach.com
What Real Services are Made Of…
Handling Exceptions
• Implement an exception filter to customize
how unhandled exceptions are treated
• Add it to action methods or controller class
public class UnhandledExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
throw new HttpResponseException(
new HttpResponseMessage(HttpStatusCode.InternalServerError) {
Content = new StringContent(
"An error has occurred" + context.Exception.Message),
ReasonPhrase = "Critical Error"
});
}
}
www.devreach.com
What Real Services are Made Of…
Caching
• ASP.NET’s OutputCache isn’t supported in Web
API
• Caching is handled manually by adding HTTP
cache headers
• Either create an HttpResponseMessage and add
cache headers
• Or use an action filter to set the headers for all
responses
• Action filters are types that can execute code
before and after an action
var cacheControl = new CacheControlHeaderValue();
cacheControl.MaxAge = TimeSpan.FromSeconds(300);
response.Headers.CacheControl = cacheControl;
www.devreach.com
What Real Services are Made Of…
Versioning & Concurrency
• When caching content, we need to identify
when content has changed
• The ETag (entity tag) header represents the
version of the content
• ETags are sent to the client with the
response, and are re-sent to the server on
subsequent requests
• In the action, compare received and existing
ETags, and return either:
– A new entity if they are different
– An HTTP 304 (Not Modified) if they are identical
• When updating entities using POST/PUT, use
the ETag for concurrency (version) checks
www.devreach.com
What Real Services are Made Of…
Versioning & Concurrency
public HttpResponseMessage Get(int id)
{
HttpResponseMessage response;
var etag = Request.Headers.IfNoneMatch.FirstOrDefault();
Product product = _manager.GetProductById(id);
if (etag != null &&
etag.ToString().Replace(@"""", "") == product.Version)
{
response = new HttpResponseMessage(HttpStatusCode.NotModified);
}
else
{
response = Request.CreateResponse(HttpStatusCode.OK, product);
response.Headers.ETag = new EntityTagHeaderValue(
string.Format(@"""{0}""", product.Version));
}
return response;
}
What Real Services are Made Of…
Controlling Content Types
• The content type of a response can be
inferred by the request’s Accept header
• Web API supports XML and JSON by
default
• What other types do we need?
– Data types (image, video, document)
– View formats (CSV, RSS, HTML)
– Transfer formats (ProtoBuf, base64)
• New content types can be created by
inheriting from MediaTypeFormatter and
mapping the new formatter to a MIME type
www.devreach.com
What Real Services are Made Of…
Controlling Content Types
public class ProductJpgFormatter : BufferedMediaTypeFormatter
{
public ProductJpgFormatter()
{
this.SupportedMediaTypes.Add(new MediaTypeHeaderValue("image/png"));
}
public override bool CanReadType(Type type)
{
return false;
}
public override bool CanWriteType(Type type)
{
return typeof(Product).Equals(type);
}
//... (cnt’d on next slide)
What Real Services are Made Of…
Controlling Content Types
public override void WriteToStream(Type type, object value,
Stream stream, HttpContentHeaders contentHeaders)
{
var product = value as Product;
if (product != null)
{
var imageId = product.product;
using (FileStream fileStream = GetProductImageFile(imageId))
{
byte[] bytes = new byte[fileStream.Length];
fileStream.Read(bytes, 0, (int)fileStream.Length);
stream.Write(bytes, 0, (int)fileStream.Length);
}
}
}
}
What Real Services are Made Of…
OData HTTP Services
• Open Data Protocol (OData) enables
querying a source using query strings
• OData is supported by WCF Data Services
and ASP.NET Web API
• Use the Queryable attribute and return an
IQueryable<T>
• Web API supports only:
–
–
–
–
$top
$skip
$filter
$orderby
www.devreach.com
What Real Services are Made Of…
Web API and OData
[Queryable]
public IQueryable<Product> GetProducts()
{
return repository.GetAll().AsQueryable();
}
api/products?$orderby=Name
api/products?$skip=10
api/products?$skip=50&$top=10
api/products?$filter=price gt
50000
Demo
WHAT REAL SERVICES ARE
MADE OF…
Just When You Thought You’ve
Seen Everything…
1
Help pages
2
Validations
3
Tracing
4
Dependency resolver
Create help page for your service with ApiExplorer
Use data annotation attributes and control validation result format
Implement ITraceWriter to write traces with your choice of log/trace library
Provide your own dependency resolver or use known IoC containers
In God We Trust,
the REST We Test
• Control over request and response
– Create requests manually with
HttpRequestMessage
– Assert by checking the HttpResponseMessage
object
• Test your code, not your database
– Make your controller accept repository interfaces,
not classes
– Use dependency injection to inject mock
repositories
• Test your code, not the network
www.devreach.com
– Create an in-memory HttpServer object
What Did We Just Talk About?
• ASP.NET Web API is the new stack for
developing HTTP-based services
• Easy to create, host, and consume
• Encourages convention over configuration
and dependency injection
• Highly extensible and testable
www.devreach.com
Resources
This Presentation
http://bit.ly/devreach-2012-idof
Official Web Sites
h ttp :/ /w w w. asp . net/w eb - ap i
h ttp :/ /w w w. asp . net/w eb - ap i /vi d eo s
http://forums.asp.net/1246.aspx
Blogs & Tutorials
http://webapibloggers.com
http://bit.ly/webapi-tutorials
My Info
http://bit.ly/flatow-blog
@idoFlatow
[email protected]
Thank you!
@idoFlatow
http://bit.ly/flatow-blog
linkedin.com/in/idoflatow
Ido Flatow | Sela Group