ict.ken.be

Delivering solid user friendly software solutions since the dawn of time.

Introduction to the ASP.Net WebApi - Notes

Categories: Notes WebApi

Introduction to the ASP.NET WebApi
Jon Flanders

  • Service layer for HTML5, mobile applications, ...
  • REST : Representational State Transfer (Roy Fielding)
  • Full rest -> Level4 service
RouteTable.Routes.MapHttpRoute(
"InstructorRoute",
"{controller}/{id},
new { id = RouteParameter.Optional }
);
 
ApiController
[Authorize(users="jon")]
public string Get() //returns all
{
  return this.Request.GetUserPrinciple().Identity.Name;
}
public HttpResponseMessage<Instructor> Get(int id)
{
   var instructor = (from ...)
   if (null == instructor)
   {
      //return 404
      var notfoundMessage = new HttpRequestMessage<Instructor>(HttpStatusCode.NotFound);
      return notfoundMessage;       
   }
   var newMessage = new HttpRequestMessage<Instructor>(instructor);
   return newMessage;
}
public HttpResponseMessage<Instructor> Post(Instructor instructor)
   //create and insert and return 201 
   var newMessage = new HttpRequestMessage<Instructor>(instructor, HttpStatusCode.created);
   newMessage.Headers.Location = 
      new Uri(Request.RequestUri, "/instructors/" + instructor.ID.ToString());
}
public Instructor Put(Instructor instructor)
{ update }
public Instructor Delete(int id)
{ remove }
System.Net.Http
System.Net.Http.Formatting
System.Web.Http
System.Web.Http.Common
System.Web.Http.WebHost
System.Web.Http.SelfHost
System.Web.Http.Data
System.Web.Http.Data.EntityFramework
System.Web.Http.Data.Helpers 
  • ModelBinder for Body, header, querystring
  • MediaTypeFormatters
  • Validation via attributes
  • HttpRequestMessage and HttpResponseMessage
  • Content negotiation (xml or json)
  • Customize file extension or querystring
  • Fidler : Post with Content-Type:application/json
  • http://odata.org with IQueryable
  • HttpConfiguration class
  • GlobalConfiguration.Configuration
  • DependencyResolver 
Uniform interface of REST
  • Start with URI, add well-known HTTP verbs
  • Get: cacheble, safe, always same effect (idempotent)
  • Post: create new resource, unsafe, non-cached 
  • Put: update, idempotent
  • Delete: remove, idempotent
  • You can specify multiple verbs to one method
ApiController: IHttpController, IDisposable
{
   Configuration, ControllerContext, ModelState, Request, Url, Task<HttpResponseMessage>, ExecuteAsync, Initialize
}
 
HttpRequestMessage:IDisposable
{
  Content, Headers, Method, Properties, RequestUri, Version, Displose
}
 
HttpResponseMessage:IDisposable
{
  Content, Headers, IsSuccessStatusCode, ReasonPhrase, RequestMesage, StatusCode, Version, Dispose, EnsureSuccessStatusCode
}
$.ajax({
url: '/api/foo/id',
success: function (data) {},
statusCode: { 
404: function() {} 
}
}); 
HttpClient
  • HttpWebRequest, WebClient
var c = new HttpClient();
c.GetAsync(uri).ContinueWith((t) =>
{
  HttpResponseMessage response = null;
  response = t.Result;
  response.EnsureSuccessStatusCode();
  response.Content.ReadAsAsync<JsonObject>().ContinueWith(readTask) =>
{
  ProcessJson(readTask.Result);
}
});
  • http://search.twitter.com/search.json?q=fun
  • PostAsync, PutAsync
  • ObjectContent<T> for Json or XML
  • StreamContent for raw data
  • MultipartContent and MultiPartFormContent
  • ByteArrayContent, FormUrlEncodedContent, StringContent
  • UploadVideoClient (HttpClient)
  • Dealing with headers
  • Dealing with cookies 
