WCF Web API Self Hosting with Unity

WCF Web API Preview 4 has been out for sometime and most examples available today still use IIS, I think only handful of example I have found uses self hosting approach and they are too simplistic or forces your service to be singleton by passing instance so I decided to provide a sample similar to the one that is available for IIS but uses and IOC container.

Configuration looks like this.

1 static void Main(string[] args) 2 { 3 4 var container = new UnityContainer(); 5 container.RegisterType(typeof(PersonResource)); 6 container.RegisterType(typeof(PeopleResource)); 7 container.RegisterType( 8 typeof(IPersonRepository), typeof(PersonRespository), 9 new ContainerControlledLifetimeManager()); 10 11 var config = HttpHostConfiguration.Create() 12 .AddFormatters(new MediaTypeFormatter[] { new JpgFormatter() }) 13 .SetResourceFactory(new UnityFactory(container)) 14 .SetErrorHandler(new CustomErrorHandler()) 15 .SetOperationHandlerFactory(new OperationHandlerFactory()) 16 .AddMessageHandlers(typeof(LoggingChannel), 17 typeof(UriFormatExtensionMessageChannel)); 18 19 var serviceHost = new UnityHttpServiceHost(typeof(PersonResource),config, 20 new Uri("http://localhost:8090/person")) 21 {Container = container}; 22 serviceHost.Open(); 23 PrintEndpointDescription(serviceHost); 24 25 Console.WriteLine("Press any key to exit"); 26 Console.ReadLine(); 27 serviceHost.Close(); 28 }

The magic is done as a part of UnityHttpServiceHost class which sets up binding, behavior and hook into the unity pipeline.

1 public class UnityHttpServiceHost : HttpServiceHost 2 { 3 public UnityContainer Container { set; get; } 4 5 public HttpHostConfiguration Configuration { get; set; } 6 7 public UnityHttpServiceHost() 8 : base() 9 { 10 Container = new UnityContainer(); 11 } 12 13 public UnityHttpServiceHost(Type serviceType, IHttpHostConfigurationBuilder builder, Uri baseAddresses) 14 : base(serviceType, baseAddresses) 15 { 16 Container = new UnityContainer(); 17 ServiceType = serviceType; 18 if (builder == null) 19 this.Configure(new HttpHostConfiguration()); 20 else 21 Configure(builder.Configuration); 22 23 } 24 25 public Type ServiceType { get; set; } 26 27 public override void AddServiceEndpoint(ServiceEndpoint endpoint) 28 { 29 var httpEndpoint = (HttpEndpoint)endpoint; 30 httpEndpoint.Binding = new HttpBinding(); 31 if (Configuration != null) 32 { 33 httpEndpoint.OperationHandlerFactory = Configuration.OperationHandlerFactory; 34 httpEndpoint.MessageHandlerFactory = Configuration.MessageHandlerFactory; 35 if (this.Configuration.ErrorHandler != null) 36 { 37 var behavior = endpoint.Behaviors.Remove<HttpBehavior>(); 38 endpoint.Behaviors.Add(new HttpBehaviorWithErrorHandler(this.Configuration.ErrorHandler) 39 { 40 OperationHandlerFactory = this.Configuration.OperationHandlerFactory 41 }); 42 } 43 if (this.Configuration.InstanceFactory != null) 44 { 45 var behavior = 46 new InstanceProviderBehavior(new ResourceFactoryProvider(this.ServiceType, 47 this.Configuration.InstanceFactory)); 48 endpoint.Behaviors.Add(behavior); 49 } 50 } 51 base.AddServiceEndpoint(endpoint); 52 } 53 54 private void Configure(HttpHostConfiguration configuration) 55 { 56 this.Configuration = configuration; 57 this.OperationHandlerFactory = configuration.OperationHandlerFactory; 58 this.MessageHandlerFactory = configuration.MessageHandlerFactory; 59 AddDefaultEndpoints(); 60 } 61 62 protected override void OnOpening() 63 { 64 new UnityServiceBehavior(Container).AddToHost(this); 65 base.OnOpening(); 66 } 67 68 }

UnityServiceBehavior looks like following. It uses UnityInstanceProvider which implements IInstanceProvider interface to manage the lifetime of resources.

