Statistics
| Branch: | Revision:

root / trunk / hammock / README.markdown @ 0eea575a

History | View | Annotate | Download (19.3 kB)

1
# Hammock - REST, easy.
2

    
3
#### Welcome to the Programmable Web
4

    
5
If you haven't had the requirement to connect with another web service in your projects, you soon will. 
6
In an increasingly connected world, where the presence of an Application Programming Interface (API) is 
7
as essential to a growing company's online success as a blog was five years ago, it's inevitable that you 
8
will need to build software that integrates with multiple sources of data from numerous partners. 
9
This is achieved through API programming, and by way of adoption this is most often accomplished through
10
HTTP programming, based on the REST architectural style (to varying degrees), which provides convenient 
11
metaphors for creating, retrieving, updating, and deleting data over HTTP, as well as providing smooth 
12
transitions from one set of data to the next through the URIs embedded in a literal web of results. 
13

    
14
Notable companies that provide a REST-like API for their customers include Twitter, Facebook, Google, Amazon, 
15
and Microsoft. There are too many more to mention. When you are tasked with consuming multiple APIs, you need a 
16
simple, expressive toolset that allows you to rapidly build client code that is easy to understand, while 
17
allowing you to meet the needs of multiple styles, authentication requirements, and messaging. 
18
This is where Hammock comes in.
19

    
20
#### The Philosophy and Purpose of Hammock
21

    
22
Hammock's philosophy is simple: _REST, easy._
23
Hammock's purpose is to provide a common foundation for RESTful communication in .NET that gives developers a 
24
clean and easy way to build RESTful client libraries, or consume RESTful services from their applications, 
25
without compromising time or quality. It shares similar aspirations with other web API utilities, but focuses on 
26
completeness, performance, and getting out of your way. 
27

    
28
#### A Brief History of Hammock
29

    
30
Hammock grew from the networking code in TweetSharp (http://tweetsharp.com), .NET's most popular Twitter API library. 
31
When the library's authors found themselves maintaining numerous API projects, they turned to TweetSharp's foundation again 
32
and again, pulling in snippets and adding new features. As APIs continued to explode online, it made sense to consolidate 
33
the code into a common library, add an abstraction layer to sweeten its use, and open it up to the public to benefit. 
34
The project was launched feature complete in April 2010 and is still in active development.
35

    
36
#### What Does It Do?
37

    
38
Hammock provides classes to help you consume RESTful services or develop your own client API libraries with a 
39
minimum of effort. It supports asynchronous operation, query caching and mocking, periodic and rate limited tasks,
40
object model serialization, multi-part forms, Basic and OAuth authentication, and timeout and retry policies. 
41
It is designed to allow you to extend it easily, so you can provide your own serialization scheme, custom validation, 
42
rate limiting rules, and web credentials. It also helps you unify projects across a wide range of platforms and 
43
devices, as it works on .NET, .NET Compact Framework, Silverlight, Windows Phone, Mono, MonoTouch, and MonoDroid.
44

    
45

    
46

    
47
## What You Need to Get Started
48
[]
49

    
50

    
51

    
52
## Creating REST Requests
53

    
54
This section covers creating a RESTful request issued from your application to the service you intend to consume.
55
Building requests in Hammock uses the language of HTTP, to make it easier to find the methods you're looking for.
56
If you need to add an HTTP header to your outgoing HttpRequest, you'd use the AddHeader method, and so on.
57

    
58
### RestClient, RestRequest, and RestResponse
59
These three classes form the backbone of Hammock, and provide all of the properties you need to build RESTful 
60
requests and process their results. RestClient and RestRequest have a special relationship; any property that can
61
be set on both classes obeys a hierarchical relationship, so you are free to set common values on RestClient, 
62
and override them with RestRequest, depending on your needs. This makes it easy and efficient to create a 
63
RestClient with the most common request values, and then use specific request values for each request sent through 
64
the client. An obvious example of that could be credentials; if your authorization requirements never change between
65
requests for the client you are consuming, then setting credentials on RestClient makes the most sense, and will allow
66
you to omit setting them on every RestRequest. 
67

    
68
As a working example, here is a simple Twitter API RestClient that stores basic information about the service, and a 
69
RestRequest that sets values, like credentials specific to the user, and the target API path, using your application.
70

    
71
    BasicAuthCredentials credentials = new BasicAuthCredentials
72
                                      {
73
                                          Username = "username",
74
                                          Password = "password"
75
                                      };
76
    RestClient client = new RestClient
77
             {
78
                 Authority = "http://api.twitter.com",
79
                 VersionPath = "1"
80
             };
81
    RestRequest request = new RestRequest
82
              {
83
                  Credentials = credentials,
84
                  Path = "statuses/home_timeline.json"
85
              };          
86
    RestResponse response = client.Request(request);
87

    
88

    
89
### Building Request Paths
90

    
91
The final URL for a request path is constructed first from the RestClient's Authority property. From there, either RestRequest 
92
or RestClient is inspected for the VersionPath, and Path values. The VersionPath is always inserted between the Authority and 
93
Path values. It is useful for targeting specific versions of an API endpoint, or for any URI path fragment that varies between 
94
the authority and the target path. Hammock can handle secure endpoints, just ensure you include the HTTPS scheme when declaring 
95
your authority.
96

    
97
// TODO
98

    
99
Once your path is defined, you still need to add request state. State in a REST request refers to the query string parameters, 
100
POST parameters or fields, request headers, or entity body contents that make up the data sent to the specified path. 
101
In Hammock, you can do this one of two ways: using a traditional method API, or applying attributes to a custom class that is passed 
102
to Hammock.
103

    
104
### Request State Using Method Style
105

    
106
Properties and methods on both RestClient and RestRequest form the basis of most requests in Hammock: WebMethod, UserAgent, AddHeader, 
107
AddParameter, AddFile, AddField, and Entity form the list of major members. Methods that begin with Add allow setting multiple values, 
108
while the properties allow setting a single value. All of these members are allowable on either class, making it easier to build up 
109
clients with default behavior. The UserAgent property is a formalized way of setting the User-Agent request property, but it can be 
110
set using either the property or the AddHeader method. If both are explicitly set...
111

    
112
[]
113

    
114
The Entity property allows you to set the body of a POST or PUT request, and it can be set to any object. If you do not specify a 
115
WebMethod before setting the Entity, Hammock will assume you are making a POST request.
116

    
117
[]
118

    
119
### Request State Using Attribute Style
120

    
121
Some APIs, like TweetSharp, need a more stateful approach to assembling REST requests. For example, you may want to collect information 
122
about a particular request over time in your own class, and at request time, transform it into the appropriate header, parameter, 
123
user agent, and entity body values. You can certainly do this using the previous method style, but it would result in a lot of 
124
"left right" coding, or mapping state from your code to the most appropriate RestClient or RestRequest properties. To avoid having to 
125
perform this work, and to enable some advanced validation and transforming capabilities, you can use Hammock's attributes style.
126

    
127
[]
128

    
129
### Using IWebQueryInfo
130

    
131
The IWebQueryInfo interface is an empty marker interface you can attach to the class you intend to pass to Hammock to build requests. 
132
Once your class implements IWebQueryInfo, you can use the built-in specialized attributes to declare essential request data like 
133
parameters, headers, user agent, and entity, and you can use ours or your own validation attributes to transform values or throw 
134
exceptions based on property values at the time of the request. Attributes are only applied to public properties defined in your class. 
135
Once you have an instance of your IWebQueryInfo class with attributes applied, you can set the Info property of RestClient or 
136
RestRequest to that instance, and Hammock will set all request values appropriately. As always, the Info set on RestRequest will 
137
override the Info set on RestClient.
138

    
139
[]
140

    
141
### Specialized Attributes
142

    
143
Specialized attributes set fundamental HTTP API elements on outgoing requests. By marking a public property with UserAgentAttribute, 
144
its value is used as the request's "User-Agent" header value. ParameterAttribute and HeaderAttribute are used to set request URL or 
145
POST parameters and headers respectively, and require a name string to identify the name of the name-value pair to use. The next 
146
listing demonstrates an IWebQueryInfo class with some fundamental request values using attributes.
147

    
148
[]
149

    
150
### Validation Attributes
151

    
152
Hammock includes a number of validation attributes for your use, that we've found are useful for building libraries. The purpose of a 
153
validation attribute is to transform, at request time, the value of the decorated public property. This gives you an opportunity to 
154
course correct for your needs (such as is the case with DateFormatAttribute and BooleanToIntegerAttribute), or prevent issuing requests 
155
that don't meet specific criteria (as in SpecificationAttribute and RequiredAttribute). You can also derive from ValidationAttribute 
156
to create your own custom validation or transformations. The following example demonstrates an IWebQueryInfo class with typical 
157
validation attributes, including the use of a Hammock-provided ValidEmailSpecification specification and SpecificationAttribute. 
158
In the example, a ValidationException will throw if the value of Email is not a valid email address at the time of a request, and if 
159
it were, the value of the valid email address would be passed in a parameter named "Contact" (either in the URL with GET or in the POST 
160
body, depending on the request).
161

    
162
    // A IWebQueryInfo implementation for custom validation
163
    public class MyCustomInfo : IWebQueryInfo
164
    {
165
        [Parameter("Contact")]
166
        [Specification(typeof(ValidEmailSpecification))]
167
        public string Email { get; set; }
168
    }
169

    
170
    // Example code that would throw an exception due to an invalid email address
171
    IWebQueryInfo info = new MyCustomInfo { Email = "nowhere" };
172
    
173
    RestClient client = new RestClient
174
    {
175
        Authority = "http://nowhere.com",
176
        Info = info
177
    };
178

    
179
    RestRequest request = new RestRequest
180
    {
181
        Path = "fast"
182
    };
183

    
184
    client.Request(request);
185

    
186
    // From Hammock.Validation namespace, for illustration purposes
187
    public class ValidEmailSpecification : HammockSpecification<string>
188
    {
189
        // Accepts names, i.e. John Smith <john@johnsmith.com>
190
        private static readonly Regex _names =
191
           new Regex(
192
                @"\w*<([-_a-z0-9'+*$^&%=~!?{}]+(?:\.[-_a-z0-9'+*$^&%=~!?{}]+)*@(?:(?![-.])[-a-z0-9.]+(?<![-.])\.[a-z]{2,6}|\d{1,3}(?:\.\d{1,3}){3})(?::\d+)?)>",
193
                RegexOptions.Compiled | RegexOptions.IgnoreCase
194
                );
195

    
196
        // Just an email address
197
        private static readonly Regex _explicit =
198
            new Regex(
199
                @"^[-_a-z0-9'+*$^&%=~!?{}]+(?:\.[-_a-z0-9'+*$^&%=~!?{}]+)*@(?:(?![-.])[-a-z0-9.]+(?<![-.])\.[a-z]{2,6}|\d{1,3}(?:\.\d{1,3}){3})(?::\d+)?$",
200
                RegexOptions.Compiled | RegexOptions.IgnoreCase
201
                );
202

    
203
        public override bool IsSatisfiedBy(string instance)
204
        {
205
            var result = _explicit.IsMatch(instance) || _names.IsMatch(instance);
206
            return result;
207
        }
208
    }
209

    
210

    
211
### Writing a Custom Validation Attribute
212

    
213
You can supply your own transformation or validation behaviors by extending the abstract ValidationAttribute class and providing an 
214
implementation for the single method defined. The next code example shows a custom implementation of a validation attribute that 
215
uppercases a string value, and converts null values to string literals. The value passed to TransformValue could be _null_, so your 
216
validation attribute should account for this.
217

    
218
### Combining Method and Attribute Styles
219

    
220
You can use a combination of method and attribute style programming to build your requests, just keep in mind that Hammock will 
221
honor the RestClient and then RestRequest values you specify before using attributes. In other words, if you specify a parameter or 
222
user agent attribute on a class with IWebQueryInfo, that has the same name as a parameter you pass to RestClient, the value of that 
223
parameter or the user agent will take the RestClient value, not the attribute value. Headers will continue to combine values from all 
224
sources.
225

    
226

    
227

    
228
## Sending Web API Requests
229

    
230
After your request is ready for transport, you can execute it sequentially or asynchronously to return a RestResponse object. 
231
RestResponse is also available as a generic class scoped to the expected return value. This is useful if you are expecting a response 
232
entity that you can deserialize into a common class, making the process of converting content from the HTTP seam to regular .NET 
233
classes transparent; in this case the object is available in `RestResponse<T>`'s ContentEntity property. If you do not specify an 
234
`IDeserializer` on `RestRequest` or `RestClient`, but you expect a `ContentEntity` anyway, it will return a null value. If you are not 
235
intending to capture strongly typed responses, you can obtain the raw results from the Content property or cast the `ContentEntity` 
236
yourself provided you also supplied a ResponseEntityType along with your non-generic request, otherwise the deserializer would not know 
237
which target type you intended. Other diagnostic information about the request is available in the RestResponse class, and Hammock 
238
will output essential information about requests and responses through .NET's tracing capability, so you can attach your own listeners 
239
to log important details.
240

    
241
[]
242

    
243
### Basic Operation
244

    
245
There are two methods available to send sequential requests when your application will wait for the results: `Request`, and `Request<T>`. 
246
The only difference is whether you expect to deserialize the response body as a .NET class before building the RestResponse object.
247

    
248
<pre>
249
using System;
250
using System.IO;
251
using Hammock.Serialization;
252
using Newtonsoft.Json;
253

    
254
namespace Hammock.Extras
255
{
256
    public class HammockJsonDotNetSerializer : ISerializer, IDeserializer
257
    {
258
        private readonly JsonSerializer _serializer;
259

    
260
        public HammockJsonDotNetSerializer(JsonSerializerSettings settings)
261
        {
262
            _serializer = new JsonSerializer
263
            {
264
                ConstructorHandling = settings.ConstructorHandling,
265
                ContractResolver = settings.ContractResolver,
266
                ObjectCreationHandling = settings.ObjectCreationHandling,
267
                MissingMemberHandling = settings.MissingMemberHandling,
268
                DefaultValueHandling = settings.DefaultValueHandling,
269
                NullValueHandling = settings.NullValueHandling
270
            };
271

    
272
            foreach (var converter in settings.Converters)
273
            {
274
                _serializer.Converters.Add(converter);
275
            }
276
        }
277

    
278
        #region IDeserializer Members
279

    
280
        public virtual object Deserialize(string content, Type type)
281
        {
282
            using (var sr = new StringReader(content))
283
            {
284
                using (var jtr = new JsonTextReader(sr))
285
                {
286
                    return _serializer.Deserialize(jtr, type);
287
                }
288
            }
289
        }
290

    
291
        public virtual T Deserialize<T>(string content)
292
        {
293
            using (var sr = new StringReader(content))
294
            {
295
                using (var jtr = new JsonTextReader(sr))
296
                {
297
                    return _serializer.Deserialize<T>(jtr);
298
                }
299
            }
300
        }
301

    
302
        #endregion
303

    
304
        #region ISerializer Members
305

    
306
        public virtual string Serialize(object instance, Type type)
307
        {
308
            using (var sw = new StringWriter())
309
            {
310
                using (var jtw = new JsonTextWriter(sw))
311
                {
312
                    jtw = Formatting.Indented;
313
                    jtw = '"';
314

    
315
                    _serializer.Serialize(jtw, instance);
316
                    var result = sw();
317
                    return result;
318
                }
319
            }
320
        }
321

    
322
        public virtual string ContentType
323
        {
324
            get { return "application/json"; }
325
        }
326

    
327
        public virtual Encoding ContentEncoding
328
        {
329
            get { return Encoding.UTF8; }
330
        }
331

    
332
        #endregion
333
    }
334
}
335
</pre>
336

    
337
### Asynchronous Operation
338
[]
339

    
340
### Using RetryPolicy and Timeout
341
[]
342

    
343
### Caching Responses
344
[]
345

    
346
### Mocking Responses
347
[]
348

    
349
### Authenticating Requests
350
[]
351

    
352
#### Using BasicAuthCredentials
353
[]
354

    
355
#### Using OAuthCredentials
356
[]
357

    
358
#### Creating Custom Credentials
359
[]
360

    
361
### Request Tracing
362
[]
363

    
364
### Entity Serialization
365

    
366
Hammock provides hooks to allow you to serialize objects into your preferred format, usually JSON or XML when sending a POST or PUT 
367
request. This is a common scenario in many APIs that expect an entity when creating or updating records. Similarly, you can attach a 
368
deserializer to automatically convert a server entity into a regular class, to make it easy to work with in your code.
369

    
370
Hammock doesn't push a particular serialization strategy on you. There are already too many ways to serialize and deserialize objects 
371
(communiy favorites are JSON.NET http://codeplex.com/json and ServiceStack TypeSerializer http://code.google.com/p/servicestack/wiki/TypeSerializer, 
372
and we don't want to introduce another one.
373

    
374
You decide how you will serialize or deserialize your entities, and provide an implementation of ISerializer and IDeserializer to do
375
the work. That said, Hammock does include stock .NET serializers that will serialize and deserialize entities based on 
376
DataContractSerializer, DataContractJsonSerializer, JavaScriptSerializer, and XmlSerializer, so if you are already using those schemes 
377
in your code, hooking them up is straightforward.
378

    
379
#### Creating Custom Serializers
380

    
381
If you are using a different serialization scheme, you will need to provide an implementation of ISerializer, IDeserializer, or both, 
382
typically in the same concrete class, depending on your needs. One popular serialization library for JSON (with support for XML) is 
383
JSON.NET, which is used in TweetSharp. Here is an example implementation of a custom Hammock serializer that will let you use JSON.NET 
384
to serialize and deserialize your request and response entities, respectively. This example is provided in the Hammock.Extras project 
385
included with the source code.
386

    
387
<pre>
388
</pre>
389

    
390
#### Achieving POCO for XML and JSON Entities
391

    
392
#### Dynamic serialization in .NET 4.0
393

    
394
### Periodic Tasks
395

    
396
### Rate Limiting
397

    
398
## Handling REST Responses
399
### Deserializing Responses
400
### Handling Errors
401

    
402
## Walkthrough: Postmark (JSON)
403

    
404
## Walkthrough: FreshBooks (XML)
405

    
406
## Walkthrough: Twitter (OAuth 1.0a)
407
### OAuth 1.0a Authentication
408
### Credential Proxies using OAuth Echo 
409

    
410
## Walkthrough: Facebook Graph (OAuth 2.0)
411
### OAuth 2.0 Authentication
412

    
413