Per-request headers
var pcontent = new HttpRequestMessage<Person>().CreateContent<Person>(new Person());
pcontent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
pcontent.Headers.Add("Accept","application/xml");
  • public class HttpClientHandler : HttpMessageHandler
  • Derive from DelegatingHandler
  • The HttpClient does not send a UserAgent by default
  • client.DefaultRequestHeaders.Add("User-Agent","Jon's Cool Client");
  • Token-based authorization can be accomplished by a custom HttpClientHandler
Self-Hosting Example
  • Create a HttpConfiguration (eg. HttpSelfHostConfiguration)
  • Create your HttpServer (eg. HttpSelfHostServer)
  • Start listening: HttpServer.OpenAsync();
  • When using the first contructor overload, you end up with HttpControllerDispatcher as your HttpMessageHandler. It allows you to use the same ASP.NET routing and same convention or configuration settings as ApiController in ASP.Net Hosting.
  • config.Routes.MapHttpRoute("DefaultRoute", "{controller}/{id}", new { id=Parameter.Optional });
  • You can pass HttpServer to HttpClient, useful for self-hosting testing (all in memory)
var client = new HttpClient(server);
client.GetAsync(...).ContinueWith(
(t)=>{ var result = t.Result; result.Content.ReadAsStringAsync().ContinueWith(
(tr) => { Console.Write(rt.Result);})
;});
Security
ASP.NET Authentication or WCF for self-hosting
 
AllowAnonymousAttribute : explicitly allow access to action of apicontroller
 
Forms authentication
  • /account/jsonlogin endpoint with MVC4 projects
  • Authentication_JSON_AppService.axd
  • App gets cookie
 
Basic authentication
 
  • HttpClient/WebHttpRequest/WebClient understand Windows authentication
 
You can add support for token authentication (eg OAuth) fairly easily
 
Authorization
  • AuthorizeAttribute with names or roles on individual actions (returns 401 if unauthorized)
Self-host
  • HttpMessageHandler
  • request.GetUserPrincipal().Identity.Name;
  • HttpSelfHostConfiguration
protected override BindingParameterCollection OnConfigureBinding(HttpBinding httpBinding)
{
   httpBinding.Security.Mode = HttpBindingSecurity.TransportCredentials;
   httpBinding.Security.Transport.CredentialType=HttpClientCredentialType.Windows;
   return base.OnConfigureBinding(httpBinding);
Asp.Net Hosting
  • Global.asax.cs
  • GlobalConfiguration.Configuration.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
Extensibility
  • GlobalConfiguration.Configuration
  • HttpConfiguration
  • Filters, Formatters, MessageHandlers, Routes, ServiceResolver
  • IncludeErrorDetailPolicy.Always / Never / LocalOnly
 
Formatters will match incoming Content-Type with a MediaTypeFormatter (content negotiation and serialization)
  • JsonP
  • Inject code that isn't in same domain as controller
  • HttpResponseMessage<JsonPReturn> Get(int id, string callback)
  • response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/javascript");
<script>
function myCallback(obj) { document.getElementById('msg').innerText = obj.Data; }
</script>
<script src="http://server.com/api/controller/values/1?callback=myCallBack" />
Filters hook into the model binding and controller execution pipeline (model-related issues)
  • AuthorizeAttribute is an IFilter
  • MyFilter : IActionFilter
  • actionContext.ActionArguments 
 
Message Handlers are the http processing pipeline base class (http-issues)
  • HttpServer, HttpClientHandler, HttpControllerDispatcher
public class MyMethodOverrideHandler : DelegatingHandler
{
   const string header = "X-HTTP-Method-Override";
   //override SendAsync 
   //and let clients put the real verb in this header
}
ServiceResolver (internal implementation detail extensibility)
  • Implement IDependencyResolver or Call SetResolver and pass in two delegates
Action Filter vs. Mesage Handler
  • MessageHandler - before model binding - HTTP message level - eg. run an xml Schema validation
  • ActionFilter - after model binding - Extending your controller - eg. run an object validation