Implementing a Rest service or End point without automatic deserialization

203 views
Skip to first unread message

Nicolás Granelli

unread,
Jul 26, 2011, 10:02:09 PM7/26/11
to ServiceStack .NET Open Source REST Web Services Framework
What do I have to do to have a rest service without aumatic json
deserialization? For example, I would like to have a url like
http://domain.com/dosomething that accept only POST, and in the code
get the original data passed by the client, to be manipulated in
differents ways (for example, depending in a value inside that post
data, deserialize it with one DTO or another).

I guess I have to implement or inherit something different from
RestServiceBase<T>, but I'm a little bit lost in the source code.

I'm sorry if this is a newbie question, I just need that someone give
me a little of direction :)

Demis Bellot

unread,
Jul 26, 2011, 11:01:02 PM7/26/11
to servic...@googlegroups.com
Hi Nicolas,

Your description is a bit vague so I'm not entirely sure what you're trying to do exactly.

ServiceStack only deserializes JSON if you're sending a JSON request, i.e. your request content-type is 'application/json'
Each web service also accepts a standard HTML FORM POST (i.e. x-www-form-urlencoded) where no JSON deserialization is done and your Request DTO is simply populated with the HTML Forms key-value pairs.
For more details into the different endpoints and formats ServiceStack accepts see: http://www.servicestack.net/ServiceStack.Hello/

From the description of your requirements it sounds like you want to send an arbitrary DTO inside another:

//Request DTO
public class DoSomething
{
    public string Type { get; set; }
    public string Body { get; set; }
}

public class AddSomething
{
    public int Arg1 { get; set; }
    public int Arg2 { get; set; }
}

//Response DTO
public class DoSomethingResponse
{
    public int Result { get; set; }
}

//Rest Service
public DoSomethingService : RestServiceBase<DoSomething>
{
    public override object OnPost(DoSomething request) 
    {
        if (request.Type == "AddSomething")
        {
            var innerDto = request.Body.FromJson<AddSomething>(); //uses Extension method
            return new DoSomethingResponse { Result = innerDto.Arg1 + innerDto.Arg2 };
        }
        throw new ArgumentException("Unknown Request Type: " + request.Type);
    }
}

//AppHost.Configure():

//Configure /dosomething route to above service
Routes.Add<DoSomething>("/dosomething");

If you had inherited from ServiceBase then all HTTP Verbs (e.g. GET, POST, etc) on all formats/endpoints would invoke the web service (as seen in the HelloWorld example).
However since we have only implemented OnPost() above you will need to POST to /dosomething in order to call the service. 

POST /dosomething
...
Content-Type: application/json
 
{"Type":"AddSomething","Body":"{\"Arg1\":10,\"Arg2\":20}"}

Otherwise should you prefer you can call the service with a standard HTML Form Post like:

POST /dosomething
...
Content-Type: application/x-www-form-urlencoded
 
Type=AddSomething&Body={"Arg1":10,"Arg2":20}

If for whatever reason you want to disable say JSON and SOAP endpoints/formats completely you just need to add this in your AppHost.Configure():

var disableFeatures = Feature.Json | Feature.Soap;
SetConfig(new EndpointHostConfig
{
    EnableFeatures = Feature.All.Remove(disableFeatures),
});

Although I don't see much reason to do this since it just disables features others might like to use.

Since it's potentially related to what you want to do, you can actually delegate to different web services using the base.ResolveService<TService>() method

var ordersService = base.ResolveService<OrdersService>();
var ordersResponse = (OrdersResponse)ordersService.Get(new Orders { CustomerId = customer.Id });

Hopefully this helps in what you're trying to do.

Cheers,


2011/7/27 Nicolás Granelli <nicolas...@sitio54.com.ar>

Nicolás Granelli

unread,
Jul 26, 2011, 11:36:40 PM7/26/11
to ServiceStack .NET Open Source REST Web Services Framework
Thanks for the very complete response.

What I'm trying to do is a post like this (the same that you used in
your mail)

POST /dosomething
> ...
> Content-Type: application/json

