Tuesday, August 17, 2010

Silverlight, WCF, IIS, HTTPS, BasicHttpBinding, and UserNamePasswordValidator

Creating a WCF service and Silverlight client that work together seems like a fairly common requirement. You would probably also require username/password and HTTPS. And it would be best if your development bindings were similar to deployment bindings. Here is what I used:
  • Visual Studio 2010, .NET 4.0
  • Windows 7
  • IIS 7
Note that you cannot use the built-in ASP.NET Development Server. It does not support HTTPS. And you cannot use Message credentials without HTTPS. Here is how I did it:
  • Make sure you have IIS and the "IIS Metabase and IIS6 configuration compatibility" installed.
  • Run Visual Studio as an Administrator so that you can access IIS configuration features from within Visual Studio.
  • Create a new WCF Service Application.
    • Change the debug options on the Project Properties, Web tab to Use Local IIS Web Server.
    • Press the Create a Virtual Directory button.
    • Test that your service provides metadata. (http://localhost/WcfService1/Service1.svc?wsdl)
    • Optional: Enable directory browsing on your virtual directory.
  • Setup IIS, IE, and Visual Studio for HTTPS.
    • In Internet Information Server (IIS) Manager, select the local computer and the "Server Certificates."
    • Create a Self-Signed Certificate.
    • On the web site (not the virtual directory), add a binding for HTTPS and test that it works. (https://localhost/WcfService1/Service1.svc)
    • In Internet Explorer, install the self-signed certificate, placing the certificate in the Trusted Root Certification Authorities.
    • Change the WCF project's Project URL to use https and the machine name, instead of http and the localhost. (https://mishael/WcfService1)
    • Test that Visual Studio can launch the web service view in Internet Explorer without a certificate error.
  • Create a new Silverlight Application.
    • Add a Service Reference to the WCF service.
    • Add a button that calls the WCF service.

      int counter = 0;
      private void button1_Click(object sender, RoutedEventArgs e)
      {
          var c = new Service1Client();
          c.GetDataCompleted += c_GetDataCompleted;
          c.GetDataAsync(++counter);
      }
      
      void c_GetDataCompleted(object sender, GetDataCompletedEventArgs e)
      {
          if (e.Error != null)
              textBlock1.Text = e.Error.Message;
          else
              textBlock1.Text = e.Result;
      }
    • Edit the generated ServiceReferences.ClientConfig to use https instead of http.
    • Edit the generated ServiceReferences.ClientConfig to use security mode of Transport.

      security mode="Transport"
  • Using the WCF Service Configuration Tool, update the WCF service as follows:
    • Create a new service, with the endpoint address pointing to the services.
    • Create a New Binding Configuration for basicHttpBinding. On the Security tab, change the mode to Transport.
    • Use the new binding configuration for the service endpoint. It will be something like this:

      
        
          
            
          
        
      
      
        
          
        
      
    • Testing the service endpoint with Internet Explorer (https://mishael/WcfService1/Service1.svc) reveals that there is an issue with multipleSiteBindingsEnabled. Comment out the multipleSiteBindingsEnabled attribute in the web.config.
  • Test that the Silverlight app can successfully communicate with the WCF service using HTTPS, including stopping at breakpoints in both the Silverlight client and WCF service.
  • Enable client username and password authentication as follows:
    • In the Web.config, switch the security from "Transport" to "TransportWithMessageCredential". and a client message credential type of "UserName".


      
         
      
    • In the ServiceReferences.ClientConfig, switch the security from "Transport" to "TransportWithMessageCredential".
    • Define a UserNamePasswordValidator class to do the username and password validation. (The base class UserNamePasswordValidator is in System.IdentifyModel.dll.)

      namespace WcfService1
      {
          public class MyUserNamePasswordValidator: UserNamePasswordValidator
          {
              public override void Validate(string userName, string password)
              {
                  if (userName == "Wally" && password == "password")
                      return;
                  throw new SecurityTokenValidationException("Unrecognized user.");
              }
          }
      }
    • Update the service behavior to use the new UserNamePasswordValidator class.

      
        
          
            
          
        
      
    • Update the Silverlight client code set a username and password.

      var c = new Service1Client();
      c.ClientCredentials.UserName.UserName = "Wally";
      c.ClientCredentials.UserName.Password = "password";
      
    • Using breakpoints, verify that the UserNamePasswordValidator class is being called.
  • In your WCF operation, throw a FaultException. Testing will reveal that on the client, all you get is a CommunicationException. Improve the availability of exception messages sent to the client as follows:
    • Update the Web.config to includeExceptionDetailInFaults.
      serviceDebug includeExceptionDetailInFaults="true"
    • Create a BehaviorExtensionElement class using the code here.
    • Update the Web.config to include this behavior extension.
      
        
          
        
      
      
    • Update the Web.config to add the extension to the endpoint behavior.
      
              
                
              
            
      
    • Update the Web.config to use this behavior for your endpoint.
      behaviorConfiguration="SilverlightFaultBehavior"

    Monday, August 16, 2010

    Setting up HTTPS for local development without installing IIS

    As the subject implies, I wanted to do some web development that used HTTPS. The WebDev.WebServer (aka Cassini) that comes with Visual Studio does not support HTTPS. I did not want to require IIS for myself or my colleagues.

    I'm not entirely sure of all the details, but this seemed to work:

    • Create a test certificate as described here. You must run as an Administrator.

      > makecert -ss My -sr LocalMachine -sky exchange -r -n "CN=Wallace Kelly" -sk WallaceKelly -pe
      
    • Get the new certificate's "thumbnail" as described here. I used the MMC snap in.
    • Configure a port with the SSL certificate, as described here. The special IP address 0.0.0.0 matches any IP address for the local machine. The certhash is the "thumbnail" without the spaces. The appid is a GUID. I used the GUID for the WebServer2, from the AssemblyInfo.cs. I'm not sure if this can be any GUID.

      > netsh http add sslcert ipport=0.0.0.0:8080 certhash=4dbcbe2656f62af17ceba1f760
      ba7d065fd919bf appid={D7A0778F-A61C-463F-8C39-893DF0AA3748}
      
      SSL Certificate successfully added
      
      
      > netsh http show sslcert
      
      SSL Certificate bindings:
      -------------------------
      
          IP:port                 : 0.0.0.0:8080
          Certificate Hash        : 4dbcbe2656f62af17ceba1f760ba7d065fd919bf
          Application ID          : {d7a0778f-a61c-463f-8c39-893df0aa3748}
          Certificate Store Name  : (null)
          Verify Client Certificate Revocation    : Enabled
          Verify Revocation Using Cached Client Certificate Only    : Disabled
          Usage Check    : Enabled
          Revocation Freshness Time : 0
          URL Retrieval Timeout   : 0
          Ctl Identifier          : (null)
          Ctl Store Name          : (null)
          DS Mapper Usage    : Disabled
          Negotiate Client Certificate    : Disabled
      
      
      
      
    • Use WebDev.WebServer2, found here.

      > WebDev.WebServer2.exe /path:"c:\temp" /prefix:"http://localhost" /port:8080
      

    Wednesday, May 19, 2010

    Custom numeric string formatting, including engineering notation

    I needed to display numbers in an engineering application. The customer wanted the numbers formatted nicely, with about four significant digits and engineering notation. .NET has very flexible number formatting. However, it lacks standard formatting into engineering notation.

    The extension point for formatting numeric strings is through the IFormatProvider and ICustomFormatter interfaces. I implemented a format provider for the engineering notation. However, exclusively using engineering notation was awkward in our application. As an alternative to exclusively using engineering notation, I decided on the following format, which mixes four significant digits, engineering notation, and thousand separators:

    3.1416E-05  31.42E-06
    0.00031416  314.2E-06
    0.0031416   0.003142
    0.031416    0.03142
    0.31416     0.3142
    3.1416      3.142
    31.416      31.42
    314.16      314.2
    3141.6      3,142
    31416       31,416
    3.1416E+05  314,159
    3.1416E+06  3.142E+06
    3.1416E+07  31.42E+06
    3.1416E+08  314.2E+06
    3.1416E+09  3.142E+09
    

    In case you want similar formatting, here is the class that implements it:

    public class EngineeringFormatProvider : IFormatProvider, ICustomFormatter
    {
        public object GetFormat(Type formatType)
        {
            return (formatType == typeof(ICustomFormatter)) ? this : null;
        }
    
        public string Format(string format, object arg, IFormatProvider formatProvider)
        {
            // for doubles, store the value of the double
            var val = Double.NaN;
            if (arg is Double)
                val = (double)arg;
    
            // for other types, try to convert to a double
            else
            {
                var typeConverter = TypeDescriptor.GetConverter(arg);
                if (typeConverter.CanConvertTo(typeof(Double)))
                {
                    try
                    {
                        val = (double)typeConverter.ConvertTo(arg, typeof(Double));
                    }
                    catch
                    {
                        // ignore
                    }
                }
    
                // if cannot convert, return a default value
                if(Double.IsNaN(val))
                    return arg == null ? String.Empty : arg.ToString();
            }
                    
            // for special cases, just write out the string
            if (val == 0.0 || Double.IsNaN(val) || Double.IsInfinity(val))
                return val.ToString();
    
            else
            {
                // calculate the exponents, as a power of 3
                var exp = Math.Log10(Math.Abs(val));
                var exp3 = (int)(Math.Floor(exp / 3.0) * 3.0);
    
                // calculate the coefficient
                var coef = val / Math.Pow(10, exp3);
    
                // special case, for example 0.3142
                if(exp3 == -3 && Math.Abs(coef / 1000.0) < 1 && Math.Abs(coef / 1000.0) > 0.1)
                    return String.Format("{0:G4}", val);
    
                // for "small" numbers
                if(exp3 <= -6)
                    return String.Format("{0:G4}E{1}{2:00}", coef, exp3 > 0 ? "+" : "", exp3);
    
                // for "large" numbers
                if(exp >= 6)
                    return String.Format("{0:G4}E{1}{2:00}", coef, exp3 > 0 ? "+" : "", exp3);
    
                // for numbers needing thousand separators
                if (exp >= 3)
                    return String.Format("{0:N0}", val);
    
                // default
                return String.Format("{0:G4}", val);
            }
        }
    }
    

    Here is how to use it:

    var p = new EngineeringFormatProvider();
    var s = String.Format(p, "{0}", number);

    Tuesday, May 18, 2010

    Closing a window or dialog box when an escape key is pressed in WPF

    When you pop up a dialog box, users expect to be able to press the ESC key to close the dialog box. It can be quite annoying otherwise. In WPF, the easiest way to do that is to set the IsCancel property of your close button like this:

    <Button Content="Close" Click="OnClose" IsCancel="True" />
    

    On a recent project, however, I did not have a close button to set IsCancel on. The solution is to use WPF commands and input gestures. Here is an example:

    <Window.CommandBindings>
       <CommandBinding Command="Close" Executed="OnCloseCmdExecuted" />
    </Window.CommandBindings>
    
    <Window.InputBindings>
       <KeyBinding Command="Close" Key="Escape" />
    </Window.InputBindings>
    

    private void OnCloseCmdExecuted(object sender, ExecutedRoutedEventArgs e)
    {
       this.Close();
    }
    

    Thursday, April 22, 2010

    MSBUILD 4.0, XmlPeek, XmlPoke, and MSB4036

    I installed Visual Studio 2010. I was creating a new MSBUILD file and was delighted to find the new XmlPeek and XmlPoke tasks have been added to .NET Framework 4.0. However, when I tried the following:

    <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      <Target Name="XmlPokeTest">
        <XmlPoke etc.../>
      </Target>
    </Project>
    

    ... I get the dreaded task was not found message:

    Microsoft (R) Build Engine Version 4.0.30319.1 Copyright (C) Microsoft Corporation 2007. All rights reserved. error MSB4036: The "XmlPoke" task was not found.

    The error has a standard set of suggested solutions, including this one:

    Check the following:
    3.) The task is correctly declared within the project file,
    or in the *.tasks files located in the
    "C:\Windows\Microsoft.NET\Framework\v2.0.50727" directory.
    

    This works:

    <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      <UsingTask TaskName="Microsoft.Build.Tasks.XmlPoke"
                 AssemblyName="Microsoft.Build.Tasks.v4.0, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
      <Target Name="XmlPokeTest">
        <XmlPoke etc.../>
      </Target>
    </Project>
    

    There is a Microsoft.Commmon.Tasks file in the C:\Windows\Microsoft.NET\Framework\v4.0.30319 folder that lists the new MSBuild 4.0 tasks, like XmlPeek and XmlPoke. But MSBuild 4.0 is apparently still using the Microsoft.Commmon.Tasks file in C:\Windows\Microsoft.NET\Framework\v2.0.50727.

    Using the toolsversion switch also works, presumably causing MSBuild to reference the new 4.0 Microsoft.Common.Tasks file:

    msbuild /toolsversion:4.0 XmlPokeTest.proj

    For future reference, the 4.0 Microsoft.Common.Tasks file includes these entries that are not included in the the 2.0 version:

    AssignProjectConfiguration
    AssignTargetPath
    CreateCSharpManifestResourceName
    CreateVisualBasicManifestResourceName
    FindAppConfigFile
    FindInList
    FormatUrl
    FormatVersion
    GenerateTrustInfo
    GetReferenceAssemblyPaths
    Move
    RequiresFramework35SP1Assembly
    ResolveManifestFiles
    ResolveNonMSBuildProjectOutput
    UpdateManifest
    XmlPeek
    XmlPoke
    XslTransformation
    

    Thursday, April 15, 2010

    TemplateBinding and the Visual Studio 2010 WPF Designer

    I upgraded to Visual Studio 2010. I was looking forward to the improved WPF designer. However, when I converted one of my projects I found that my WPF windows would not display properly. My windows that displayed properly in the VS2008 WPF designer were not displaying in the VS2010 WPF designer. Compiling and running worked fine in both. It was simply the designer complaining.

    In VS2010, the designer complained about the window: "An error occurred while finding the resource dictionary."

    In VS2010, the resource dictionary complained: "The type 'TemplateBinding' was not found. Verify that you are not missing an assembly reference and that all referenced assemblies have been built."

    I found that my old resource dictionary had this default XML namespace for the XAML:
    xmlns="http://schemas.microsoft.com/netfx/2007/xaml/presentation"

    I found that a new resource dictionary created in VS2010 uses this default XML namespace:
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    Replacing the default namespace in my old resource dictionary solved the problem.

    Hope this helps you!

    Wednesday, March 10, 2010

    Registering for Dependency Property Changed Events

    WPF dependency properties have built-in support for change notification. However, it was not until last week that I realized that I had never registered for a change on a dependency property (at least not one that lacks an explicitly-defined event.) I have made use of the feature many times while data binding. But I had never had to register my own handler for a dependency property changed event.

    The registration mechanism is non-intuitive. So I defined the following extension methods:

    public static void AddDependencyPropertyChangedEventHandler(this DependencyObject obj, DependencyProperty dp, EventHandler handler)
    {
        var dpd = DependencyPropertyDescriptor.FromProperty(dp, obj.GetType());
        dpd.AddValueChanged(obj, handler);
    }
     
    public static void RemoveDependencyPropertyChangedEventHandler(this DependencyProperty obj, DependencyProperty dp, EventHandler handler)
    {
        var dpd = DependencyPropertyDescriptor.FromProperty(dp, obj.GetType());
        dpd.RemoveValueChanged(obj, handler);
    }