gpt4 book ai didi

c# - ServiceStack RESTful WebService并在消息正文中传递数据

转载 作者:太空狗 更新时间:2023-10-29 22:33:43 27 4
gpt4 key购买 nike

我目前正在评估ServiceStack。我需要创建一堆RESTful Web服务。我正在运行初始代码,对此我感到非常满意。我苦苦挣扎的是如何创建一个可以使用POST(或PUT)HTTP请求的服务,该请求的主体具有数据。

我已经在ServiceStack论坛(http://groups.google.com/group/servicestack/browse_thread/thread/693145f0c3033795)上找到了该线程,并对其进行了指导,我被引导着着眼于SO上的以下线程(Json Format data from console application to service stack),但它并没有真正的帮助-它描述了如何创建请求,而不是如何创建可以使用此类HTTP请求的服务。

当我尝试传递其他数据(在HTTP消息正文中)时,我的servuce返回以下错误(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请求的正确方法,该POST请求的消息正文中包含其他信息。我做错什么了吗?有什么我想念的吗?

在我提供的SO线程链接上也提到了使用DTO是将数据传递到基于ServiceStack的服务的首选方法。假设客户端需要发送大量数据,我们如何实现呢?我不想在URI中将数据作为JSON对象传递。我在这里做任何错误的假设吗?

  • 客户端不会用C#/.Net编写。将会使用完全不同的技术。这就是使用RESTful Web服务的原因之一。
  • 我知道将xml作为字符串返回可能不是最好的主意。目前,这只是一个示例代码。稍后将对此进行更改。
  • 最重要的部分是,为我提供的解决方案是否是创建可使用HTTP请求的Web服务的正确方法,该HTTP请求的主体中附加了xml数据。我与您共享的内容无法100%地确定这是实现我的目标的最佳方法。


  • 于2012年3月8日星期四编辑:

    阅读答案和注释后,我对代码做了一些更改。我非常确定,如果要使用序列化,则必须使用 namespace (将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
    主机:本地主机
    内容类型:application/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

    请求 header :
    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 namespace 感到非常满意-这使得开发服务变得更加容易。

    最佳答案

    是的,不建议返回xml字符串,因为返回的任何字符串都会直接写入响应流,因此该服务仅适用于XML服务,而不适用于所有其他端点。

    ServiceStack方式

    是为了让您在中定义Web服务的DTO保留自己的,它们在很大程度上是无依赖的程序集(我通常只会引用无impl和无dep的ServiceStack.Interfaces.dll)。然后,您可以将这些DTO与ServiceStack的通用服务客户端一起重用,以获取简洁,类型化的端到端API,而无需任何代码源。

    不同的内置服务客户端

    您的C#/。NET客户端只需使用包含在
    ServiceStack.Common NuGet软件包,其中仅包含用于完整.NET和Silverlight 4/5客户端构建的ServiceStack.Text.dll,ServiceStack.Interfaces.dll和ServiceStack.Common.dll。

    ServiceStack.Common包含以下服务客户端:

  • JsonServiceClient -轻量,无处不在,自我描述的弹性格式
  • JsvServiceClient -比JSON更紧凑,是.NET到.NET服务的理想选择
  • XmlServiceClient -适用于喜欢使用XML(比JSON/JSV慢)的人
  • Soap11ServiceClient/Soap12ServiceClient -如果您的公司要求使用SOAP。

  • 如果安装 ProtoBuf Format插件,则还可以选择使用 ProtoBufServiceClient ,这是.NET最快的二进制序列化程序。

    易于交换,易于测试

    C#服务客户端共享相同的 IServiceClientIRestClient接口(interface),因此,如果您想利用高级格式,就可以轻松换出。 Here's an example可以利用 相同的单元测试,也可以将用作JSON,XML,JSV和SOAP集成测试。

    默认情况下,ServiceStack开箱即用,按照以下约定,您可以通过 pre-defined routes来使用所有服务:
    /api/[xml|json|html|jsv|csv]/[syncreply|asynconeway]/[servicename]

    这是服务客户端在使用 Send<TResponse>SendAsync<TResponse> API方法时使用的方法,这些方法使您可以调用Web服务 ,而不必定义任何自定义路由,例如:
    var client = new JsonServiceClient();
    var response = client.Send<TaskResponse>(new Task());

    如果需要,可以使用Get,Post,Put,Delete API方法,这些方法允许您指定url,以便您可以使用自定义路由调用网络服务,例如:

    异步API示例
    FilesResponse response;
    client.GetAsync<FilesResponse>("files/", r => response = r, FailOnAsyncError);

    同步API范例
    var response = client.Get<FilesResponse>("files/README.txt");

    这是 Sync中的一些 AsyncRestFiles example project API示例。

    XML和SOAP问题

    通常,与其他格式相比,XML和SOAP更为严格和脆弱,为了最大程度地减少互操作性问题并减少有效负载膨胀,您应该通过在DTO Assembly.cs文件中添加Assembly属性为所有DTO设置全局XML命名空间,例如:
    [assembly: ContractNamespace("http://schemas.servicestack.net/types", 
    ClrNamespace = "MyServiceModel.DtoTypes")]

    如果要使用与上述不同的ContractNamespace,则如果希望使用SOAP端点,还需要在 EndpointHostConfig.WsdlServiceNamespace中对其进行设置。

    这是开发SOAP/XML Web服务时的更多版本控制技巧:
    https://groups.google.com/d/msg/servicestack/04GQLsQ6YB4/ywonWgD2WeAJ

    SOAP与REST

    由于SOAP通过HTTP POST动词路由所有请求,因此,如果您也希望通过SOAP使每个服务可用,则需要为每个服务创建一个新类,并将到每个服务的自定义REST风格的路由定义为 described here

    由于SOAP/XML的脆弱性,有效载荷大小过大和性能较慢,建议使用JSON,JSV或ProtoBuf格式/端点。

    请求模型绑定(bind)器

    使用 IRequiresRequestStream的另一种替代方法是使用您可以在AppHost中定义的请求模型绑定(bind)程序,例如:
    base.RequestBinders[typeof(Task)] = httpReq => ... requestDto;

    C#客户推荐

    建议将ServiceStack的内置服务客户端用于C#客户端,尽管如果您希望使用自己的HttpClient,那么比仍然使用XmlServiceClient会更方便,因为您可以使用Fiddler来查看ServiceStack期望的确切线格式。

    关于c# - ServiceStack RESTful WebService并在消息正文中传递数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9605167/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com