As you might know, I spent quite some time with ASP.NET 2.0 in the last 15 months. Now that the book is finished I investigated a little more in WCF and examined some scenarios I care about. And, of course, looking at WCF from an ASP.NET perspective is one of them.
The way how WCF services are hosted in ASP.NET differs fundamentally to .asmx web services. Here are some observation I made and some tips.
As an .asmx developer you are used to rely on the features of the hosting environment (IIS/ASP.NET) like HTTP authentication, session, cookies or cache support. WCF on the other hand was designed to be host agnostic and such services are provided by its own host called the ServiceHost (this includes e.g. authentication and authorization information).
So the first thing you might notice is, that HttpContext.Current is null by default. Other ASP.NET pipeline services like File and UrlAuthorization, Session, Output Cache or Impersonation are also ignored. This is intentional so that you are not tempted to use any host specific features and to make it easier to move the service to a different hosting environment. How does that work?
By default an HttpModule (System.ServiceModel.Activation.HttpModule) subscribes asynchronously to the PostAuthenticateRequest pipeline event. When a request for a .svc file comes in, the request is dispatched to a WCF controlled thread and the ASP.NET worker thread is put back into the thread pool. This makes WCF independent of ASP.NET threads which can be a bottle-neck in some situations. The HttpContext gets nulled and the service gets invoked. When WCF has finished processing, the response is transmitted back to the client. The whole HTTP pipeline gets short-circuited when WCF takes over control (this also implies that custom modules that subscribe to events later in the pipeline will not get called).
Where have my HttpContext provided Services gone? Some Workarounds.
Well – usually you shouldn’t need those services at all. WCF has an equivalent called the OperationContext and e.g. you get to the identity of your client (the first thing I looked for) from OperationContext.Current.ServiceSecurityContext.PrimaryIdentity.Name.
Another thing you might need is the Cache, e.g. when you want to share cached data between the service and an ASP.NET application in the same AppDomain. It turns out that you don’t have to get the Cache from HttpContext.Cache – you can also use the HttpRuntime class like that:
Cache cache = HttpRuntime.Cache;
The third thing I came across is the mapping of virtual paths to physical one. Since there is no HttpContext by default, you cannot easily call Server.MapPath(). A workaround for that is to use the HostingEnvironment class:
string file = String.Format(“{0}\{1}\{2}”,
HostingEnvironment.ApplicationPhysicalPath, “App_Data”, “foo.txt”);
ASP.NET impersonation is another thing you may rely on. All impersonation is undone when the request enters the WCF ServiceHost. You have to configure impersonation in the WCF service itself – either via a serviceBehavior configuration setting or an OperationBehavior attribute in code.
The equivalent of the ASP.NET <authorization> element is a [PrincipalPermission] attribute on top of an service method or class or a custom ServiceAuthorizationManager derived class.
IIS Authentication Settings
Another thing I noticed is, that WCF requires to enable anonymous authentication for the .svc files in IIS (regardless of WCF authentication settings). If you drop a .svc file into an existing ASP.NET application that uses Windows authentication you will see this error message:
“Security settings for this service require ‘Anonymous’ Authentication but it is not enabled for the IIS application that hosts this service.”
You have to individually enable anonymous access for the .svc files to make it work.
Sharing Users between an Application and a Service
WCF supports the ASP.NET membership feature. If your want to share your user base between an application and a service and you have wrapped your security logic into a membership provider, you are good to go. You can also write a UserNamePasswordValidator derived class which can be plugged into WCF for username/password validation. This is more light-weight than implementing a full featured membership provider.
WCF enforces (transport) security when you use authentication – so prepare to finally get friendly with SSL. Also have a look at the other authentication types like Windows, Certificates and issued tokens (Federation of CardSpace).
Role-based Security
The standard WCF authorization model is based on claims. But you can also get traditional role based security to work. The replacement for Page/Context.User.IsInRole() is now Thread.CurrentPrincipal.IsInRole(). Thread.CurrentPrincipal is not always populated. For Windows authentication you will find the Windows groups as roles there. WCF also support the ASP.NET role manager, so you can plug in a role provider for mapping usernames to roles. For all other cases you can write a class that implements IAuthorizationPolicy to create your custom principal object.
Trust Level
WCF does not support partially trusted callers. That means you are forced to run the ASP.NET application in full trust. If you spent time and effort to make your application or service work in partial trust (which is a good thing), you have to host the WCF service in a different application (=AppDomain). Bad news here.
Which Binding?
The basicHttpBinding is compatible with .asmx – that means transport level security (SSL) and HTTP authentication. If you want to move to message level security, WS-Security and other credential shapes use the wsHttpBinding.
ASP.NET Compatibility Mode
If you absolutely need the HttpContext and pipeline services, e.g. because of migration issues or to share certain services with an ASP.NET application in the same AppDomain, there is also a compatibility mode.
You have to enable the compatibility mode with this configuration setting:
<serviceHostingEnvironment aspNetCompatibilityEnabled=”true” />
Additionally, in the service you have to opt-in using this attribute on the service class:
[AspNetCompatibilityRequirements
(RequirementsMode=AspNetCompatibilityRequirementsMode.Required)]
In this mode, the request will not get intercepted by the HttpModule, but will pass the complete front-part of the HTTP pipeline. The asynchronous handler for .svc files (System.ServiceModel.Activation.HttpHandler) dispatches the request then to the WCF host. The ASP.NET worker thread is freed at this point. After WCF processing is finished, a new worker thread is grabbed from ASP.NET, and the post-request pipeline events fire. In this mode, WCF behaves much like plain .asmx.
You shouldn’t use ASP.NET features like cookies or session state in web services. The standard mode is more efficient and the compatibility mode is really only for migration purposes.
UPDATE: Christan just pointed me to this article which contain extra info.