RSS

Monthly Archives: April 2013

Getting the parameters and values of the SoapMessage

Sometimes it’s a good practice to be able to process a WebService request’s argument. You may want to consider various things: logging the arguments, detecting specific arguments for some business logic implementation or validation of the passed-in values (security).

For the sake of this post, the arguments will be logged using the following code, but then again – you should think “bigger”. Anyway, here’s the logging code:

public static class Logger
{
    public static void LogParameters(NameValueCollection map)
    {
        foreach (string item in map)
        {
            System.Diagnostics.Debug.WriteLine("{0}={1}", item, map[item]);
        }
    }
}

Simple POST and GET
“Regular” POST or GET requests are quite simple to process. All you have to do is place code in the constructor of the WebService, get the collection of arguments and do whatever you want with them:

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class MyService : System.Web.Services.WebService
{
    public MyService()
    {
        var request = this.Context.Request;
        NameValueCollection map = null;
        if (request.HttpMethod == "POST")
        {
            map = request.Form;

        }
        else if (request.HttpMethod == "GET")
        {
            map = request.QueryString;
        }

        if (map != null)
            Logger.LogParameters(map);
    }

    [WebMethod]
    [TraceExtension]
    public string GetGreeting(string name)
    {
        return "Hello " + name;
    }
}

SOAP
However, for SOAP this is a little more difficult. Although you can try to get the request contents and parse the arguments and their values, there’s a much more convenient way for doing so. .NET already parses the Soap message so why not use it? Apparently you can write a class that receives the parsed SoapMessage and take the arguments from there. In order to do so, you have to write two classes: one class will receive the SoapMessage, and the other class is an Attribute that will perform the hook between the called WebMethod and your class.

If you noticed, the GetGreeting WebMethod above has a TraceExtension Attribute (line 24 in the previous code block). Here is the code for it:

[AttributeUsage(AttributeTargets.Method)]
public class TraceExtensionAttribute : SoapExtensionAttribute
{
    public override Type ExtensionType { get { return typeof(TraceExtension); } }
    private int priority;
    public override int Priority
    {
        get { return this.priority; }
        set { this.priority = value; }
    }
}

Note that the Attribute inherits from SoapExtensionAttribute, which is a requirement to perform the hook to your class.
Also note that on line 4 above, the returned Type is that of the actual class that is going to receive the SoapMessage and handle it. See the class below:

public class TraceExtension : SoapExtension
{
    public override void ProcessMessage(SoapMessage message)
    {
        switch (message.Stage)
        {
            case SoapMessageStage.AfterDeserialize:
                var map = new NameValueCollection();
                for (int i = 0; i < message.MethodInfo.InParameters.Length; ++i)
                {
                    var p = message.MethodInfo.InParameters[i];
                    object val = message.GetInParameterValue(i);
                    map.Add(p.Name, val.ToString());
                }

                Logger.LogParameters(map);
                break;

            case SoapMessageStage.AfterSerialize:
                break;

            case SoapMessageStage.BeforeDeserialize:
                break;

            case SoapMessageStage.BeforeSerialize:
                break;
        }
    }

    public override object GetInitializer(Type serviceType)
    {
        return null;
    }

    public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute)
    {
        return null;
    }

    public override void Initialize(object initializer)
    {
    }
}

The important code here is the override method of ProcessMessage. Here the SoapMessage is received in various stages of the deserialization (and serialization) that allow you flexibility should you require it. As you can see, in lines 8-14 there’s an iteration that loops over the input parameters. This way we receive the already parsed parameters which is what we wanted in the first place. Once we have the parameters in a NameValueCollection, we pass it to the handling method for further processing (logging, in this particular example).

Credits:
The code here is based mainly on the SoapExtension class example from MSDN.

If you’re interested to learn more about the life of the SoapMessage or other stuff that you can do with it, this link may interest you.

 
1 Comment

Posted by on 03/04/2013 in Software Development

 

Tags: