我目前正在评估 ServiceStack。我需要创建一堆 RESTful 网络服务。我运行了初始代码,对此我感到非常满意。我有点挣扎的是如何创建一个服务,该服务可以使用在其正文中包含数据的 POST(或 PUT)HTTP 请求。
我在 ServiceStack 论坛 ( http://groups.google.com/group/servicestack/browse_thread/thread/693145f0c3033795 ) 上找到了这个线程,然后我被引导查看关于 SO ( Json ) 的以下线程将数据从控制台应用程序格式化到服务堆栈)但它并没有真正的帮助 - 它描述了如何创建请求,而不是如何创建可以使用此类 HTTP 请求的服务。
当我尝试传递其他数据(在 HTTP 消息正文中)时,我的服务返回以下错误(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>
这导致我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<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;
}
}
我的路线:
Routes.Add<Task>("/tasks/{TaskName}").Add<Task>("/tasks");
它可以工作,但是...因为我找不到任何类似的示例,所以我想问一下,这是否是创建能够处理消息正文中包含附加信息的 POST 请求的服务的正确方法。我做错什么了吗?有什么我错过的吗?
在我提供的 SO 线程链接中也提到,使用 DTO 是将数据传递给基于 ServiceStack 的服务的首选方式。假设客户端需要发送大量数据,我们如何实现呢?我不想在 URI 中将数据作为 JSON 对象传递。我在这里做任何错误的假设吗?
- 客户端不会用 C#/.Net 编写。将使用完全不同的技术。这就是为什么使用 RESTful Web 服务的原因之一。
- 我知道将 xml 作为字符串返回可能不是最好的主意。目前它只是一个示例代码。这将在稍后更改。
- 最重要的部分是,为我提供的解决方案是否是创建 Web 服务的正确方法,该 Web 服务可以使用在其主体中附加了 xml 数据的 HTTP 请求。我与您分享的内容我只是不能 100% 确定这是实现我的目标的最佳方式。
2012 年 3 月 8 日星期四编辑:
阅读答案和评论后,我稍微更改了我的代码。我很确定,如果我想使用序列化,我必须使用名称空间(将 HTTP 消息正文中的数据传递给服务时)。
我过去常常http://localhost:53967/api/servicestack.task/xml/metadata?op=Task
获取有关我创建的服务的更多信息。
休息路径:
All Verbs /tasks/{TaskName}
All Verbs /tasks
HTTP + XML:POST /xml/asynconeway/Task HTTP/1.1 主机:localhost 内容类型:应用程序/xml 内容长度:长度
<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 命名空间非常满意——这使得开发服务变得更加容易。