ServiceStack RESTful WebService and data transfer in the message body

I am evaluating ServiceStack at the moment. I need to create a bunch of RESTful web services. My initial code works, and I'm quite happy with it. What I'm struggling a bit is creating a service that can consume an HTTP POST (or PUT) request that has data in its body.

I found this thread on the ServiceStack forum ( http://groups.google.com/group/servicestack/browse_thread/thread/693145f0c3033795 ), and after that I was oriented to look at the next thread on SO ( Json Format data from the console application to the service stack ), but it was not very useful - it describes how to create a request, and not how to create a service that can use such an HTTP request.

When I tried to transfer additional data (in the body of the HTTP message), my servuce returned the following error (HTTP 400):

<TaskResponse xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="">
<ResponseStatus>
<ErrorCode>SerializationException</ErrorCode>
<Message>Could not deserialize 'application/xml' request using ServiceStackMVC.Task'
Error: System.Runtime.Serialization.SerializationException: Error in line 1 position 8.Expecting element 'Task' from namespace 'http://schemas.datacontract.org/2004/07/ServiceStackMVC'..    
Encountered 'Element'  with name 'Input', namespace ''. 
at System.Runtime.Serialization.DataContractSerializer.InternalReadObject(XmlReaderDelegator xmlReader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
at System.Runtime.Serialization.XmlObjectSerializer.ReadObjectHandleExceptions XmlReaderDelegator reader, Boolean verifyObjectName, DataContractResolver dataContractResolver)
at System.Runtime.Serialization.XmlObjectSerializer.ReadObject(XmlDictionaryReader reader)
at System.Runtime.Serialization.XmlObjectSerializer.ReadObject(Stream stream)
at ServiceStack.Text.XmlSerializer.DeserializeFromStream(Type type, Stream stream) in  C:\src\ServiceStack.Text\src\ServiceStack.Text\XmlSerializer.cs:line 76
at ServiceStack.WebHost.Endpoints.Support.EndpointHandlerBase.CreateContentTypeRequest(IHttpRequest httpReq, Type requestType, String contentType) in C:\src\ServiceStack\src\ServiceStack\WebHost.Endpoints\Support\EndpointHandlerBase.cs:line 107</Message>
<StackTrace>   at ServiceStack.WebHost.Endpoints.Support.EndpointHandlerBase.CreateContentTypeRequest(IHttpRequest httpReq, Type requestType, String contentType) in C:\src\ServiceStack\src\ServiceStack\WebHost.Endpoints\Support\EndpointHandlerBase.cs:line 115
at ServiceStack.WebHost.Endpoints.RestHandler.GetRequest(IHttpRequest httpReq, IRestPath restPath) in C:\src\ServiceStack\src\ServiceStack\WebHost.Endpoints\RestHandler.cs:line 98
at ServiceStack.WebHost.Endpoints.RestHandler.ProcessRequest(IHttpRequest httpReq, IHttpResponse httpRes, String operationName) in C:\src\ServiceStack\src\ServiceStack\WebHost.Endpoints\RestHandler.cs:line 60</StackTrace>
</ResponseStatus>
</TaskResponse>

This led me to https://github.com/ServiceStack/ServiceStack/wiki/Serialization-deserialization I thought I would give IRequiresRequestStreama go. At the moment, my code is as follows:

public class Task : IRequiresRequestStream
{
    public string TaskName { get; set; }
    public string bodyData { get; set; }

    public override bool Equals(object obj)
    {
        Task task = obj as Task;
        if (task == null)
            return false;
        return TaskName.Equals(task.TaskName);
    }

    public override int GetHashCode()
    {
        return TaskName.GetHashCode();
    }

    public System.IO.Stream RequestStream
    {
        get
        {
            return new MemoryStream(System.Text.Encoding.UTF8.GetBytes(bodyData));
        }
        set
        {
            if (value.Length == 0)
            {
                bodyData = string.Empty;
            }
            else
            {
                byte[] buffer = new byte[value.Length];
                int bytesRead = value.Read(buffer, 0, (int)value.Length);
                bodyData = System.Text.Encoding.UTF8.GetString(buffer);
            }
        }
    }
}

and the service itself:

public class TaskService : RestServiceBase<Task>
{
    public List<Task> tasks { get; set; }

    public override object OnGet(Task request)
    {
        if (string.IsNullOrEmpty(request.TaskName))
        {
            if (tasks == null || tasks.Count == 0)
                return "<tasks/>";
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("<tasks>");
            foreach (Task t in tasks)
            {
                sb.AppendFormat("  <task id={0}><![CDATA[{2}]]><task/>{1}", t.TaskName, System.Environment.NewLine, t.bodyData);
            }
            sb.AppendLine("</tasks>");
            return sb.ToString();                
        }
        else
        {
            if (tasks.Contains(request))
            {
                var task = tasks.Where(t => t.TaskName == request.TaskName).SingleOrDefault();
                return String.Format("<task id={0}><![CDATA[{2}]]><task/>{1}", task.TaskName, System.Environment.NewLine, task.bodyData);
            }
            else
                return "<task/>";
        }
    }

    public override object OnPost(Task request)
    {
        if (tasks.Contains( request ))
        {
            throw new HttpError(System.Net.HttpStatusCode.NotModified, "additional information");
        }

        tasks.Add(new Task() { TaskName = request.TaskName, bodyData = request.bodyData });
        return null;
    }
}

My routes:

Routes.Add<Task>("/tasks/{TaskName}").Add<Task>("/tasks");

It works, but ... since I could not find such an example, I would like to ask if this is the right way to create a service that is capable of handling POST requests that contain additional information contained in their message body. Am I doing something wrong? Is there something I missed?

SO-, , DTO ServiceStack. , , ? JSON URI. - ?


  • #/.Net. . , - RESTful.
  • , xml . . .
  • - , , , - -, HTTP-, XML . , , 100%, .

8 2012 :

, . , , ( HTTP ).

http://localhost:53967/api/servicestack.task/xml/metadata?op=Task .

REST:

All Verbs /tasks/{TaskName}
All Verbs /tasks

HTTP + XML:   POST/xml/asynconeway/ HTTP/1.1   : localhost   Content-Type: application/xml   Content-Length: length

<Task xmlns:i="http://www.w3.org/2001/XMLSchema-instance"   xmlns="http://schemas.datacontract.org/2004/07/ServiceStackMVC">
  <AuxData>String</AuxData>
  <TaskName>String</TaskName>
</Task>

, "" REST URI xml.

Fiddler, POST:

POST http://localhost:53967/api/tasks/22

:

User-Agent: Fiddler
Host: localhost:53967
Content-Type: application/xml
Content-Length: 165

:

<Task xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/ServiceStackMVC">
  <AuxData>something</AuxData>
</Task>

DTO :

public class Task
{
    public string TaskName { get; set; }
    public string AuxData { get; set; }

    public override bool Equals(object obj)
    {
        Task task = obj as Task;
        if (task == null)
            return false;
        return TaskName.Equals(task.TaskName);
    }

    public override int GetHashCode()
    {
        return TaskName.GetHashCode();
    }
}

:

public class TaskService : RestServiceBase<Task>
{
    public List<Task> tasks { get; set; }

    public override object OnGet(Task request)
    {
        return tasks;
    }

    public override object OnPost(Task request)
    {
        if (tasks.Contains( request ))
        {
            throw new HttpError(System.Net.HttpStatusCode.NotModified, "additional information");
        }

        tasks.Add(new Task() { TaskName = request.TaskName });
        return null;
    }
}

, XML ? , xml, .

+5
1

, xml, , , XML, .

ServiceStack

DTO, -, ( impl free-service ServiceStack.Interfaces.dll), DTO ServiceStack, , , API -gen.

#/.NET , ServiceStack.Common NuGet, ServiceStack.Text.dll, ServiceStack.Interfaces.dll ServiceStack.Common.dll .NET Silverlight 4/5.

ServiceStack.Common :

  • JsonServiceClient. , , .
  • JsvServiceClient - , JSON, .NET .NET.
  • XmlServiceClient - , XML (, JSON/JSV)
  • Soap11ServiceClient/Soap12ServiceClient. SOAP.

ProtoBuf Format, ProtoBufServiceClient, serializer .NET.

,

# IServiceClient IRestClient, , . , Unit Test , JSON, XML, JSV SOAP.

- ServiceStack :

/api/[xml|json|html|jsv|csv]/[syncreply|asynconeway]/[servicename]

, API Send<TResponse> SendAsync<TResponse>, - , :

var client = new JsonServiceClient();
var response = client.Send<TaskResponse>(new Task());

, Get, Post, Put, Delete API, URL-, -, , :

API Async

FilesResponse response;
client.GetAsync<FilesResponse>("files/", r => response = r, FailOnAsyncError);

API

var response = client.Get<FilesResponse>("files/README.txt");

Sync Async API RestFiles.

XML SOAP

, XML SOAP , , XML DTO, Assembly DTO Assembly.cs, :

[assembly: ContractNamespace("http://schemas.servicestack.net/types", 
    ClrNamespace = "MyServiceModel.DtoTypes")]

, , EndpointHostConfig.WsdlServiceNamespace, SOAP.

- SOAP/XML: https://groups.google.com/d/msg/servicestack/04GQLsQ6YB4/ywonWgD2WeAJ

SOAP vs REST

SOAP HTTP POST-, , SOAP, REST-ful .

- , SOAP/XML / JSON, JSV ProtoBuf.

IRequiresRequestStream , AppHost, :

base.RequestBinders[typeof(Task)] = httpReq => ... requestDto;

#

, ServiceStack #, , HttpClient, XmlServiceClient, , Fiddler, , ServiceStack ,

+9

All Articles