1 public class UnityServiceBehavior : IServiceBehavior 2 { 3 public UnityInstanceProvider InstanceProvider 4 { 5 get; set; 6 } 7 8 private ServiceHost serviceHost = null; 9 10 public UnityServiceBehavior() 11 { 12 InstanceProvider = new UnityInstanceProvider(); 13 } 14 public UnityServiceBehavior(UnityContainer unity) 15 { 16 InstanceProvider = new UnityInstanceProvider {Container = unity}; 17 } 18 19 public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) 20 { 21 //throw new NotImplementedException(); 22 } 23 24 public void ApplyDispatchBehavior(ServiceDescription serviceDescription,ServiceHostBase serviceHostBase) 25 { 26 foreach (ChannelDispatcherBase cdb 27 in serviceHostBase.ChannelDispatchers) 28 { 29 var cd= cdb as ChannelDispatcher; 30 if (cd != null) 31 { 32 foreach (EndpointDispatcher ed 33 in cd.Endpoints) 34 { 35 InstanceProvider.ServiceType= serviceDescription.ServiceType; 36 ed.DispatchRuntime.InstanceProvider = InstanceProvider; 37 38 } 39 } 40 } 41 }

Complete Sample can be downloaded from here

Advertisements

Introducing WCF Web API

Introduction

WCF is a highly sophisticated technology built on the foundation of replaceable transport. It is optimised for complex scenarios however only a handful of developers ever need to deal with those scenarios.

Most developers care a lot about the reach of the application and one of the best and easiest ways to do this is by supporting HTTP. WCF has always supported HTTP since its inception however the support was far from ideal because it could never take full advantage of HTTP.

HTTP is very well understood technology. Nearly every connected device available today supports one or other form of HTTP. If you speak the language of the Web, almost every consumer can consume your application.

WCF Web API fully implements HTTP Specification. It is important to note that WCF Web API is built on top of existing WCF and not replacing the existing WCF as we have come to know.

HTTP Support in WCF

HTTP Support has been part of WCF as part of various HTTP bindings, 3.5 SP1 made it easy to consume the response in XML and JSON. However it has never supported the features that make HTTP really powerful like full access to request and response, caching etc. HTTP is extremely flexible in the sense that it allows the body of the message to be presented in thousands of formats. WCF Web API allows this nicely by providing classes where you can provide support for any format over HTTP.

Architecture

clip_image002

Fig 1.0 Architecture Diagram

WCF Web API hooks into the extensibility mechanism of WCF. At the top layer it consists of resource class which consist of set of operation which receives HTTP Request and return HTTP Response.

Once the request is initiated from the client, WCF intercepts the request and start processing, it goes through its usual channel pipeline and once request is received in the form of bytes from the transport layer, it is converted from bytes to HttpRequestMessage.

After creation, the HttpRequestMessage instance passes through a sequence of message handlers. Each one of these message handlers receives an HttpRequestMessage and returns an HttpResponseMessage asynchronously.

After all the message handlers are processed, the operation is dispatched by first selecting the operation based on the request method name and Uri.

The next processing phase is responsible for producing the parameter set required by the selected operation, from the request message. These parameters are produced by operation handlers. These operation handlers may also be used to perform other type of operation specific message processing.

Before the operation is called, a resource class instance is obtained.

At the topmost layer, the operation is invoked using the parameters computed by the operation handlers.

After the operation’s invocation, the output parameters pass through a sequence of response operation handlers.

Also, if the response message was not returned by the operation, it is created in this phase.

Finally, the response message flows down through the message handlers until being turned into a byte sequence and written into the transport layer

Examples of Use of HTTP Verbs with WCF Web API

1. “GET” Example

This example takes a single parameter and returns fully typed HTTPResponseMessage of type person with the use of verb “GET”. Please note the use of HTTP natively and passing the Status Code in the standard HTTP format.

1 [WebGet(UriTemplate = "{id}")] 2 public HttpResponseMessage<Person> Get(int id) 3 { 4 5 var person = _repository.Get(id); 6 var response=new HttpResponseMessage<Person>(null); 7 if (person==null) 8 { 9 response.StatusCode = HttpStatusCode.NotFound; 10 } 11 else 12 { 13 response.Content = new ObjectContent<Person>(person); 14 } 15 return response; 16 }

2. “PUT” Example

This example takes two parameters and demonstrates how you can use “PUT”

1 [WebInvoke(UriTemplate = "{id}", Method = "PUT")] 2 public HttpResponseMessage<Person> Put(int id, Person person) 3 { 4 Get(id); 5 _repository.Update(person); 6 return new HttpResponseMessage<Person>(person); 7 }

3. GET with Queryable” Example

This example demonstrates OData style querying capabilities. More specifically you can retrieve records, apply filtering and also order them.

1 [WebGet(UriTemplate = "")] 2 public IQueryable<Person> Get() 3 { 4 return _repository.GetAll().AsQueryable(); 5 }

You can write queries In the format like

http://localhost/oData.Web/people?$filter=Id%20eq%201

Components of WCF Web API

WCF Web API is divided into following sections

1. Media Type Formatters

MediaTypeFormatters enables content negotiation. I.e. it makes it easier to support other format besides XML & JSON. If you want a resource to represent via specific format say iCal, just author a formatter and plug it in the pipeline. Following example shows how to represent a resource in a JpgFormatter.

1 public class JpgFormatter : MediaTypeFormatter 2 { 3 4 public JpgFormatter() 5 { 6 SupportedMediaTypes.Add(new MediaTypeHeaderValue("image/jpg")); 7 } 8 9 protected override bool OnCanReadType(Type type) 10 { 11 return true; 12 } 13 14 protected override bool OnCanWriteType(Type type) 15 { 16 return (type == typeof(Person)); 17 } 18 19 20 public override object OnReadFromStream(Type type, Stream stream, 21 HttpContentHeaders contentHeaders) 22 { 23 throw new NotImplementedException(); 24 } 25 26 public override void OnWriteToStream(Type type, object value, 27 Stream stream, HttpContentHeaders contentHeaders, 28 System.Net.TransportContext context) 29 { 30 var person = value as Person; 31 if (person != null) 32 { 33 { 34 var path = string.Format(CultureInfo.InvariantCulture, @"{0}bin\Images\Image{1}.jpg", 35 AppDomain.CurrentDomain.BaseDirectory, (Convert.ToInt32(person.Id) % 3) + 1); 36 using (var fileStream = new FileStream(path, FileMode.Open)) 37 { 38 var bytes = new byte[fileStream.Length]; 39 fileStream.Read(bytes, 0, (int)fileStream.Length); 40 stream.Write(bytes, 0, (int)fileStream.Length); 41 } 42 } 43 } 44 } 45 46 }

2. Message Handlers

WCF Web API provides low level message handlers as a mechanism to provide the hook into the HTTP Message Pipeline. You can do things like auditing of messages, provide security mechanisms etc. Following shows you an example of Logging Channel which hooks into the pipeline and then writes messages to trace.

1 public class LoggingChannel:DelegatingChannel 2 { 3 public LoggingChannel(HttpMessageChannel handler):base(handler) 4 { 5 } 6 7 protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, 8 System.Threading.CancellationToken cancellationToken) 9 { 10 System.Diagnostics.Trace.TraceInformation("Begin Request: {0} {1}", request.Method, request.RequestUri); 11 return base.SendAsync(request, cancellationToken); 12 } 13 }

 

