codeape
codeape

Reputation: 100766

Using WCF webHttp binding, is it possible to pass arbitrary query string arguments?

The scenario:

Example:

[WebGet(UriTemplate="/{resourcename}/???")]
[OperationContract]
Whatever DoTheThing(string resourcename, Dictionary<string, string> queryStringArgs)

Upvotes: 4

Views: 534

Answers (2)

Ashkan
Ashkan

Reputation: 2392

You can write a custom QueryStringConverter which receives all your query string parameters and can process it however you need to. This is the most flexible way but needs some work.

1. First create your custom QueryStringConverter class as below:

public class CustomQueryStringConverter : System.ServiceModel.Dispatcher.QueryStringConverter
{
    public override bool CanConvert(Type type)
    {
        if (type == typeof(Dictionary<string, string>))
            return true;
        // ELSE:
        return base.CanConvert(type);
    }

    public override object ConvertStringToValue(string parameter, Type parameterType)
    {
        if (parameterType != typeof(Dictionary<string, string>))
            return base.ConvertStringToValue(parameter, parameterType);
        // ELSE (the type is Dictionary<string, string>)
        if (parameter == null)
            return new Dictionary<string, string>();

        return JsonConvert.DeserializeObject<Dictionary<string, string>>(parameter);
    }
}

2. Second in order to use this query string converter in your endpoints you need to create a custom WebHttpBehavior and override GetQueryStringConverter to return your custom query string converter, as below:

public sealed class CustomWebHttpBehavior : WebHttpBehavior
{
    public CustomWebHttpBehavior()
    {
        // you can set default values for these properties here if you need to:
        DefaultOutgoingResponseFormat = WebMessageFormat.Json;
        AutomaticFormatSelectionEnabled = true;
        DefaultBodyStyle = WebMessageBodyStyle.Bare;
        HelpEnabled = true;
    }

    protected override System.ServiceModel.Dispatcher.QueryStringConverter GetQueryStringConverter(OperationDescription operationDescription)
    {
        return new CustomQueryStringConverter();
    }
}

3. Third step is to create BehaviorExtensionElement class to be able to register your custom behavior in wcf configuration file, as below:

public class CustomWebHttpBehaviorElement : BehaviorExtensionElement
{

    public override System.Type BehaviorType
    {
        get { return typeof(CustomWebHttpBehavior); }
    }

    protected override object CreateBehavior()
    {
        return new CustomWebHttpBehavior();
    }
}

4. As the last step you should register your custom behavior in your wcf project's web.config file as below:

<system.serviceModel>
    ...

    <extensions>
        <behaviorExtensions>
            <add name="customWebHttpBehaviorElement" type="Your.Namespace.CustomWebHttpBehaviorElement, Your.Assembly" />
        </behaviorExtensions>
    </extensions>
    ...

    <behaviors>
        <endpointBehaviors>
            <behavior name="RestServiceEndpointBehavior">
                <customWebHttpBehaviorElement />
            </behavior>
        </endpointBehaviors>
    </behaviors>
    ...

</system.serviceModel>

After doing these (not easy) steps, if you use RestServiceEndpointBehavior as behavior configuration for your endpoint, the custom query string converter will work for all requests to that endpoint.

Edit: Something I forgot to mention is that you need to pass your query string as json object like this: /SomeResource/?args={"a":1,"b"=2,"c"=3} which means your query string has only 1 parameter (args) which contains your actual parameters (a, b, c).

Edit 2: Your service method will look like this:

    public string DoTheThing(Dictionary<string, string> args)
    {
        var resourceName = "";
        if(args.ContainsKey("resourceName"))
            resourceName = args["resourceName"];
        return string.Format("You entered: {0}", args);     
    }

And the contract would be like this:

    [WebInvoke(Method = "GET")]
    [OperationContract]
    string DoTheThing(Dictionary<string, string> args);

Note that your parameter name "args" in query string should match the parameter name in your service method, otherwise it won't work.

The benefit of this solution is that you don't need UriTemplate for every service method, the downside is you should convert your query string to be json formatted, and of course you lose the duplicate parameter option too.

Hope this helps.

Upvotes: 1

rene
rene

Reputation: 42414

If you can live with the fact that your queryStringArgs is not populated when your method is invoked but rather as the first step of your implementation, this will work:

public string GetData(string value)
{
    var utm = WebOperationContext.Current.IncomingRequest.UriTemplateMatch;
    var queryStringArgs = new Dictionary<string, string>();
    foreach(var query in utm.QueryParameters.AllKeys)
    {
        queryStringArgs.Add(query, utm.QueryParameters[query]);
    }

    return string.Format("You entered: {0} {1}", value, queryStringArgs);
}

This is how the interface is annotated:

[OperationContract]
[WebGet(UriTemplate = "/GetData/{value}")]
string GetData(string value);

When you get hold of the current WebOperationContext you can access the property UrlTemplateMatch that is on the IncomingRequest.

By iterating over the raw content of QueryParameters (a NameValueCollection) you can handle each in a custom manner, one of which could be to add them to a dictionary.

Upvotes: 3

Related Questions