When you
expose WCF services over the Internet using HTTP protocol on a Windows Server 2003 machine,
IIS 6.0 provides a much richer hosting environment than self-hosting. Unlike self-hosting,
no code is required to instantiate ServiceHost
instances,
but you can still use declarative configuration in the application’s web.config file. More importantly, IIS 6.0 provides other
much-needed hosting features for idle-time management, health monitoring, process recycling,
and management tools for configuring application pools.
In the first three chapters of this book, you have created ASP.NET web sites to host WCF services, completed labs that used the various WCF service templates to help get you started, and experienced some of the differences between self-hosting and IIS hosting. In this section, I’ll review how IIS hosting works in general terms, discuss its distinct behaviors and added value, and provide you with an architectural overview of the IIS 6.0 hosting environment. In addition, I’ll discuss ASP.NET compatibility mode, which allows you to host WCF services through the ASP.NET pipeline to assist with migration of ASP.NET web services.
When you host your services with any version of IIS, you must provide at a minimum the following:
As illustrated in labs from earlier chapters, you can use the WCF Service project
template when you create a new web site project using Visual Studio. This template
generates a project with a .svc endpoint, a sample
service implementation, and a web.config with the
appropriate <service>
element to initialize the
ServiceHost
.
The .svc endpoint is the file that clients will
address in their target URI to reach the service. It contains a @ServiceHost
directive that indicates the service type, and if applicable,
the source code in the App_Code directory where the
service code can be found:
<% @ServiceHost Language=C# Debug="true" Service="MyService" CodeBehind="˜/App_Code/ Service.cs" %>
In reality, your services will be compiled into separate assemblies, so your web site
project must reference those assemblies, and the .svc
endpoint will reference the type only through the Service
property of the @ServiceHost
directive. For example, this Service.svc endpoint
references the service type HelloIndigo.HelloIndigoService
:
<% @ServiceHost Service="HelloIndigo.HelloIndigoService" %>
As I explained in earlier chapters, the service type specified in the @ServiceHost
directive also tells the service model which
<service>
section to use from the web.config to initialize the ServiceHost
instance.
One of the distinctions between self-hosting and IIS hosting is that the base address
is provided by the web site or virtual directory for the application. The <service>
section needn’t provide an address for each
endpoint, since the .svc file is the endpoint:
<service name="HelloIndigo.HelloIndigoService" > <endpoint contract="HelloIndigo.IHelloIndigoService" binding="wsHttpBinding"/> </service>
Clients address the service using the .svc endpoint, for example:
<client>
<endpointaddress="http://localhost/IISHost/Service.svc"
binding="wsHttpBinding"
contract="Client.localhost.HelloIndigoContract" />
</client>
In cases where you might want to expose multiple endpoints for the same .svc endpoint, you can use relative addressing. This example
illustrates a case where a single IIS endpoint, Service.svc, can be accessed using BasicHttpBinding
or WSHttpBinding
to
support different web service protocols. The <endpoint>
configuration for the service uses relative addressing,
appending “/Soap11” and “/Soap12” to the endpoint address:
<service name="HelloIndigo.HelloIndigoService" > <endpointaddress="Soap11"
contract="HelloIndigo.IHelloIndigoService" binding="basicHttpBinding"/> <endpointaddress="Soap12"
contract="HelloIndigo.IHelloIndigoService" binding="wsHttpBinding"/> </service>
Tip
The web service lab from Chapter 3 shows you how to implement multiple WCF endpoints for a single IIS service endpoint.
Clients address these service endpoints in this way:
http://localhost/IISHost/Service.svc/Soap11 http://localhost/IISHost/Service.svc/Soap12
For file-based web sites, the ASP.NET Development Web Server is used instead of IIS, which can be useful for testing only. Endpoints function in the same way as with HTTP-based web sites hosted in IIS, with the exception that a dynamically generated port assignment will exist in the endpoint address; for example, http://localhost:1260/FileBasedHost/Service.svc. This port is saved in the project settings so that the same port is used on subsequent tests.
Tip
At times this port assignment can change, and if so will invalidate hardcoded client endpoints. If you see unexpected behavior, don’t forget to check that the client and service are using the same port.
One of the most important features IIS provides your WCF services is
message-based activation, something I touched on in Chapter 1. ServiceHost
instances need
not be open prior to processing requests for a given endpoint. Instead, the
World Wide Web Publishing Service (“WWW Service”) is responsible
for ensuring that a worker process is present to handle requests. Then, when requests for
a particular .svc endpoint are forwarded to the
worker process, the service model initializes the corresponding ServiceHost
(if necessary) before the request is dispatched.
This message-based activation process allows IIS to balance the number of worker processes required to service the request load, releasing unused resources where appropriate. IIS also monitors the health of each worker process and will launch a new, healthy worker process for requests as needed. In addition, IIS can be configured to periodically recycle worker processes to reduce the risk of resource and memory leaks. These features improve the overall reliability and availability of your WCF services.
There are a few key participants in this activation process:
The HTTP Protocol Stack (http.sys) was introduced with IIS 6.0. It is a kernel-mode message processing service that can receive messages even while worker processes are not yet running. All requests arrive and return through this service.
The WWW Service is responsible for launching worker processes on demand to handle requests. It also determines the correct request queue (application pool) for messages to be sent from http.sys for processing. WWW is a Windows service that must be running for http.sys to successfully forward requests.
Requests for .svc endpoints are forwarded to an ASP.NET worker process because IIS is configured to forward requests to .svc extensions to the ASP.NET ISAPI Extension (aspnet_isapi.dll). The ASP.NET processing pipeline then relies on HTTP modules and handlers to process the request. An HTTP module is a component that can interact with any part of the request lifecycle by handling application events that are fired at specific points during request processing. An HTTP handler is the component responible for processing the incoming request stream and writing an appropriate response to the response stream. The service model provides implementations of these components to intercept and handle service requests, ensuring (as appropriate) that they are processed by the service model rather than the traditional ASP.NET pipeline.
Figure 4-15 illustrates how IIS 6.0 processes requests to WCF services. In fact, up to the point that the request reaches the worker process, it behaves much like any other request that is handled by aspnet_isapi.dll. The difference is in what happens after the ASP.NET pipeline gets hold of the request. For all requests, configured HTTP modules have an opportunity to interact with request processing at specific points in the lifecycle of the request by handling specific application events.
At some point during request processing, the pipeline also looks for an HTTP handler type that should be constructed to process the request according to file extension. Ultimately, the HTTP handler is responsible for processing requests, but this usually happens after the pipeline has had a chance to authenticate the call, verify that the request was not previously cached, reestablish the session if applicable, and process any other code injected by HTTP modules early in the request lifecycle. After the handler executes, HTTP modules then have another opportunity to interact with the response as it flows back through IIS to the client. Since service operations should be handled by the WCF processing model, not ASP.NET, the service model alters this pocessing lifecycle.
The service model provides its own HttpModule
and
HttpHandler
types, located in the System.ServiceModel.Activation
namespace. Both of these
components interact with requests directed to a .svc
endpoint using traditional ASP.NET configuration settings. As far as ASP.NET is concerned,
the HttpHandler
type is the target for request
processing, but that would mean the request would pass through the usual ASP.NET pipeline
with all of the configured modules for forms authentication, caching, and session, for
example. By default, the service model bypasses this by pipeline, by hijacking requests
targeting .svc endpoints and forwarding them to the service model for
processing.
The service model HttpModule
gets engaged very
early in the lifecycle, handling the PostAuthenticateRequest
event and, by default, forwarding the request to the
service model. In fact, the service model allocates a thread from the WCF thread pool (to
be discussed in Chapter 5) and releases the ASP.NET
thread so that another incoming request can use it. This behavior ensures that all
requests to WCF services are processed in a consistent manner, regardless of whether they
are self-hosted or hosted in IIS. The service model handler is never invoked unless
ASP.NET compatibility mode is enabled—something I’ll talk about shortly.
Once the ServiceHost
is activated, hosting a WCF service with IIS operates much the
same as self-hosted services. That is, the service model has a consistent way of
processing requests. By default, the ASP.NET processing model is only involved until the
service model’s HTTP module forwards requests to a WCF thread, allowing the initial
ASP.NET thread to be returned to the ASP.NET thread pool.
In fact, the service model has two modes for hosting WCF services:
- Mixed Transports Mode
This is the default hosting mode. In this mode, service requests are not processed by the ASP.NET pipeline. This makes it possible for endpoints to be exposed for multiple transports in a consistent manner.
- ASP.NET Compatibility Mode
This hosting mode restricts an application to exposing only HTTP services. When this is enabled, the features supplied by HTTP modules, such as file and URL authorization, session state, caching, and profile, are available to the service. In addition,
HttpContext.Current
is initialized to provide access to ASP.NET specific context.
In most cases, access to ASP.NET features and the HttpContext
should not be necessary. The service model
provides a much richer set of authentication and authorization features that are more
appropriate for SOA. A global OperationContext
is also
supplied with access to contextual information for the request, including the security
context, the request message and related headers, and information about the hosting
environment.
If you are porting existing ASP.NET web services (ASMX) you may
have existing code that relies on the HttpContext
or
other ASP.NET features. For example, you may want to access the ASP.NET Session
, Cache
, or Profile
objects to provide a consistent runtime experience as
you would have with ASMX. In this case, ASP.NET compatibility mode may be appropriate. You
can enable ASP.NET compatibility by setting the aspNetCompatibilityEnabled
property to true
in the <serviceHostingEnvironment>
section:
<system.serviceModel> <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/> <system.serviceModel>
This
makes it possible for your service code, or its downstream objects, to interact with these
aforementioned ASP.NET features. For example, you can write code that relies on the
ASP.NET Profile
. Consider this <profile>
section in the web.config:
<profile enabled="true" automaticSaveEnabled="true"> <properties> <add name="Culture" allowAnonymous="true" defaultValue=""/> </properties> </profile>
This
profile creates a single property, Culture
. Your
service code could access the profile for the current user as follows, using the current
HttpContext
:
// set the culture preference string culture = HttpContext.Current.Profile["Culture"]; // get the culture preference HttpContext.Current.Profile["Culture"] = culture;
If
a service plans to use these features, because it was migrated from an ASMX web service
perhaps, it should require that they are deployed to a hosting environment that supports
ASP.NET compatibility by applying the AspNetCompatibilityRequirementsAttribute
from the System.ServiceModel.Activation
namespace, as follows:
[AspNetCompatibilityRequirements( RequirementsMode=AspNetCompatibilityRequirementsMode.Required)] public class ProfileService : IProfileService
Other than porting ASMX services, you will most likely avoid using this feature in order to provide a consistent hosting model for your services over multiple protocols.
Tip
The following code sample illustrates ASP.NET compatibility: <YourLearningWCFPath>\Samples\Hosting\ASPNETCompatibility.
Get Learning WCF now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.