3. Operation Handlers

Operation Handlers provides mechanism to intercept request before and after an operation is invoked. You can provide multiple operation handlers for the same operation. You can also provide Operation Handler for either single operation or all operation if required.

1 public class OperationHandlerFactory:HttpOperationHandlerFactory 2 { 3 4 5 protected override Collection<.HttpOperationHandler> OnCreateRequestHandlers(ServiceEndpoint endpoint, 6 HttpOperationDescription operation) 7 { 8 9 var baseHandlers = base.OnCreateRequestHandlers(endpoint, operation); 10 if (operation.InputParameters.Any(p => p.Type == typeof(int))) 11 { 12 baseHandlers.Add(new CustomRequestHandler(operation.InputParameters.FirstOrDefault().Name)); 13 } 14 15 baseHandlers.Add(new ZipOperationHandler(operation.InputParameters.FirstOrDefault().Name)); 16 baseHandlers.Insert(baseHandlers.Count() - 1, 17 new ZipOperationHandler(operation.InputParameters.FirstOrDefault().Name)); 18 19 return baseHandlers; 20 21 } 22 23 protected override Collection<HttpOperationHandler> OnCreateResponseHandlers(ServiceEndpoint endpoint, 24 HttpOperationDescription operation) 25 { 26 var baseHandlers = base.OnCreateRequestHandlers(endpoint, operation); 27 28 baseHandlers.Add(new ZipOperationHandler(operation.InputParameters.FirstOrDefault().Name)); 29 if (operation.Name.Contains("Get")) 30 { 31 baseHandlers.Add(new ZipOperationHandler(operation.InputParameters.FirstOrDefault().Name)); 32 } 33 34 return baseHandlers; 35 } 36 37 }

