現在、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オブジェクトとしてデータを渡したくありません。私はここで何か間違った仮定をしていますか?
- クライアントはC#/.Netで記述されません。まったく異なる技術が使用されます。これが、RESTfulWebサービスの理由の1つでした。
- 文字列としてxmlを返すのは最善の方法ではないかもしれないことを私は知っています。現時点では、これは単なるサンプルコードです。これは後で変更されます。
- 最も重要な部分は、私に提供されたソリューションが、本体に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名前空間に非常に満足していると思います。これにより、サービスの開発がさらに簡単になります。