5

現在、ServiceStackを評価しています。たくさんのRESTfulWebサービスを作成する必要があります。私は最初のコードを実行していますが、非常に満足しています。私が少し苦労していたのは、本文にデータを含む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オブジェクトとしてデータを渡したくありません。私はここで何か間違った仮定をしていますか?


  1. クライアントはC#/.Netで記述されません。まったく異なる技術が使用されます。これが、RESTfulWebサービスの理由の1つでした。
  2. 文字列としてxmlを返すのは最善の方法ではないかもしれないことを私は知っています。現時点では、これは単なるサンプルコードです。これは後で変更されます。
  3. 最も重要な部分は、私に提供されたソリューションが、本体にxmlデータが添付されたHTTPリクエストを使用できるWebサービスを作成する適切な方法であるかどうかです。私があなたと共有したことは、これが私の目標を達成するための最良の方法であると100%確信しているわけではありません。

2012年3月8日木曜日に編集:

回答とコメントを読んだ後、コードを少し変更しました。シリアル化を使用する場合は、名前空間を使用する必要があると確信していました(HTTPメッセージ本文のデータをサービスに渡す場合)。

私は以前http://localhost:53967/api/servicestack.task/xml/metadata?op=Task、自分が作成したサービスに関する詳細情報を入手していました。

RESTパス:

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

HTTP + XML:POST / xml / asynconeway / Task HTTP / 1.1ホスト:localhostコンテンツタイプ: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

リクエストヘッダー:

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名前空間に非常に満足していると思います。これにより、サービスの開発がさらに簡単になります。

4

1 に答える 1

9

いいえ、xml文字列を返すことは、推奨されるアプローチではありません。返された文字列は応答ストリームに直接書き込まれるため、サービスはXMLサービスでのみ機能し、他のすべてのエンドポイントでは機能しません。

ServiceStack Way

Webサービスを定義するDTOを、ほとんど依存関係のない独自のアセンブリに保持することです(通常、参照はimplおよびdep-free ServiceStack.Interfaces.dllのみになります)。次に、これらのDTOをServiceStackの汎用サービスクライアントで再利用して、コード生成なしで簡潔な型指定されたエンドツーエンドのAPIを取得できます。

さまざまな組み込みサービスクライアント

C#/。NETクライアントは、 ServiceStack.Common NuGetパッケージに含まれているサービスクライアントのみを使用する必要があります。 このパッケージには、完全な.NETおよびSilverlight 4 /のServiceStack.Text.dll、ServiceStack.Interfaces.dll、およびServiceStack.Common.dllが含まれています。 5つのクライアントビルド。

ServiceStack.Commonには、次のサービスクライアントが含まれています。

  • JsonServiceClient-軽量でユビキタスな自己記述型の復元力のあるフォーマット
  • JsvServiceClient-.NETから.NETサービスに理想的なJSONよりも高速でコンパクト
  • XmlServiceClient -XMLの使用が好きな人向け(JSON / JSVより遅い)
  • Soap11ServiceClient/Soap12ServiceClient-会社がSOAPの使用を義務付けている場合。

ProtoBuf Formatプラグインをインストールする場合、.NET用の最速のバイナリシリアライザーであるProtoBufServiceClientを使用するオプションもあります。

交換が簡単、テストが簡単

C#サービスクライアントは同じインターフェイスを共有してIServiceClientいるIRestClientため、優れた形式を利用したい場合は簡単に交換できます。 これを利用した例を次に示します。ここでは、同じ単体テストがJSON、XML、JSV、およびSOAP統合テストとしても使用されています。

デフォルトでは、ServiceStackは、次の規則に従って、事前定義されたルートを介してすべてのサービスを利用できるようにします。

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

これは、カスタムルートを定義せずSend<TResponse>にWebサービスを呼び出すことができるAPIメソッドとSendAsync<TResponse>APIメソッドを使用するときにサービスクライアントが使用するものです。例:

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

必要に応じて、URLを指定できるGet、Post、Put、Delete APIメソッドを使用して、カスタムルートを使用してWebサービスを呼び出すことができます。例:

非同期APIの例

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

同期APIの例

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

RestFilesサンプルプロジェクトの同期および非同期APIの例を次に示します。

XMLとSOAPの問題

一般に、XMLとSOAPは他の形式に比べて厳密で脆弱です。相互運用の問題を最小限に抑え、ペイロードの肥大化を減らすには、DTO Assembly.csファイルにAssembly属性を追加して、すべてのDTOにグローバルXML名前空間を設定する必要があります。

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

上記とは異なるContractNamespaceを使用するEndpointHostConfig.WsdlServiceNamespace場合は、SOAPエンドポイントを使用する場合にもそれを設定する必要があります。

SOAP / XML Webサービスを開発する際のバージョン管理のヒントは次のとおりです。https: //groups.google.com/d/msg/servicestack/04GQLsQ6YB4/ywonWgD2WeAJ

SOAPとREST

SOAPはすべてのリクエストをHTTPPOST動詞を介してルーティングするため、各サービスをSOAP経由でも利用できるようにする場合は、サービスごとに新しいクラスを作成し、ここで説明するように各サービスへのカスタムRESTフルルートを定義する必要があります。

脆弱性、肥大化したペイロードサイズ、SOAP / XMLのパフォーマンスの低下により、JSON、JSV、またはProtoBuf形式/エンドポイントのいずれかを使用することをお勧めします。

モデルバインダーをリクエストする

を使用する別の方法IRequiresRequestStreamは、AppHostで定義できるリクエストモデルバインダーを使用することです。例:

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

C#クライアントの推奨事項

C#クライアントにはServiceStackの組み込みサービスクライアントを使用することをお勧めしますが、独自のHttpClientを使用する場合は、Fiddlerを使用してServiceStackが期待する正確なワイヤー形式を確認できるため、XmlServiceClientを使用すると便利です。

于 2012-03-07T19:05:37.667 に答える