1 public class CustomRequestHandler:HttpOperationHandler<HttpRequestMessage,int> 2 { 3 public CustomRequestHandler(string outputParameterName) : base(outputParameterName) 4 { 5 } 6 7 public override int OnHandle(HttpRequestMessage input) 8 { 9 var stringValue=input.Content.ReadAsString(); 10 return 1; 11 } 12 }

4. Http Classes

WCF Web API provides two classes HttpRequestMessage and HttpResponseMessage for HTTP request and HTTP Response respectively.

5. HTTP Client

HTTP client can be used to connect REST based web services in a fully typed manner. You can use HttpClient to connect to web service, set headers, get fully typed response and work with the data instead of raw parsing. It supports Sync and Async methods to work with the resource.

1 const string address = "http://localhost/odata.web/people/"; 2 var client = new System.Net.Http.HttpClient(address); 3 4 //set which headers do you want 5 client.DefaultRequestHeaders.Accept 6 .Add(new MediaTypeWithQualityHeaderValue("text/json")); 7 8 var personQuery = client.CreateQuery<Person>(); 9 var results = personQuery.ExecuteAsync().ContinueWith(p => 10 { 11 foreach (var result in p.Result) 12 { 13 Console.WriteLine("Person Id:{0} and Name:{1}", result.Id, 14 result.Name); 15 } 16 17 }); 18

6. Queryability

WCF Web API supports OData style querying capabilities. In order to do this all you have to do is to create a method which return IQueryable<T>. Following shows you how simple it is to write a method and expose the method which supports OData style querying capabilities which includes ordering, filtering etc.

Once this is in place you can place request like http://localhost/Person?$filter=Id%20eq%201.It indicates “find me the person with an ID equal to 1”

7. Fluent Configuration

WCF API makes it easy to configure your application. It provides nice fluent style configuration where you can plug containers, handlers, MediaTypeFormatters and your resource classes.

1 protected void Application_Start(object sender, EventArgs e) 2 { 3 IUnityContainer container = new UnityContainer(); 4 container.RegisterType(typeof(PersonResource)); 5 container.RegisterType(typeof(PeopleResource)); 6 container.RegisterType( 7 typeof(IPersonRepository), typeof(PersonRespository), 8 new ContainerControlledLifetimeManager()); 9 10 var config = HttpHostConfiguration.Create() 11 .AddFormatters(new MediaTypeFormatter[] { new JpgFormatter() }) 12 .SetResourceFactory(new UnityFactory(container)) 13 .SetErrorHandler(new CustomErrorHandler()) 14 .AddMessageHandlers(typeof (LoggingChannel)); 15 16 RouteTable.Routes.MapServiceRoute<PersonResource>("person",config); 17 RouteTable.Routes.MapServiceRoute<PeopleResource>("people",config); 18 }

How to get bits.

Either get the bits from the Nuget package or download from http://wcf.codeplex.com.At the time of writing this article (May 2011), WCF Web API is in Preview 4. Although in a CTP stage, Microsoft has made a great start with WCF Web API. It not only allows us to create powerful applications by leverage support of HTTP but also made life of developers very easy by providing minimal effort to achieve this.

References

Glenn Block’s Blog

http://codebetter.com/glennblock/2011/05/15/using-datacontracts-with-wcf-web-api/

Daniel Cazzulino’s Blog

http://blogs.clariusconsulting.net/kzu/