7

I am having problems trying to consume a simple service using WCF. Everything has gone well so far except when coming to implement optional query string parameters. The interface looks a bit like this:

[ServiceContract]
[XmlSerializerFormat]
public interface IApi
{
    [OperationContract]
    [WebGet(UriTemplate = "/url/{param}?top={top}&first={first}")]
    object GetStuff(string param, int top, DateTime first);
}

Then this is consumed by creating a class that inherits ClientBase<IApi>. I tried a few ways to make the parameters optional:

1) Make the parameters nullable

This did not work. I get a message from the QueryStringConverter like another question has asked: Can a WCF service contract have a nullable input parameter?

2) One Parameter at the end of the URL

So, I thought about changing the UriTemplate to be more generic, building the query string and passing it through as a parameter. This didn't seem to work either, as the value passed in gets encoded such that it is not recognised as a querystring by the server.

Example:

[WebGet(UriTemplate = "/url/{query}")]

3) Hackish Solution

The only way I found so far of getting this to work is to change all the parameters to strings, and NULL's seem to be allowed here.

Example:

[WebGet(UriTemplate = "/url/{param}?top={top}&first={first}")]
object GetStuff(string param, string top, string first);

The consumption of this interface still accepts the correct variable type, but ToString is used. Thes query string parameters still appear in the actual request.

So, is there a way when consuming a REST service using WCF, to make the query string parameters optional?

UPDATE - How it was fixed

The advice to create a service behaviour was taken. This inherits from WebHttpBehaviour. It looks as follows:

public class Api : ClientBase<IApi>
{
    public Api() : base("Binding")
    {
        Endpoint.Behaviors.Add(new NullableWebHttpBehavior());
    }
}

The NullableWebHttpBehavior can be found at the following Stackoverflow question: Can a WCF service contract have a nullable input parameter?. The only problem was, ConvertValueToString wasn't overloaded, so I whipped a quick one up:

    public override string ConvertValueToString(object parameter, Type parameterType)
    {
        var underlyingType = Nullable.GetUnderlyingType(parameterType);

        // Handle nullable types
        if (underlyingType != null)
        {
            var asString = parameter.ToString();

            if (string.IsNullOrEmpty(asString))
            {
                return null;
            }

            return base.ConvertValueToString(parameter, underlyingType);
        }

        return base.ConvertValueToString(parameter, parameterType);
    }

This may not be perfect, but it seems to work as intended.

Community
  • 1
  • 1
Hux
  • 3,102
  • 1
  • 26
  • 33
  • 1
    this goes to show how much wcf sucks. it's unbelievable how many hoops you have to jump through to work out such a simple thing – jere Aug 10 '12 at 14:58
  • @jere agreed, it's disappointing. – T. Webster Sep 17 '12 at 19:32
  • Agreed that wcf is overtly complex for creating rest-style applications. The problem wcf tries to solve is making services exposable on different protocols (tcp, msmq and http) and rest is by nature tied to http. So you could argue that wcf is the wrong choice for implementing rest services. – faester Aug 16 '14 at 11:06

2 Answers2

1

Your option 1) can work for a WCF client because the WebHttpBehavior can be applied to a ClientBase (or ChannelFactory) derived class as shown in this SO question & answer. Just combine the code you reference in 1) with the configuration shown in the capturing 500 responses question and it show work.

Community
  • 1
  • 1
Sixto Saez
  • 12,610
  • 5
  • 43
  • 51
0

Have you tried using nullable types?

object GetStuff(string param, int? top, DateTime? first);
wonea
  • 4,783
  • 17
  • 86
  • 139
Oscar
  • 13,594
  • 8
  • 47
  • 75