{"Type":"AddSomething","Body":"{\"Arg1\":10,\"Arg2\":20}"}

and in the server side, get a variable with that string, for example:

var something = "{"Type":"AddSomething","Body":"{\"Arg1\":10,\"Arg2\":
20}"}";

If I get that, I could use XPath or Json.NET to check the content and
do whatever I need to do.


I will try to explain it in another way. If I use the DoSomething DTO,
I will have Type and Body properties, but I wont have any other
property that the json from the post could have. Let's say that I
change the client side so I always get a Type and a Body, so I could
use the Body property to have dynamic schema, but I would have to
create a copy of the DoSomething DTO for each resource I have in the
application (because I think I can't have different
Routes.Add<DoSomething>("/dosomething") with different paths but the
same DTO). A lot of DTO classes that all would have the properties
Type and Body
> However since we have only implemented *OnPost()* above you will need to
> POST to */dosomething* in order to call the service.
>
> POST /dosomething
>
> > ...
> > Content-Type: application/json
>
> {"Type":"AddSomething","Body":"{\"Arg1\":10,\"Arg2\":20}"}
>
> Otherwise should you prefer you can call the service with a standard HTML
> Form Post like:
>
> POST /dosomething
>
> > ...
> > Content-Type: application/x-www-form-urlencoded
>
> Type=AddSomething&Body={"Arg1":10,"Arg2":20}
>
> If for whatever reason you want to disable say JSON and SOAP
> endpoints/formats completely you just need to add this in your
> AppHost.Configure():
>
> var disableFeatures = Feature.Json | Feature.Soap;
>
> > SetConfig(new EndpointHostConfig
> > {
> >     EnableFeatures = Feature.All.Remove(disableFeatures),
> > });
>
> Although I don't see much reason to do this since it just disables features
> others might like to use.
>
> Since it's potentially related to what you want to do, you can actually
> delegate to different web services using the *
> base.ResolveService<TService>()* method
> as seen in the CustomerDetailsService.cs in thehttp://www.servicestack.net/ServiceStack.Northwind/example e.g:
>
> var ordersService = base.ResolveService<OrdersService>();
>
> > var ordersResponse = (OrdersResponse)ordersService.Get(new Orders {
> > CustomerId = customer.Id });
>
> Hopefully this helps in what you're trying to do.
>
> Cheers,
>
> 2011/7/27 Nicolás Granelli <nicolasgrane...@sitio54.com.ar>
>
> > What do I have to do to have a rest service without aumatic json
> > deserialization? For example, I would like to have a url like
> >http://domain.com/dosomethingthat accept only POST, and in the code

Nicolás Granelli

unread,
Jul 27, 2011, 12:41:20 AM7/27/11
to ServiceStack .NET Open Source REST Web Services Framework
I think I almost got it. Look for the test CustomRequestDataTests,
which uses CustomFormDataService and CustomFormData (an empty DTO).
I'm using the only test in that fixture (Can_parse_custom_form_data)
as model, but this changes:

Instead of webReq.ContentType = ContentType.FormUrlEncoded; I'm using
webReq.ContentType = ContentType.Json;

and instead of:

using (var sw = new StreamWriter(webReq.GetRequestStream()))
{
sw.Write("&first-name=tom&item-0=blah&item-1-delete=1");
}

I'm using

using (var sw = new StreamWriter(webReq.GetRequestStream()))
{
sw.Write("{\"Name\":\"Nico\"}");
}


In the service class, instead of httpReq.FormData I'm trying to read
httpRequest.InputStream, but I saw it empty.


I think i'm very close... wish me luck

On 27 jul, 00:36, Nicolás Granelli <nicolasgrane...@sitio54.com.ar>
wrote:
> > >http://domain.com/dosomethingthataccept only POST, and in the code

Demis Bellot

unread,
Jul 27, 2011, 4:50:17 AM7/27/11
to servic...@googlegroups.com
Cool, good luck :)

2011/7/27 Nicolás Granelli <nicolas...@sitio54.com.ar>
Reply all
Reply to author
Forward
0 new messages