Showing posts with label WCF. Show all posts
Showing posts with label WCF. Show all posts

Wednesday, February 25, 2009

IIS7 and net.tcp only WAS hosting

I was interested in having net.tcp-only hosting (no HTTP) for a WAS-activated WCF service in IIS7. I found that IIS uses icons that trick you into thinking the site is in an error state when in reality it is not. Here are the steps I took to host my service:


Create the Website

I didn't want to use a virtual directory to host my service, so I created a new web site in IIS Manager (right-click Sites, then Add Web Site). The key here is to select the net.tcp binding type and specify the port in the Binding Information section:



After doing this, IIS7 will show the website in what appears to be an error state (it will have a red 'X' next to the website icon and many of the right-panel Actions menu options (notably the entire Manage Web Site section, containing the restart/start/stop controls and Advanced Settings link) will not appear. THIS SEEMS TO BE A RED HERRING. It will show the red 'X' whenever there are no HTTP bindings for the service. Here is what it looked like on a different site I set up in this manner:



The service will function in spite of the fact that it doesn't show as started. Starting/stopping the service must be done via the controls on the site's application pool (right-clicking it works):




Add Filesystem Components

At a minimum, you need your .svc file, a web.config file, and a bin folder containing the assembly(ies) that implement your service in the website root folder:



The .svc file simply contains the declaration of the class that implements the service:
<%@ServiceHost language="c#" Debug="true" Service="Microsoft.ServiceModel.Samples.CalculatorService" %>
In the web.config, you can omit all references to HTTP bindings, and set up the Metadata Exchange (mex) endpoint to use the net.tcp binding. You can even omit that if you don't need the service to be discoverable, although it helps to retain it:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service
          name="Microsoft.ServiceModel.Samples.CalculatorService"
          behaviorConfiguration="CalculatorServiceBehavior">

        <endpoint    address=""
                    binding="netTcpBinding"
                    contract="Microsoft.ServiceModel.Samples.ICalculator" />
        <endpoint    address="mex"
                    binding="mexTcpBinding"
                    contract="IMetadataExchange" />
      </service>
    </services>

    <!--For debugging purposes set the includeExceptionDetailInFaults attribute to true-->
    <behaviors>
      <serviceBehaviors>
        <behavior name="CalculatorServiceBehavior">
          <serviceMetadata /> <!--You do not need this node if you remove the mex endpoint-->
          <serviceDebug includeExceptionDetailInFaults="False" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>
Note that I did not specify any port information in the address for the endpoint using netTcpBinding as I wanted it to simply use the binding specified for the website (in the case of the website I showed creating up top, TCP over port 11111).


Create a Client

After this, you should be able to point to the service via net.tcp and generate a proxy. In Visual Studio 2008, you can use the Add Service Reference facility in your client project by specifying the following, substituting your service's TCP port for the '11111':
net.tcp://localhost:11111/service.svc/mex





Wednesday, September 03, 2008

WCF web service setup with integrated security

I found setting up a WCF web service to use Windows integrated security to be a somewhat less-than-transparent process, so I thought I'd publish the steps I used to make it work.

Set IIS to allow Negotiate authentication in addition to NTLM

To do this, you need to find the web site identifier. In IIS 6.0, run IIS Manager and choose the Web Sites node and note the identifier of the web site that will host your web service:

Once done, drop to a command prompt and execute the following:
cscript C:\inetpub\AdminScripts adsutil.vbs GET w3svc/<identifier from above>/root/NTAuthenticationProviders
For example:
cscript C:\inetpub\AdminScripts adsutil.vbs GET w3svc/174926873/root/NTAuthenticationProviders

This will report output like the following:
Microsoft (R) Windows Script Host Version 5.6
Copyright (C) Microsoft Corporation 1996-2001. All rights reserved.

NTAuthenticationProviders : (STRING) "Negotiate,NTLM"
If the NTAuthenticationProviders node reads "Negotiate,NTLM" you need make no changes. If it reads simply "NTLM" you must set it as follows:
cscript C:\inetpub\AdminScripts adsutil.vbs SET w3svc/<identifier from above>/root/NTAuthenticationProviders "Negotiate,NTLM"
Use at most one host header

There are workarounds, but out of the box you will get errors if you have more than one host header configured on the IIS web.

Set Windows integrated security, except for the .svc file


In IIS Manager's Directory Security tab for the web site (accessed via right-click | Properties), click Edit in the Authentication and access control section. At the web site level, Enable anonymous access should be unchecked and Integrated Windows authentication should be checked.

Now click on your web site to view its files. You should see the .svc file listed. Right-click this file and go to Properties:
This time, go to the File Security tab and click the Edit button in the Authentication and access control section. This file should have both anonymous access and integrated Windows authentication checked:
Use an integrated security binding

Lastly, you need to use an integrated security web service binding in web.config. Here is an example:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="IntegratedBinding" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647">
<readerQuotas maxArrayLength="2147483647" maxStringContentLength="2147483647" />
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="serviceBehavior">
<serviceMetadata httpGetEnabled="true" httpGetUrl="" />
<serviceDebug includeExceptionDetailInFaults="true" />
<!--<serviceAuthorization impersonateCallerForAllOperations="true" />-->
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="My.Service.OrderService" behaviorConfiguration="serviceBehavior">
<endpoint address="" binding="basicHttpBinding" bindingConfiguration="IntegratedBinding" name="integratedBasicHttpEndpoint" contract="My.Service.IOrderService" />
<endpoint address="mex" binding="mexHttpBinding" name="mexEndpoint" contract="IMetadataExchange" />
</service>
</services>
<serviceHostingEnvironment aspNetCompatibilityEnabled="false" />
</system.serviceModel>

Thursday, July 10, 2008

WCF in .NET 3.5 SP1 and Enterprise Library - TypeInitializationException

I have been fighting a maddening issue. It seems that if you have a WCF service under the .NET 3.5 SP1 beta that is impersonating the caller--either using the ServiceModel pipeline or ASP.NET Compatibility Mode--calls to Enterprise Library assemblies (at least for logging or data access needs) will fail with the following exception:
"The type initializer for 'Microsoft.Practices.EnterpriseLibrary.Logging.LogEntry' threw an exception."
When you dig for the inner exception, you find:
Could not load file or assembly 'System.Management, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. Access is denied.
I've tried numerous things to get around this, such as:
  • Changing the application pool identity to Local System
  • Directly referencing System.Management in the service assembly
  • Adding System.Management (and the Ent Lib assemblies, for that matter) to the <assemblies> node in the web.config
** Update - 11 Jul 2008: Client configuration changes were required to correct the problem. In the client's app.config, configure a behavior for the endpoint to allow impersonation:
<client>
<endpoint address="http://server/WcfService1/Service1.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IService1"
contract="ServiceReference2.IService1" name="BasicHttpBinding_IService1"
behaviorConfiguration="endpointBehavior"/>
</client>
<behaviors>
<endpointBehaviors>
<behavior name="endpointBehavior">
<clientCredentials>
<windows allowedImpersonationLevel="Impersonation"/>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
Then, for some reason, an IIS reset was needed to make the client start working. This article describes the problem in detail.

Here's the thread over on the Enterprise Library forums where I was corresponding about the issue.