68
Creating and Consuming RESTful Web Services with WCF Ron Jacobs Sr. Technical Evangelist Platform Evangelism Microsoft Corporation

Creating and Consuming RESTful Web Services with WCF

  • Upload
    mada

  • View
    31

  • Download
    0

Embed Size (px)

DESCRIPTION

Creating and Consuming RESTful Web Services with WCF. Ron Jacobs Sr. Technical Evangelist Platform Evangelism Microsoft Corporation. Agenda. What is REST? Is REST SOA? Key REST principles Adventure Works REST API WCF Example Summary. 71 Slides 5 Demos I must be insane!. Resources. - PowerPoint PPT Presentation

Citation preview

Page 1: Creating and Consuming  RESTful  Web Services with WCF

Creating and Consuming RESTful Web Services with WCFRon JacobsSr. Technical EvangelistPlatform EvangelismMicrosoft Corporation

Page 2: Creating and Consuming  RESTful  Web Services with WCF

AgendaWhat is REST?Is REST SOA?Key REST principlesAdventure Works REST API WCF ExampleSummary

71 Slides5 DemosI must be insane!

Page 3: Creating and Consuming  RESTful  Web Services with WCF

ResourcesLeonard RichardsonSam Ruby

www.ronjacobs.comCodeSlides

Page 4: Creating and Consuming  RESTful  Web Services with WCF

WHAT IS REST?

“Representational state transfer (REST) is a style of software architecture for distributed hypermedia systems such as the World Wide Web.”http://en.wikipedia.org/wiki/Representational_State_Transfer

Page 5: Creating and Consuming  RESTful  Web Services with WCF

What is REST?Application state and functionality are resources Every resource has a URIAll resources share a uniform interface

HTTP

Page 6: Creating and Consuming  RESTful  Web Services with WCF

IS REST SOA?

“Protocol independence is a bug, not a feature”.

- Mark Baker

Page 7: Creating and Consuming  RESTful  Web Services with WCF

SOAP RESTWCF Test Client Notepad

Internet Explorer

IS REST SOA?REST is an architectural style that allows you to implement services with broad reach SOA is about services

SOA is not about protocol, transport, format etc.

5 HTTP Messages18,604 bytes“You entered: 1”

Page 8: Creating and Consuming  RESTful  Web Services with WCF

KEY REST PRINCIPLES

“The promise is that if you adhere to REST principles while designing your application, you will end up with a system that exploits the Web’s architecture to your benefit.”

-Stefan Tilkovhttp://www.infoq.com/articles/rest-introduction

Page 9: Creating and Consuming  RESTful  Web Services with WCF

Key REST PrinciplesGive every “thing” an ID Link things together Use standard methods Resources with multiple representations Communicate statelessly

Page 10: Creating and Consuming  RESTful  Web Services with WCF

Give every “thing” an IDExpose thing or collection things with a scheme that is ubiquitousEmbrace the URI

