Eyal VardiCEO E4D Solutions LTDMicrosoft MVP Visual
C#Site: www.e4d.co.il
The Full Power of ASP.NET Web API
The Challenge
HTML 5 )Changes The Rules of The
Game(
Traditional apps
Browser Request
Index.html
MVC4
Traditional Request / Response for ALL rendered content and assets
Web Application (SPA)
Initial Request
Application.htm
MVC4
RequireJS Loader
Page1 Partial.htm
IndexViewModel.js
Application.js (bootstrap)
ViewModel (#index)
ViewModel (#login)
Model (LoginModel)JSON Requests
HTML5 localstorage
Handling disconnection
Web API Growth
Source: www.programmableweb.com – current APIs: 4,535
Single Page Web Apps (SPA)
WCF vs. ASP.NET Web API
SOAP WSDL HTTP
POST SimpleService.asmx/EchoString HTTP/1.1 Host: localhost:1489 User-Agent: Mozilla/5.0 Accept: text/html Content-Type: application/json; Content-Length: 27 ...
XML, JSON, SOAP, AtomPub ...
HTTP Communication
Headers
Data
Verb URL
POST SimpleService.asmx/EchoString HTTP/1.1 Host: localhost:1489 User-Agent: Mozilla/5.0 Accept: text/html,application/xhtml+xml Content-Type: application/json; Content-Length: 27 ...
JSON vs. SOAP
{"Age":37,"FirstName":"Eyal",
"ID":"123", "LastName":"Vardi“ }
Headers
Data
Verb URL
<Envelope> <Header> <!–- Headers --> <!-- Protocol's & Polices --> </Header> <Body> <!– XML Data --> </Body> </Envelope>
Protocols & Formats
BL’s
Interfaces
Services
WCF
WCF Data
Services
ASMX MVC Web API SignalR
B B B B B
SOAP REST OData JSON POX ??
ASP.NET Web API vs. ASP.NET MVC
Validation
Help Page
Security
Routing
Self-Hosting
Dependency Injection
Filters
Content Negotiation
RESTful APIHTTP Message Handlers
Media Formatter
OData
Testing
MICROSOFT CONFIDENTIAL – INTERNAL ONLY
GET /en/html/dummy.php?name=MyName&married=not+single &male=yes HTTP/1.1Host: www.explainth.atUser-Agent: Mozilla/5.0 (Windows;en-GB; rv:1.8.0.11) Gecko/20070312 Firefox/1.5.0.11Accept: text/xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5Accept-Language: en-gb,en;q=0.5Accept-Encoding: gzip,deflateAccept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7Keep-Alive: 300Connection: keep-aliveReferer: http://www.explainth.at/en/misc/httpreq.shtml
Embrace HTTP
ASP.NET Ecosystem
ASP.NET
SignalR
Services
SPAMVCWebForms
WebAPI
Sites
ASP.NET Web API (Hello World)
REpresentational State Transfer
REST is..
“An architectural style for building distributed
hypermedia systems.”
WSDL vs. RESTWSDL description of a web services types defined in <xsd:schema>simple messages parts of messages
interface specification operations of an interface input message output message
binding of interface to protocols & encoding description of the binding for each operation
service descriptionURI and binding to port
<definitions> <types></types> <message> <part></part> </message> <portType> <operation> <input> <output> </operation> </portType> <binding> <operation> </binding> <service> <port> </service></definitions>
From RPC to REST
* From ASP.NET MVC 4 and the Web API: Building a REST Service from Start to Finish book
URIs and Resources (Routing)
* From ASP.NET MVC 4 and the Web API: Building a REST Service from Start to Finish book
HTTP Verbs
* From ASP.NET MVC 4 and the Web API: Building a REST Service from Start to Finish book
“Hypermedia as the engine of application state” (HATEOAS)<?xml version="1.0" encoding="utf-8"?><Tasks>
<TaskInfo Id="1234" Status="Active" > <link rel="self" href="/api/tasks/1234" method="GET" /> <link rel="users" href="/api/tasks/1234/users" method="GET" /> <link rel="history" href="/api/tasks/1234/history" method="GET" /> <link rel="complete" href="/api/tasks/1234" method="DELETE" /> <link rel="update" href="/api/tasks/1234" method="PUT" /> </TaskInfo>
<TaskInfo Id="0987" Status="Completed" > <link rel="self" href="/api/tasks/0987" method="GET" /> <link rel="users" href="/api/tasks/0987/users" method="GET" /> <link rel="history" href="/api/tasks/0987/history" method="GET" /> <link rel="reopen" href="/api/tasks/0987" method="PUT" /> </TaskInfo>
</Tasks>
/api/tasks
“Hypermedia as the engine of application state” (HATEOAS)
* From ASP.NET MVC 4 and the Web API: Building a REST Service from Start to Finish book
The Advantages of REST over SOAP Simpler API for CRUD
Standardize Development methodology
Wide industry adoption, Ease of use
Smaller payloads than SOAP
Testability
RESTful
OData OData is a standardized protocol for
creating and consuming data APIs.
Query string options $Expand
/Customers(ALFKI)?$expand=Orders.Employees
$Orderby /Customers?$orderby=City desc,
CompanyName
$Skip /Customers?$skip=10
$Top /Orders?$orderby=TotalDue&$top=5
$Filter /Customers?$filter=City eq ‘London’
LINQ to URI /ds.svc/Customers
from c in Customers select c
/ds.svc/Customers('ALFKI') ( from c in Customers
where c.keyProperty == "ALFKI" select c ).First()
/ds.svc/Customers('ALFKI')/Address ( from c in Customers
where c.keyProperty == "ALFKI" select c.Address ).First()
/ds.svc/Customers('ALFKI')/Orders from c in Customers
from c2 in c.Orders where c.keyProperty == "ALFKI" select c2
OData
Help Page ASP.NET and Web Tools 2012.2 Update
integrates help pages into the Web API project template.
Web API generated dynamically, using the IApiExplorer interface.
Adding a simple Test Client to ASP.NET Web API Help Page
Help Page
Format & Model Binding
Format & Model Binding
Serialization
Validation
DeserializationResponse
Controller / Action
Request HTTP/1.1 200 OK Content-Length: 95267 Content-Type: text/html | application/json Accept: text/html, application/xhtml+xml, application/xml
Media Formatters (Serialization) A media-type formatter is an object that
can: Read CLR objects from an HTTP message body.
Write CLR objects into an HTTP message body.
GlobalConfiguration.Configuration.Formatters
JSON Media-Type Formatter
Json.NET or DataContractJsonSerializer?
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter; json.UseDataContractJsonSerializer = true;
JSON Media-Type Formatter
What Gets Serialized? "opt-out" approach
"opt-in" approach
public class Product{ public string Name { get; set; } public decimal Price { get; set; } [JsonIgnore] public int ProductCode { get; set; }}
[DataContract] public class Product{ [DataMember] public string Name { get; set; } [DataMember] public decimal Price { get; set; } // omitted by default public int ProductCode { get; set; }}
JSON Media-Type Formatter
Anonymous and Weakly-Typed Objects
public object Get() { return new { Name = "Alice", Age = 23, Pets = new List<string> { "Fido", "Polly", "Spot" } }; }
public void Post(JObject person) { string name = person["Name"].ToString(); int age = person["Age"].ToObject<int>(); }
XML Media-Type Formatter
DataContractSerializer or XmlSerializer?
var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter; xml.UseXmlSerializer = true;
The XmlSerializer class supports a narrower set of types than DataContractSerializer, but gives more control over the resulting XML. Consider using XmlSerializer if you need to match an existing XML schema.
Custom Media Formatters Derives from BufferedMediaTypeFormater.
In the constructor, add the media types that the formatter supports.
To indicate which types the formatter can serialize, Override the methods: CanWriteType CanReadType WriteToStream
Use the Formatters property on the HttpConfiguration object to add a custom media type formatter.
Custom Media Formatters
Content Negotiation The primary mechanism for content
negotiation in HTTP are these request headers: Accept Accept-Charset Accept-Encoding Accept-Language
“the process of selecting the best representation for a given response when there are multiple representations available.”
Content Negotiation Samplepublic HttpResponseMessage GetProduct(int id){ var product = new Product() { Id = id, Name = "Gizmo", Price = 1.99M };
IContentNegotiator negotiator = this.Configuration .Services.GetContentNegotiator();
ContentNegotiationResult result = negotiator.Negotiate( typeof(Product), this.Request, this.Configuration.Formatters);
if (result == null) { var response = new HttpResponseMessage(HttpStatusCode.NotAcceptable); throw new HttpResponseException(response)); }
return new HttpResponseMessage() { Content = new ObjectContent<Product>( product, // What we are serializing result.Formatter, // The media formatter result.MediaType.MediaType // The MIME type ) };}
Validations
Model Data Annotations Specify validation for individual fields in
the data model. System.ComponentModel.DataAnnotations
Provide both client and server validation checks with no additional coding required by you.
public class ProductMD{ [StringLength(50), Required] public string Name { get; set; } [Range(0, 9999)] public int Weight { get; set; }}
Validation Attributes Metadata Validation Attributes:
[Required] [Exclude] [DataType] [Range]
[MetadataTypeAttribute( typeof( Employee.EmployeeMetadata ) )]public partial class Employee} internal sealed class EmployeeMetadata } [StringLength(60)] [RoundtripOriginal] public string AddressLine { get; set; } }}
[StringLength(60)] [RegularExpression
] [AllowHtml] [Compare]
Web API Validation
public class ProductsController : ApiController{ public HttpResponseMessage Post(Product product) { if ( ModelState.IsValid ) { // Do something with the product (not shown). return new HttpResponseMessage(HttpStatusCode.OK); } else { return new HttpResponseMessage(HttpStatusCode.BadRequest); } }}
Custom Validation public class ModelValidationFilterAttribute : ActionFilterAttribute { public override void OnActionExecuting(HttpActionContext actionContext) { if (actionContext.ModelState.IsValid == false) { // Return the validation errors in the response body. var errors = new Dictionary<string, IEnumerable<string>>(); foreach ( var keyValue in actionContext.ModelState ) { errors[keyValue.Key] = keyValue.Value.Errors.Select(e => e.ErrorMessage); } actionContext.Response = actionContext .Request .CreateResponse(HttpStatusCode.BadRequest, errors); } } }
public class ModelValidationFilterAttribute : ActionFilterAttribute { public override void OnActionExecuting(HttpActionContext actionContext) { if (actionContext.ModelState.IsValid == false) { // Return the validation errors in the response body. var errors = new Dictionary<string, IEnumerable<string>>(); foreach ( var keyValue in actionContext.ModelState ) { errors[keyValue.Key] = keyValue.Value.Errors.Select(e => e.ErrorMessage); } actionContext.Response = actionContext .Request .CreateResponse(HttpStatusCode.BadRequest, errors); } } }
Custom Validation
protected void Application_Start() { // ... GlobalConfiguration .Configuration .Filters.Add(new ModelValidationFilterAttribute()); }
Custom Validation
Server-Side Message Handlers The Web API pipeline uses some built-in
message handlers: HttpServer gets the request from the host.
HttpRoutingDispatcher dispatches the request based on the route.
HttpControllerDispatcher sends the request to a Web API controller.
You can add custom handlers to the pipeline.
Custom Message Handlers
Self-Hosting
var config = new HttpSelfHostConfiguration("http://localhost:8080");
config.Routes.MapHttpRoute( "API Default", "api/{controller}/{id}", new { id = RouteParameter.Optional });
using (HttpSelfHostServer server = new HttpSelfHostServer(config)) { server.OpenAsync().Wait(); Console.ReadLine(); }
Client Side
Client Side Frameworks for SPA
Resources http://www.asp.net/web-api/ Why I’m giving REST a rest Building Hypermedia Web APIs with ASP.NET Web A
PI
ASP.NET MVC 4 and Web API Book
Thankseyalvardi.wordpress.com
Eyal VardiCEO E4D Solutions LTDMicrosoft MVP Visual C#blog: www.e4d.co.il