Date post: | 26-Dec-2014 |
Category: |
Technology |
Upload: | christian-horsdal |
View: | 1,955 times |
Download: | 0 times |
Click to edit Master title style
• Click to edit Master text styles– Second level• Third level
– Fourth level» Fifth level
19-04-2012 1
The Lightweight Approach to Building Web Based APIs with .NET
MOW 2012
Who Am I?
What is “lightweight”
RestBucks
REST
Nancy
Agenda
3
What is lightweight?
4
Low ceremony
Low cruft
Conventions
What is lightweight?
5
Open
Agile
Inexpensive
What is lightweight?
6
Restbucks
7
REST
8
Level 3: Hypermedia
Level 2: HTTP Verbs
Level 1: Resources
Level 0: POX-RPC
REST – Richardsons Maturity Model
9
The basic building blocks of web API
Anything with a URI
http://restbucks.com/menu/
http://restbucks.com/orders/
http://restbucks.com/order/42/
http://restbucks.com/order/42/payment/
REST - Resources
10
REST- Representations
GET http://restbucks.com/order/19202048/ HTTP/1.1
Accept: application/vnd.restbucks+xml
<?xml version="1.0"?><order xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://restbuckson.net"> <links> <link uri="http://restbucks.com/order/19202048" rel="http://restbucks.com/docs/order-get.htm" mediaType="application/vnd.restbucks+xml" /> <link uri="http://restbucks.com/order/19202048" rel="http://restbucks.com/docs/order-update.htm" mediaType="application/vnd.restbucks+xml" /> <link uri="http://restbucks.com/order/19202048" rel="http://restbucks.com/docs/order-cancel.htm" mediaType="application/vnd.restbucks+xml" /> <link uri="http://restbucks.com/order/19202048/payment" rel="http://restbucks.com/docs/order-pay.htm" mediaType="application/vnd.restbucks+xml" /> </links> <location>inShop</location> <cost>7.60000</cost> <items> <item> <name>Latte</name> <quantity>1</quantity> <milk>skim</milk> <size>large</size> </item> </items> <status>unpaid</status></order>
11
REST - Representations
GET http://restbucks.com/order/19202048/ HTTP/1.1
Accept: application/vnd.restbucks+json
{ "Location":inShop, "Cost":7.60000, "Items":[ { "Name":"Latte", "Quantity":1, "Preferences":{ "milk":"skim", "size":"large" } } ], "Status":1, "Links":[ { "Uri":"http://restbucks.com/order/19202048", "Relation":"http://restbucks.com/docs/order-get.htm", "MediaType":"application/vnd.restbucks+xml" }, { "Uri":"http://restbucks.com/order/19202048", "Relation":"http://restbucks.com/docs/order-update.htm", "MediaType":"application/vnd.restbucks+xml" }, { "Uri":"http://restbucks.com/order/19202048", "Relation":"http://restbucks.com/docs/order-cancel.htm", "MediaType":"application/vnd.restbucks+xml" }, { "Uri":"http://restbucks.com/order/19202048/payment", "Relation":"http://restbucks.com/docs/order-pay.htm", "MediaType":"application/vnd.restbucks+xml" } ]}
12
GET
POST
PUT
DELETE
HEAD
OPTIONS
PATCH
REST - verbs
13
Accept
Content-Type
If-None-Match
Etag
REST - Headers
14
15
“Close” to http
Very, very readable code
Very explicit routing
Embraces modularity
Embraces IoC/DI
Embraces testing
Runs anywhere
Why Nancy?
Organizes your routes
Nancy Basics
public class MainModule : NancyModule { public MainModule() { Get["/"] = _ => "Hello from root"; } }
public class SubModule : NancyModule { public SubModule() : base("subpath") { Get["/"] = _ => "Hello from subpath"; } }
Defines which verbs you accepts
HEAD and OPTIONS and automatic
Nancy Basics
public class MainModule : NancyModule { public MainModule() { Get["/"] = _ => "Hello from root"; Post["/”] = _ => DoPost(Request.Form.my_value) Delete["/{id}”] = p => Delete(p.id); Put["/”] = _ => DoPut(Request.Body); Patch["/”] = _ => DoPatch(Request.Body); } }
18
Restbuck on Nancy – Place an Order
public OrdersResourceHandler(IRepository<Product> productRepository, IRepository<Order> orderRepository) : base("/orders"){ this.productRepository = productRepository; this.orderRepository = orderRepository;
Post["/"] = _ => HandlePost(this.Bind<OrderRepresentation>());}
private Response HandlePost(OrderRepresentation orderRepresentation){ var order = TryBuildOrder(orderRepresentation); if (!order.IsValid()) return InvalidOrderResponse(order);
orderRepository.MakePersistent(order); return Created(order);}
19
RestBucks on Nancy – View an Order
public OrderResourceHandler(IRepository<Order> orderRepository) : base(”/order”){ this.orderRepository = orderRepository; Get["/{orderId}/”] = parameters => GetHandler((int) parameters.orderId); …}
public Response GetHandler(int orderId){ var order = orderRepository.GetById(orderId); if (order == null) return HttpStatusCode.NotFound;
if (order.Status == OrderStatus.Canceled) return Response.MovedTo(new ResourceLinker(CanceledOrderUri(orderId);
if (Request.IsNotModified(order)) return Response.NotModified();
return Response.WithContent(Request.Headers.Accept, OrderRepresentationMapper.Map(order,Request.BaseUri())) .WithCacheHeaders(order);}
20
RestBucks on Nancy – Cancel an Order
Delete["/{orderId}/"] = parameters => Cancel((int) parameters.orderId);
public Response Cancel(int orderId) { var order = orderRepository.GetById(orderId); if (order == null) return HttpStatusCode.NotFound;
order.Cancel("canceled from the rest interface"); return HttpStatusCode.NoContent; }
21
RestBucks on Nancy –Pay an Order
Post["/{orderId}/payment"] = parameters => Pay((int) parameters.orderId, this.Bind<PaymentRepresentation>());
public Response Pay(int orderId, PaymentRepresentation paymentArgs) { var order = orderRepository.GetById(orderId); if (order == null) return HttpStatusCode.NotFound;
order.Pay(paymentArgs.CardNumber, paymentArgs.CardOwner); return HttpStatusCode.OK; }
22
RestBucks on Nancy – XML or JSON
return Response.WithContent(Request.Headers.Accept, OrderRepresentationMapper.Map(order, Request.BaseUri())) .WithCacheHeaders(order);
public static Response WithContent<T>(this IResponseFormatter formatter, IEnumerable<Tuple<string, decimal>> acceptHeaders, T content) { var xmlWeight = CalculateWeightForContentType(acceptHeaders, "xml"); var jsonWeight = CalculateWeightForContentType(acceptHeaders, "json"); if (jsonWeight > xmlWeight) return formatter.AsJson(content); else return formatter.AsXml(content); }
23
RestBucks on Nancy – Conditional Gets
return Response.WithContent(Request.Headers.Accept, OrderRepresentationMapper.Map(order, Request.BaseUri())) .WithCacheHeaders(order);
public static Response WithCacheHeaders(this Response response, IVersionable versionable, TimeSpan? maxAge = null){ return response.WithHeaders( new { Header = "ETag", Value = string.Format("\"{0}\"", versionable.Version) }, new { Header = "Cache-Control", Value = string.Format("max-age={0}, public", maxAge ?? TimeSpan.FromSeconds(10)) }); }
24
RestBucks on Nancy – Conditional Gets
if (Request.IsNotModified(order)) return Response.NotModified();
public static bool IsNotModified(this Request request, IVersionable versionable) { if (!request.Headers.IfNoneMatch.Any()) return false; var etag = request.Headers.IfNoneMatch.First(); return string.Format("\"{0}\"", versionable.Version) == etag; }
public static Response NotModified(this IResponseFormatter formatter, TimeSpan? maxAge = null) { Response response = HttpStatusCode.NotModified;
return response.WithHeaders( new { Header = "ReasonPhrase", Value = "Not modified"}, new { Header = "Cache-Control", Value = string.Format("max-age={0}, public", maxAge ?? TimeSpan.FromSeconds(10)) }); }
…
Nancy.Hosting
Nancy
Your Application
Nancy.Hosting
Usage:> Install-Package Nancy.Hosting.*
Hosts:ASP.NET
WCF
Self
OWIN
Nancy.Hosting
29
Nancy.Testing
[Test] public void WhenOrderHasNotChanged_ThenReturn304() { // Arrange var orderRepo = new RepositoryStub<Order>(new Order(1, 123); var app = new Browser( new ConfigurableBootstrapper (with => { with.Dependency<IRepository<Product>>(…); with.Dependency<IRepository<Order>>(orderRepository); } )); // Act var response = app.Get("/order/123/", with => { with.HttpRequest(); with.Header("If-None-Match", "\"1\""); }); //Assert response.StatusCode.Should().Be.EqualTo(HttpStatusCode.NotModified); }
30
“Close” to http
Very, very readable code
Very explicit routing
Embraces modularity
Embraces IoC/DI
Embraces testing
Runs anywhere
Why Nancy?
31
LightweightLow ceremony
Low cruft
Follows conventions
Open
Agile
Why REST + Nancy
32
Restbucks on Nancy: http://github.com/horsdal/Restbucks-on-Nancy
Rest in Practice: http://restinpractice.com/book.html
Nancy: www.nancyfx.org
Me:
Twitter: @chr_horsdal
Blog: horsdal.blogspot.com
email: [email protected]
More …