How to get it (http:// or net.tcp:// or net.msmq:// etc.)Where to get it (example.com)What to get (customer 1234)

Page 11: Creating and Consuming  RESTful  Web Services with WCF

Give every “thing” an ID

Customer C = GetCustomer(1234);

http://example.com/customers/1234

An API like this

Can be represented like this

Page 12: Creating and Consuming  RESTful  Web Services with WCF

Link Things TogetherHypermedia as the engine of application state

Just means that you should link things togetherPeople or apps can transition state by following linksLinks can be to the same app or to some other app

Page 13: Creating and Consuming  RESTful  Web Services with WCF

Link Things Together

<CustomerData> <Self>http://adventure-works.com/customer/1</Self> <CompanyName>A Bike Store</CompanyName> <CustomerID>1</CustomerID> <EmailAddress>[email protected]</EmailAddress> <FirstName>Orlando</FirstName> <LastName>Gee</LastName> <Orders>http://adventure-works.com/customer/1/orders</Orders> <RowID>3f5ae95e-b87d-4aed-95b4-c3797afcb74f</RowID></CustomerData>

http://search.live.com/results.aspx?q=Ron+Jacobs&first=11...

Page 14: Creating and Consuming  RESTful  Web Services with WCF

Use Standard Methods

public class Resource {

Resource(Uri u); Response Get(); Response Post(Request r); Response Put(Request r); Response Delete();Response Head();

}

Page 15: Creating and Consuming  RESTful  Web Services with WCF

Shift to Resource ThinkingOperation SQL Command HTTP VerbCreate a resource INSERT POST(a), PUTRead a resource SELECT GETUpdate a resource UPDATE PUT, POST(p)Delete a resource DELETE DELETEQuery Metadata (Systables) HEAD

INSERT INTO CUSTOMERS (...) VALUES (...)SQL

(POST) http://example.com/customers<Customer>...</Customer>

REST

Page 16: Creating and Consuming  RESTful  Web Services with WCF

Shift to Resource ThinkingOperation SQL Command HTTP VerbCreate a resource INSERT POSTRead a resource SELECT GETUpdate a resource UPDATE PUT (POST)Delete a resource DELETE DELETEQuery Metadata (Systables) HEAD

SELECT FROM CUSTOMERS WHERE ID=567SQL

(GET) http://example.com/customers/567REST

Page 17: Creating and Consuming  RESTful  Web Services with WCF

Resources as operationsThe result of an operation can be considered a resource

var result = CalculateShipping(“Redmond”, “NYC”);API

http://example.com/calculateShipping?from=“Redmond”&to=“NYC”

REST

Page 18: Creating and Consuming  RESTful  Web Services with WCF

Content NegotiationAllow the client to ask for what they want“I want XML”

“I want JSON”

“I want …” (HTML, CSV, etc.)

GET /customers/1234 HTTP/1.1Host: example.com Accept: text/xml

GET /customers/1234 HTTP/1.1Host: example.com Accept: text/json

JSR 311 features the ideaof extensions as a wayto do content negotiationwithout the headers as in/customers.xml /customers.json

Page 19: Creating and Consuming  RESTful  Web Services with WCF

Communicate StatelesslyStateless means that every request stands alone

Session is not requiredCan be bookmarked

Application State lives on the ClientEverything that is needed to complete the request must be included in the request

Resource State lives on the server(s)Stored in durable storage (typically)May be cached

Page 20: Creating and Consuming  RESTful  Web Services with WCF

ADVENTURE WORKS REST API

Implementation Time

Page 21: Creating and Consuming  RESTful  Web Services with WCF

AdventureWorks Customer APIURI Metho

dCollection Operation

/customers POST Customers Create

/customers/{custId} GET Customers Read

/customers/{custId} PUT Customers Update

/customers/{custId} DELETE Customers Delete

/customers/{custId}/Orders

GET Sales Read customer orders

Page 22: Creating and Consuming  RESTful  Web Services with WCF

HTTP GET

“Remember that GET is supposed to be a “safe” operation, i.e. the client does not accept any obligations (such as paying you for your services) or assume any responsibility, when all it does is follow a link by issuing a GET.”

-Stefan Tilkov http://www.infoq.com/articles/tilkov-rest-doubts

Page 23: Creating and Consuming  RESTful  Web Services with WCF

GET CUSTOMER DEMO

WebGet AttributeUriTemplateQuery String Parameters

http://rojacobsxps/AdventureWorksDev/api/customer/1

Page 24: Creating and Consuming  RESTful  Web Services with WCF

WebGet AttributeWebGet Indicates you want to use an HTTP GET for this methodMethod name is resource nameArguments are query string parameters

// GET a customer[OperationContract][WebGet]CustomerData GetCustomer(string customerId);

http://localhost/service.svc/GetCustomer?customerId=1

Page 25: Creating and Consuming  RESTful  Web Services with WCF

WebGet UriTemplateUriTemplate maps the URI to parameters in your method

Using parameters in the Uri makes them mandatory, query string parameters are optional.

// GET a customer[OperationContract][WebGet(UriTemplate = "customer/{customerId}")]CustomerData GetCustomer(string customerId);http://localhost/service.svc/Customer/1

Page 26: Creating and Consuming  RESTful  Web Services with WCF

Making your first RESTful Service

Create a WCF Service LibraryAdd a reference / usingSystem.ServiceModel.Web

Decorate your method with WebGetModify configuration

Change the binding from wsHttpBinding to webHttpBindingAdd the webHttp endpoint behavior to the endpoint

Note: WCF will start up without this behavior though it is not very useful configuration

Page 27: Creating and Consuming  RESTful  Web Services with WCF

Get CustomersReturns a collection of customers from the databaseIssues

Security – you can only see orders you are allowed to seePaging – stateless requests decide where to start

REST API

SOAP API

http://adventure-works.com/customer

Customer[] GetCustomers()

Page 28: Creating and Consuming  RESTful  Web Services with WCF

PagingAllows clients to request a subset of the collectionUse Query String parameters to specify start index and count

http://adventure-works.com/customer?start=200&count=25

Page 29: Creating and Consuming  RESTful  Web Services with WCF

Gotcha! // GET customers[OperationContract] [WebGet(UriTemplate="customer?start={start}&count={count}")]CustomerGroupingData GetCustomers(int start, int count);

// POST to customers[OperationContract][WebInvoke(UriTemplate = "customer")]CustomerData AppendCustomer(CustomerData customer);

405 Method not allowedhttp://adventure-works.com/customer

Page 30: Creating and Consuming  RESTful  Web Services with WCF

Why?The template matching engine tries to find the best matchThe more specific a match is, the betterWhen the URL contains just the resource “customer”The match for “customer” is a POST method

Return 405 Method not allowed

Page 31: Creating and Consuming  RESTful  Web Services with WCF

Why?Solution

Don’t include the query string parameters in the UriTemplateGet them instead from the WebOperationContext.CurrentUriTemplate is now just “customer” for both GET and POST

Page 32: Creating and Consuming  RESTful  Web Services with WCF

Solution // GET customers[OperationContract] [WebGet(UriTemplate = "customer")]CustomerGroupingData GetCustomers(int start, int count);

// POST to customers[OperationContract][WebInvoke(UriTemplate = "customer")]CustomerData AppendCustomer(CustomerData customer);

200 Okhttp://localhost/AdventureWorksDev/api/customer

Page 33: Creating and Consuming  RESTful  Web Services with WCF

Query String Parametersprivate string GetQueryString(string argName){ UriTemplateMatch match =

WebOperationContext.Current.IncomingRequest.UriTemplateMatch;

try { return match.QueryParameters[argName]; } catch (KeyNotFoundException) { return null; }}

Query String Parametersare found in here

Page 34: Creating and Consuming  RESTful  Web Services with WCF

CachingUse HttpRuntime.Cache to cache items on the server if it makes sense to do so

// Check the cacheCustomerData customerData = (CustomerData)HttpRuntime.Cache[requestUri.ToString()];

// Not found in the cacheif (customerData == null){ // Try to get the customer data customerData = CustomersCollection.GetCustomer(custId);

// Still not found if (customerData == null) { outResponse.SetStatusAsNotFound(string.Format("Customer Id {0} not found", customerId)); } else // found { // Set the headers outResponse.LastModified = customerData.LastModified; outResponse.ETag = customerData.ETag.ToString(); CacheCustomer(requestUri, customerData); }}

Page 35: Creating and Consuming  RESTful  Web Services with WCF

Client CachingAdd Expires or Cache-Control headers to provide clients with hints on cachingWCF Default: Cache-Control: private

No caching of private results

// Allow client to cache for 5 minutesoutResponse.Headers.Add("Cache-Control", "300");

Page 36: Creating and Consuming  RESTful  Web Services with WCF

Conditional GETHeaders used by clients to save bandwidth if they hold cached dataIf-Modified-Since: (Date)

Return the data only if it has been modified since (Date)

Page 37: Creating and Consuming  RESTful  Web Services with WCF

Conditional GETIf-None-Matches: (Etag)

Return the data only if there are no records matching this tag

If the data exists but has not been modified return 304 “Not Modified”

The server still has to verify that the resource exists and that it has not changed

Page 38: Creating and Consuming  RESTful  Web Services with WCF

Supporting If-Modified-SinceYour data should have a LastModified valueUpdate it whenever the data is written

// Set the headersoutResponse.LastModified = customerData.LastModified;

Page 39: Creating and Consuming  RESTful  Web Services with WCF

Supporting If-None-MatchesYour data should have a row versionThis data is returned in an Etag header as an opaque string

// Set the headersoutResponse.ETag = customerData.ETag.ToString();

Page 40: Creating and Consuming  RESTful  Web Services with WCF

Conditional GET Checkprivate static void CheckModifiedSince( IncomingWebRequestContext inRequest, OutgoingWebResponseContext outResponse, CustomerData customerData){ // Get the If-Modified-Since header DateTime? modifiedSince = GetIfModifiedSince(inRequest);

// Check for conditional get If-Modified-Since if (modifiedSince != null) { if (customerData.LastModified <= modifiedSince) { outResponse.SuppressEntityBody = true; outResponse.StatusCode = HttpStatusCode.NotModified; } }} Not Modified?

Suppress bodyReturn 304 “Not

Modified”

Page 41: Creating and Consuming  RESTful  Web Services with WCF

GET Response200 OK

GET successful304 Not Modified

Conditional GET did not find new data400 Bad Request

Problem with the request of some kind404 Not Found

Resource was not found500 Internal Server Error

Everything else

Page 42: Creating and Consuming  RESTful  Web Services with WCF

HTTP POST

“You can use it to create resources underneath a parent resource and you can use it to append extra data onto the current state of a resource.”

- RESTful Web Services

Page 43: Creating and Consuming  RESTful  Web Services with WCF

HTTP POSTPOST is ambiguously defined in the HTTP specPOST is the second most used RESTful verbOften referred to as POST(a) for “Append”

Posting to a collection means to append to that collectionAllows the server to determine the ultimate URI

Page 44: Creating and Consuming  RESTful  Web Services with WCF

HTTP POSTProblem

How to detect duplicate POST requests?Solutions

Use PUT (it’s Idempotent by nature)Schemes involving handshaking of some kind between the client and serverClient generated identifier for POST

Page 45: Creating and Consuming  RESTful  Web Services with WCF

POST to CustomersAppends a new customer to the collectionIssues

Security – Are you allowed to create a customer?Idempotency – is this a duplicate POST request?

REST API

SOAP API(POST) http://adventure-works.com/customers

CustomerData AppendCustomer(CustomerData customer);

Page 46: Creating and Consuming  RESTful  Web Services with WCF

POST Examplepublic CustomerData AppendCustomer(CustomerData customer){ OutgoingWebResponseContext outResponse = WebOperationContext.Current.OutgoingResponse;

try { CustomerData newCustomer = CustomersCollection.AppendCustomer(customer);

if (newCustomer.CustomerID != 0) { outResponse.SetStatusAsCreated(

BuildResourceUri("customer", newCustomer.CustomerID.ToString())); }

return newCustomer; } catch (CustomerRowIDExistsException) { outResponse.StatusCode = HttpStatusCode.Conflict; outResponse.StatusDescription = "RowID exists, it must be unique"; return null; } catch (Exception ex) { Log.Write(ex); throw; }}

Page 47: Creating and Consuming  RESTful  Web Services with WCF

POST(a) Response200 OK

POST successful400 Bad Request

Problem with the request of some kind409 Conflict

Resource already exists500 Internal Server Error

Everything else

Page 48: Creating and Consuming  RESTful  Web Services with WCF

Testing POST MethodsFiddler – http://www.fiddler2.com HTTP proxyUse machine name instead of localhost in URI

IIS hosting helps with thisBuild a request

Drag a GET request to the Request BuilderOpen a GET in Visual Studio for easy formatting of XML

Set the Request type to POSTSet the request body to valid XMLSet the Content-Type: text/xml

Page 49: Creating and Consuming  RESTful  Web Services with WCF

HTTP PUT

PUT is an idempotent way to create / update a resource

Page 50: Creating and Consuming  RESTful  Web Services with WCF

HTTP PUTCreates or Updates the resource

Completely replaces whatever was there before with the new contentUpdate the cache with new resource

Idempotent by designCreating or Updating record 123 multiple times should result in the same valueDo NOT do some kind of relative calculation

Page 51: Creating and Consuming  RESTful  Web Services with WCF

PUT and IDsIf you can allow the client to define an ID within a context that is unique, PUT can insert, otherwise PUT is used to update resourcesREST API

SOAP API

Note: the first arg comes from the URI, the customer data comes from the request body

(PUT) http://adventure-works.com/customers/123

CustomerData PutCustomer(string customerId, CustomerData customer);

Page 52: Creating and Consuming  RESTful  Web Services with WCF

PUT Response200 OK

Update successful201 Created

Insert Successful400 Bad Request

Problem with the request of some kind404 Not Found

Resource to update was not found500 Internal Server Error

Everything else

Page 53: Creating and Consuming  RESTful  Web Services with WCF

HTTP DELETE

DELETE is for uh... well... um... deleting things

Page 54: Creating and Consuming  RESTful  Web Services with WCF

DELETEUsed to delete a resourceIssues

Security – can you delete a resourceCache – need to remove it from the server cache

Page 55: Creating and Consuming  RESTful  Web Services with WCF

DELETE Response200 OK

Delete successful400 Bad Request

Problem with the request of some kind404 Not Found

Resource to delete was not found500 Internal Server Error

Everything else

Page 56: Creating and Consuming  RESTful  Web Services with WCF

URI MappingURI Method Maps Toapi/customer GET api.svc/customerapi/customer POST api.svc/customerapi/customer/{ID} GET api.svc/customer/{ID}api/customer/{ID} DELETE api.svc/customer/{ID}api/customer/{ID} PUT api.svc/customer/{ID}api/customer/{ID} HEAD api.svc/customer/{ID}

Page 58: Creating and Consuming  RESTful  Web Services with WCF

Gotchca! I installed my HttpModule in web.config under <system.web><httpModules>Not workingSearched blogs to find out that for IIS 7 you must install under <system.webServer><modules>If your HttpModule does not rewrite the URL correctly you will get 404 errors and have a hard time understanding why

Page 59: Creating and Consuming  RESTful  Web Services with WCF

Content NegotiationTrend is toward an extension syntax

Unfortunately you must specify the response format in the WebGet, WebInvoke attributeYou can dynamically choose your format by making your service return a stream and then serializing your content directly to the stream

http://adventure-works.com/customers.xhtmlhttp://adventure-works.com/customers.xmlhttp://adventure-works.com/customers.json

Page 60: Creating and Consuming  RESTful  Web Services with WCF

Two Servicesapixml.svc for XMLapijson.svc for JSONUriMapper code looks for an extension on the resource and maps it to the appropriate service

default is XML2 .SVC files means two classes that implement 2 different contracts

Page 61: Creating and Consuming  RESTful  Web Services with WCF

Class Diagram

Page 62: Creating and Consuming  RESTful  Web Services with WCF

URI MappingURI Method Maps Toapi/customer GET apixml.svc/customerapi/customer POST apixml.svc/customerapi/customer/{ID} GET apixml.svc/customer/{ID}api/customer/{ID} DELETE apixml.svc/customer/{ID}api/customer/{ID} PUT apixml.svc/customer/{ID}api/customer/{ID} HEAD apixml.svc/customer/{ID}api/customer.json GET apijson.svc/customerapi/customer.json POST apijson.svc/customerapi/customer/{ID}.json

GET apijson.svc/customer/{ID}

api/customer/{ID}.json

DELETE apijson.svc/customer/{ID}

api/customer/{ID}.json

PUT apijson.svc/customer/{ID}

api/customer/{ID}.json

HEAD apijson.svc/customer/{ID}

Page 63: Creating and Consuming  RESTful  Web Services with WCF

Content Negotiation Demo

http://rojacobsxps/AdventureWorksDev

Page 64: Creating and Consuming  RESTful  Web Services with WCF

Consuming a RESTful ServiceUse WCF or HttpWebRequestNeed a ServiceContract interface

Copy from serviceBuild from scratch

Build UriTemplates that will match up to the service

Page 65: Creating and Consuming  RESTful  Web Services with WCF

Data Contract SchemaExport Schema from Assembly

svcutil foo.dll /dconlyGenerate data contracts from xsd files

svcutil *.xsd /dconly

Page 66: Creating and Consuming  RESTful  Web Services with WCF

Using WCF on the Clientpublic CustomerData GetCustomer(string customerId, Guid? eTag, DateTime? modifiedSince){ using (var factory = new WebChannelFactory<IAdventureWorksServiceXml>("AdventureWorks")) { IAdventureWorksServiceXml service = factory.CreateChannel();

using (OperationContextScope scope = new OperationContextScope( (IContextChannel)service)) { OutgoingWebRequestContext request = WebOperationContext.Current.OutgoingRequest;

if (eTag != null) request.IfNoneMatch = eTag.ToString();

if (modifiedSince != null) { DateTimeFormatInfo formatInfo = CultureInfo.InvariantCulture.DateTimeFormat;

// RFC1123Pattern request.IfModifiedSince = modifiedSince.Value.ToString("r", formatInfo); } return service.GetCustomer(customerId); } }}

You must createa scope to access the

context

Page 67: Creating and Consuming  RESTful  Web Services with WCF

SummaryRESTful services extend the reach of HTTP to your SOARESTful design is harder than you might thinkImplementation has some tricky issues to overcome

Page 68: Creating and Consuming  RESTful  Web Services with WCF