ServiceStack RESTful WebService и передача данные в теле сообщения

В данный момент я оцениваю ServiceStack. Мне нужно создать кучу веб-сервисов RESTful. У меня есть исходный код, и я вполне доволен этим. Я немного мучился, как создать служба, которая может использовать HTTP-запрос POST (или PUT), в теле которого есть данные.

Я нашел эту ветку на форуме ServiceStack (http://groups.google.com/group/servicestack/browse_thread /поток/693145 f0c3033795) и после него мне посоветовали взглянуть на следующий поток на SO (Json Format data from console application to service stack), но это не очень помогло — в нем описывалось, как создать запрос, а не как создать сервис, который может использовать такой HTTP-запрос.

Когда я попытался передать дополнительные данные (в теле сообщения HTTP), моя служба вернула следующую ошибку (HTTP 400):



SerializationException
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
   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


Это привело меня к https://github.com/ServiceStack/ServiceStack/wiki/Serialization-deserialization Я решил попробовать IRequiresRequestStream. На данный момент мой код выглядит следующим образом:

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);
            }
        }
    }
}

и само обслуживание:

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

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

    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;
    }
}

Мои маршруты:

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

Это работает, но... так как я не смог найти подобного примера, я хотел бы спросить, является ли это правильным способом создания службы, способной обрабатывать запросы POST, в тело сообщения которых включена дополнительная информация. Я делаю что-то не так? Есть ли что-то, что я пропустил?

В ссылке на поток SO, которую я предоставил, также упоминалось, что использование DTO является предпочтительным способом передачи данных в службу на основе ServiceStack. Предполагая, что клиенту нужно отправить много данных, как мы можем этого добиться? Я не хочу передавать данные как объект JSON в URI. Делаю ли я здесь какое-то ложное предположение?


  1. Клиент не будет написан на C# / .Net. Будет использоваться совершенно другая технология. Это было одной из причин, почему веб-сервисы RESTful.
  2. Я знаю, что возврат xml в виде строки может быть не лучшей идеей. На данный момент это просто пример кода. Это будет изменено позже.
  3. Самая важная часть заключается в том, является ли предоставленное мне решение правильным способом создания веб-сервиса, который может использовать 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 Хост: локальный Тип содержимого: приложение/xml Content-Length: length


  String
  String

Я хотел проверить, можно ли «смешивать» 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

Тело запроса:


  something

Сейчас мой 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
{
    public List 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
задан Community 23 May 2017 в 11:43
поделиться