Reputation: 100766
The scenario:
The service accepts GET requests such as:
GET /endpoint/{resourcename}/?arg1=a&arg2=b&...
The query string arguments can vary, based on resourcename. I do not want to create multiple methods in the interface with specific UriTemplate
settings.
GET /SomeResource/?a=1&a=2&a=3
is a valid request.Example:
[WebGet(UriTemplate="/{resourcename}/???")]
[OperationContract]
Whatever DoTheThing(string resourcename, Dictionary<string, string> queryStringArgs)
Upvotes: 4
Views: 534
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
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