Optional parameters

625 views
Skip to first unread message

mtrax

unread,
Sep 13, 2011, 11:27:46 AM9/13/11
to ServiceStack .NET Open Source REST Web Services Framework, t...@immobilien.net, r...@immobilien.net
Hi!

I'm at the moment comparing "WCF rest service" with ServiceStack.NET.
What is the best way to handle this problem?

Based on this request url: http://our.service.com/{id}

Our pseudo code:

---
Routes.Add<RequestDto>("/GetArticles/{id}");
---

---
[DataContract]
public class Request
{
[DataMember]
public int Id { get; set; }

}
---

---
public object Execute(Request request) {
return GetArticle(request.Id);
}
---

^^
This is quite simple. But, what happens when I need more parameters
like http://our.service.com/{id}/{parameter2}/{parameter3} ?
Parameter2 could be optional.

Should I add a new request with additional parameters, like
"Request2"? Or what's the best practise? Escpecially, the Execute
method, should I use nested If-Else statements for each combination of
available parameters to call the right overloaded GetArticle method?
(In my view very complex and not the best way).

Thanks in advance!

Mario

Demis Bellot

unread,
Sep 13, 2011, 11:48:38 AM9/13/11
to servic...@googlegroups.com
Hi Mario,

Note: just in-case you run into problems, the routes definition are case-sensitive so you would need to provide the right case, i.e:

Routes.Add<RequestDto>("/GetArticles/{Id}");

Also I generally prefer not to suffix the DTO's with 'Dto' (i.e. just leave as Request) as IMHO it's the most important model/artefact and ideally you don't what your internal naming bleeding out into your Web Service API.
And unless you don't need to control the XML output you can leave off the [DataContract]/[DataMember] attributes which makes it a little more wrist-friendly :)

If you need more parameters you have a couple of options, i.e. you could do:

Routes
  .Add<RequestDto>("/GetArticles/{Id}")
  .Add<RequestDto>("/GetArticles/{Id}/{Param1}")
  .Add<RequestDto>("/
GetArticles/{Id}/{Param1}/{Param2}");

Which works well if you have a couple of variations though after a while it can become cumbersome and you will end up with more code than alternatively using a wildcard and splitting the params yourself, e.g:

Routes.Add<RequestDto>("/GetArticles/{Params*}");

....

Run(Request request)
{
    string[] params = request.Params.split('/');

I don't think adding a separate web service (w/ extra DTOs) is the right approach, if it conceptually belong to the same web service externally you should leave it with one web service.
Generally there will be more elegant solutions than nested if statements, i.e. if you put the params in an array like the above you can loop over all the params and build your query generically.

Hope this helps.

Cheers,

mtrax

unread,
Sep 14, 2011, 3:44:22 AM9/14/11
to ServiceStack .NET Open Source REST Web Services Framework
Hi Demis!

Thanks for the fast response!

And thank you again for the hint with the case-sensitive routing.

The "Dto" was a copy/paste mistake, never mind :-)

I had the same idea with the params, but the problem is, that I have
to iterate over every parameter and have to cast the value. Compared
to the WCF Rest Service you are following a different approach. It
would be nice if servicestack can also deal with more than one
parameter(object) and not expecting only one, for example:

public class ServiceStackService : IService<T1, T2, T3, ...> {

public object Execute(string request){
...
}

public object Execute(string request, int id){
...
}

}

Greets

Mario

Demis Bellot

unread,
Sep 14, 2011, 9:07:38 AM9/14/11
to servic...@googlegroups.com
On the grand scheme of things a cast in C# is a very in-expensive operation, it definitely isn't something I'd considering changing/complicating the ServiceStack API with.  In essence a ServiceStack web service accepts a single Request DTO (which the entire request maps to) and returns a Response DTO.  If WCF doesn't make you do it, it means it's still being done - it's just being done under the covers.

At the same time there's nothing stopping you from implementing the desired behaviour yourself, here's a method you can stick in your base class that should do what you want:

public class ExampleWebService {
public object MapRequestToExec(params string[] args) 
{
var mi = GetType().GetMethods()
.First(x => x.Name == "Exec" 
&& x.GetParameters().Length == args.Length);
if (mi == null)
throw new Exception("Method not found matching params: " + args);
var miParams = mi.GetParameters();
var castedParams = new List<object>();
for (var i=0; i<miParams.Length; i++) 
{
var param = miParams[i];
var arg = args[i];
var val = TypeSerializer.DeserializeFromString(arg, param.ParameterType);
castedParams.Add(val);
}
return mi.Invoke(this, castedParams.ToArray());
}
public object Exec(string path)
{
return string.Format("Exec(string {0})", path);
}
public object Exec(string path, int id)
{
return string.Format("Exec(string {0}, int {1})", path, id);
}
public object Exec(string path, int id, double rate)
{
return string.Format("Exec(string {0}, int {1}, double {2})", path, id, rate);
}
}

class Program 
{
public static void Main (string[] args) 
{
var service = new ExampleWebService();
Console.WriteLine(service.MapRequestToExec("testpath"));
Console.WriteLine(service.MapRequestToExec("testpath/10".Split('/')));
Console.WriteLine(service.MapRequestToExec("testpath/10/66.44".Split('/')));
}
}

Reply all
Reply to author
Forward
0 new messages