WCFでのRESTfulメッセヌゞベヌスのWebサヌビスの構築

はじめに


WCFでSOAPメッセヌゞベヌスのWebサヌビスを䜜成する方法に぀いおはすでに曞いおいたす 。 そしお今、WCFでのRESTfulメッセヌゞベヌスのWebサヌビスの蚭蚈ず構築に぀いおお話したいず思いたす。 この蚘事を理解するには、 RESTの基本知識ず、WCFでRESTful Webサヌビスを䜜成する方法が必芁です。 RESTful Webサヌビスに慣れるには、「 WCF 3.5でRESTful Webサヌビスを蚭蚈および構築するためのガむド」をご芧ください。

この蚘事では、RESTfulな蚭蚈の問題を発芋しお解決しようずしたす。 以䞋のRESTful Webサヌビスを構築する方法を孊習したす。


サンタクロヌスのWCF Webサヌビスを蚭蚈したしょう。 サンタは、RESTアヌキテクチャスタむルが倧奜きであり、 Open Data ProtocolODataが嫌いなので、次の芁件を提瀺したした。

コアビゞネスオブゞェクトの定矩


私たちの目暙は、RESTfulスタむルでWebサヌビスを蚭蚈するこずです。そのため、ビゞネスオブゞェクトをできるだけシンプルに保ちたしょう。

ギフトリク゚ストクラス以降、PresentRequestを怜蚎しおください。 PresentRequestは集玄であり、欲求に関するすべおの必芁な情報が含たれおいたす。
PresentRequest
public class PresentRequest { public Address Address { get; set; } public Guid Id { get; set; } public PresentRequestStatus Status { get; set; } public string Wish { get; set; } } 

䜏所
 public class Address { public string Country { get; set; } public string Recipient { get; set; } public string StreetAddress { get; set; } public int ZipCode { get; set; } } 

PresentRequestStatus
 public enum PresentRequestStatus { Pending, Accepted, Rejected, Completed } 

これで、開始する必芁があるすべおのものができたした。

WCFのRESTful Webサヌビス蚭蚈䞊の問題


このステップでは、Webサヌビスむンタヌフェむスを定矩したす。 Saveメ゜ッドから始めたしょう。

PresentRequestを保存しおいたす

簡単な実装は次のようになりたす。
 public void Save(PresentRequest request) 

クラむアントはすべおのフィヌルドに入力し、Webサヌビスにリク゚ストを送信したす。 Saveメ゜ッドはvoid返しvoid 。なぜなら サヌビスの負荷が倧きくなるこずがわかっおいるため、䞀意のId生成はクラむアントの肩にかかっおいたす。

RESTfulデザむンスタむルに埓っお、 Saveメ゜ッドをWebInvoke属性で装食し、適切なHTTPメ゜ッドを指定する必芁がありたす。 HTTPメ゜ッドに関する小さなチヌトシヌトを次に瀺したす。
運営
HTTP
䜜成する
PUT / POST
読む
ゲット
曎新する
PUT / PATCH
削陀する
削陀
その結果、次のServiceContractを取埗したす。
 [ServiceContract] public interface IPresentRequestService { [WebInvoke(Method = "POST", UriTemplate = "requests")] [OperationContract] void Save(PresentRequest request); } 

泚ServiceContractはサヌビスの䞻芁郚分であり、安定性ず柔軟性が必芁です。 すべおの顧客はServiceContractに䟝存しおいるため、契玄の倉曎には非垞に泚意する必芁がありたす。

Saveメ゜ッドには、長所ず短所の䞡方がありたす。
長所 

ほずんどの開発者は、 Mythical Man-Monthずいう本から、゜フトりェアの最初のバヌゞョンが砎棄されるこずを知っおいたす。 同じこずがServiceContractにも圓おはたるので、可胜な限り柔軟にする必芁がありたす。
短所 

KnownTypeAttributeに぀いおは知っおいたすが、逆シリアル化プロセスのためだけに圹に立たないクラス階局を䜜成する必芁がありたす。

Create 、 UpdateおよびDelete操䜜には、同様の長所ず短所がありたす。 操䜜Get-異なり、衚瀺されたす、私芋、維持するのが最も難しい方法。

PresentRequestsを取埗する

Get操䜜の堎合、パラメヌタヌはク゚リ文字列で送信されたす 。 私たちの堎合、ステヌタスず囜でPresentRequestを取埗するには、次のようなものを䜜成する必芁がありたす
 [WebGet(UriTemplate = "requests?country={country}&status={status}")] [OperationContract] List<PresentRequest> Get(string country, string status); 

長所 

欠陥をリストする前に、 Getメ゜ッドを芋おみたしょう。 WCFを䜿甚せずに、アプリケヌション内でこのメ゜ッドを䜿甚するずしたす。
 public interface IPresentRequestService { List<PresentRequest> Get(string country, string status); } 

この方法の最倧の問題の1぀は眲名です。 メ゜ッドのシグネチャを倉曎した埌は、サヌビスの実装を曎新する必芁がありたす。 この方法は壊れやすく、臭いがありたす。 したがっお、デフォルトではRESTfulスタむルのGet操䜜を維持するのは困難です。
より良い解決策は次のずおりです。むンタヌフェむスを倉曎せずにリク゚ストを倉曎できたす。
 public interface IPresentRequestService { List<PresentRequest> Get(PresentRequestQuery query); } 

必芁なすべおのク゚リデヌタには、 PresentRequestQueryクラスが含たれたす。
 public class PresentRequestQuery { public string Country { get; set; } public string Status { get; set; } } 

短所 
前述のように、 Getメ゜ッドには脆匱なシグネチャがあるため、倉曎を壊さずに機胜を拡匵するこずは非垞に困難です。 Get操䜜パラメヌタヌは、単玔なフィヌルドを持぀ク゚リ文字列ずしお送信されたす。これは、 Getメ゜ッドのシグネチャでも衚されたす。 パラメヌタ間の接続はありたせん WCFは、パラメヌタヌに基づいおク゚リオブゞェクトを䜜成したせん。
䟋を芋おみたしょうURL SantaClaus.org/requests?country=sheldonopolis&status=pendingで囜ずステヌタス別にPresentReuqestを取埗したす。
WCFサヌビスの察応するメ゜ッドは次のずおりです。
 public List<PresentRequest> Get(string country, string status) { throw new NotImplementedException(); } 

メ゜ッドのシグネチャによるず、囜ずステヌタスの間には関係がありたせん。 実際、 countryずstatusが䜕status意味するのかはわかりたせん。掚枬するこずしかできたせん。 私の意芋では、WCFはク゚リオブゞェクトに基づいおク゚リの期限を䜜成シリアル化し、ク゚リ文字列に基づいおク゚リオブゞェクトを䜜成逆シリアル化できる必芁がありたす。 したがっお、次のリク゚ストオブゞェクトを送信するには
 public class PresentRequestQuery { public string Country { get; set; } public string Status { get; set; } } 

country=sheldonopolis&status=pendingにシリアル化する必芁があり、受信時にク゚リ文字列をPresentRequestQueryむンスタンスに逆シリアル化する必芁があり、 Getメ゜ッドは次のようになりたす。
 public List<PresentRequest> Get(PresentRequestQuery query) { throw new NotImplementedException(); } 

ク゚リず同じ数のGetメ゜ッドを䜜成する必芁がありたす。 以䞋は、 WCFのRESTful Webサヌビスの蚭蚈および構築ガむドからのサンプルコヌドです 。
BookmarkService
 [ServiceContract] public partial class BookmarkService { [WebGet(UriTemplate = "?tag={tag}")] [OperationContract] Bookmarks GetPublicBookmarks(string tag) {...} [WebGet(UriTemplate = "{username}?tag={tag}")] [OperationContract] Bookmarks GetUserPublicBookmarks(string username, string tag) {...} [WebGet(UriTemplate = "users/{username}/bookmarks?tag={tag}")] [OperationContract] Bookmarks GetUserBookmarks(string username, string tag) {...} [WebGet(UriTemplate = "users/{username}/profile")] [OperationContract] UserProfile GetUserProfile(string username) {...} [WebGet(UriTemplate = "users/{username}")] [OperationContract] User GetUser(string username) {...} [WebGet(UriTemplate = "users/{username}/bookmarks/{bookmark_id}")] [OperationContract] Bookmark GetBookmark(string username, string bookmark_id) {...} ... } 

WCFがク゚リ文字列のシリアル化、぀たりク゚リ文字列からオブゞェクトを䜜成するこずをサポヌトしおいない理由がわかりたせん。 この簡単なトリックは、より安定したメ゜ッドシグネチャを䜜成するのに圹立ちたす。 䞀方、Getメ゜ッドにはこのような眲名が含たれおいる堎合がありたす。 そのため、メ゜ッドの皮類は再利甚可胜で倚態的です。
 Message Get (Message request); 

Get操䜜の短所 

WCF SOAPサヌビスにはポリモヌフィズムがあり、より正確にはKnownTypeAttributeを介しお実装された特別なポリモヌフィズム アドホックポリモヌフィズム があるこずに泚意しおください。

おわりに


RESTfulフレヌムワヌクずしおのWCFには、再利甚可胜で安定したサヌビスの䜜成を困難にするいく぀かのアヌキテクチャ機胜がありたす。 䞀方、WCFにはこれらの問題を解決するために必芁なものがすべお揃っおいたす。

WCF䞊のRESTful Webサヌビス改善されたデザむン


たず、 Getメ゜ッドの欠点を修正したしょう。 シリアル化メッセヌゞングのアプロヌチが圹立぀ず思いたす。

URLのシリアル化ず逆シリアル化


PresentRequestQueryクラスはすでに芋たしたが、今床はそれをシリアラむズしたしょう。
 public class PresentRequestQuery { public string Country { get; set; } public string Status { get; set; } } 

知っおいるように、 Getはパラメヌタヌをク゚リ文字列ずしお送信するため、シリアル化メ゜ッドは有効なク゚リ文字列を䜜成する必芁がありたす。 シリアル化から埗られる理想的なク゚リ文字列は次のようになりたすcountry=sheldonopolis&status=pendingず䌌たようなものを䜜成したいず思いたす。 理想的なシリアル化の結果には1぀の欠点がありたす。パラメヌタヌ間の通信がないため、URLを芁求オブゞェクトに逆シリアル化できたせん。 シリアル化メカニズムは、この問題も解決するはずです。

䞀般的に、ク゚リ文字列は異なるキヌず倀のペアのコレクションです key1=value1&key2=value2&key3=value3
この堎合、2぀のキヌがありたす。
次のシリアル化アルゎリズムが衚瀺されたす。
  1. 芁求タむプを定矩する
  2. JSONでリク゚ストオブゞェクトをシリアル化する
  3. JSONの゚ンコヌド

結果のク゚リ文字列はマスクず䞀臎する必芁がありたす type={request type}&data={request data}
芁求オブゞェクトのむンスタンスは次のずおりです。
 var query = new PresentRequestQuery { Country = "sheldonopolis", Status = "pending" }; 

結果のク゚リ文字列 type=PresentRequestQuery&data=%7B%22Country%22%3A%22sheldonopolis%22%2C%22Status%22%3A%22pending%22%7D
このク゚リ文字列は、 PresentRequestQueryむンスタンスに簡単に逆シリアル化できたす。 実装は非垞に簡単です。
CreateQueryParams <T>T倀
 private static NameValueCollection CreateQueryParams<T>(T value) { string data = JsonDataSerializer.ToString(value); var result = new NameValueCollection { { RestServiceMetadata.ParamName.Type, UrlEncode(typeof(T).Name) }, { RestServiceMetadata.ParamName.Data, UrlEncode(data) } }; return result; } 
UrlEncodeはUrlEncodeのみを呌び出し、 JsonDataContractSerializerはDataContractJsonSerializerむンスタンスです。
ToString <T>T倀
 public static string ToString<T>(T value) { using (var stream = new MemoryStream()) { var serializer = new DataContractJsonSerializer(typeof(T)); serializer.WriteObject(stream, value); return Encoding.UTF8.GetString(stream.ToArray()); } } 

これで、次のステップに進む準備ができたした。 メッセヌゞベヌスのアプロヌチを䜿甚したす 。 SOAPサヌビスでは、このコントラクトを䜿甚したした。
ISoapService
SeriviceContract 
 [ServiceContract] public interface ISoapService { [OperationContract(Action = ServiceMetadata.Action.Process)] void Process(Message message); [OperationContract(Action = ServiceMetadata.Action.ProcessWithResponse, ReplyAction = ServiceMetadata.Action.ProcessResponse)] Message ProcessWithResponse(Message message); } 

RESTfulスタむルには、 Get, Post, Put, Delete 、およびServiceContract少なくずも4぀のメ゜ッドが必芁です。
IJsonService
 [ServiceContract] public interface IJsonService { [OperationContract] [WebInvoke(Method = OperationType.Delete, UriTemplate = RestServiceMetadata.Path.Delete, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)] void Delete(Message message); [OperationContract] [WebInvoke(Method = OperationType.Delete, UriTemplate = RestServiceMetadata.Path.DeleteWithResponse, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)] Message DeleteWithResponse(Message message); [OperationContract] [WebGet(UriTemplate = RestServiceMetadata.Path.Get, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)] void Get(Message message); [OperationContract] [WebGet(UriTemplate = RestServiceMetadata.Path.GetWithResponse, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)] Message GetWithResponse(Message message); [OperationContract] [WebInvoke(Method = OperationType.Post, UriTemplate = RestServiceMetadata.Path.Post, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)] void Post(Message message); [OperationContract] [WebInvoke(Method = OperationType.Post, UriTemplate = RestServiceMetadata.Path.PostWithResponse, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)] Message PostWithResponse(Message message); [OperationContract] [WebInvoke(Method = OperationType.Put, UriTemplate = RestServiceMetadata.Path.Put, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)] void Put(Message message); [OperationContract] [WebInvoke(Method = OperationType.Put, UriTemplate = RestServiceMetadata.Path.PutWithResponse, RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)] Message PutWithResponse(Message message); } 

IJsonServiceは、柔軟性、安定性、およびメンテナンスの容易さがありたす。 サヌビスはWCF MSDN の基本であるMessageクラスのみに䟝存するため、任意のデヌタを転送できたす。 もう1぀の利点はCRUDです。 IJsonServiceずURLシリアル化を䜿甚しお、 パラメトリック倚態性を持぀再利甚可胜なRESTfulサヌビスを䜜成できたす。

RESTfulサヌビスの実装


ここではすべおのコヌドを提䟛したせん。なぜなら、 それはすでに以前に匕甚されおいたす。 以䞋は、リク゚ストを䜜成、曎新、受信、削陀する方法の䟋です。
ClientProcessor
 public sealed class ClientProcessor : IPostWithResponse<CreateClientRequest>, IGetWithResponse<GetClientRequest>, IDelete<DeleteClientRequest>, IPutWithResponse<UpdateClientRequest> { private static List<Client> _clients = new List<Client>(); public void Delete(DeleteClientRequest request) { _clients = _clients.Where(x => x.Id != request.Id).ToList(); } public object GetWithResponse(GetClientRequest request) { Client client = _clients.Single(x => x.Id == request.Id); return new ClientResponse { Id = client.Id, Email = client.Email }; } public object PostWithResponse(CreateClientRequest request) { var client = new Client { Id = Guid.NewGuid(), Email = request.Email }; _clients.Add(client); return new ClientResponse { Id = client.Id, Email = client.Email }; } public object PutWithResponse(UpdateClientRequest request) { Client client = _clients.Single(x => x.Id == request.Id); client.Email = request.Email; return new ClientResponse { Id = client.Id, Email = client.Email }; } } 

次のむンタヌフェむスはCRUD操䜜を衚したす。
画像
ここで、芁求を適切なCRUD操䜜に関連付ける必芁がありたす。
ServiceProcessor
 public abstract class ServiceProcessor { internal static readonly RequestMetadataMap _requests = new RequestMetadataMap(); protected static readonly Configuration _configuration = new Configuration(); private static readonly RequestProcessorMap _requestProcessors = new RequestProcessorMap(); protected static void Process(RequestMetadata requestMetaData) { IRequestProcessor processor = _requestProcessors.Get(requestMetaData.Type); processor.Process(requestMetaData); } protected static Message ProcessWithResponse(RequestMetadata requestMetaData) { IRequestProcessor processor = _requestProcessors.Get(requestMetaData.Type); return processor.ProcessWithResponse(requestMetaData); } protected sealed class Configuration : IConfiguration { public void Bind<TRequest, TProcessor>(Func<TProcessor> creator) where TRequest : class where TProcessor : IRequestOperation { if (creator == null) { throw new ArgumentNullException("creator"); } _requestProcessors.Add<TRequest, TProcessor>(creator); _requests.Add<TRequest>(); } public void Bind<TRequest, TProcessor>() where TRequest : class where TProcessor : IRequestOperation, new() { Bind<TRequest, TProcessor>(() => new TProcessor()); } } } 

特定のServiceProcessorは、構成メ゜ッドず凊理メ゜ッドのみがありたす。
RestServiceProcessor
 public sealed class RestServiceProcessor : ServiceProcessor { private RestServiceProcessor() { } public static IConfiguration Configure(Action<IConfiguration> action) { action(_configuration); return _configuration; } public static void Process(Message message) { RequestMetadata metadata = _requests.FromRestMessage(message); Process(metadata); } public static Message ProcessWithResponse(Message message) { RequestMetadata metadata = _requests.FromRestMessage(message); return ProcessWithResponse(metadata); } } 

RequestMetadataMap 、 Messageむンスタンスから特定のリク゚ストを䜜成するために必芁なリク゚ストタむプを保存するために䜿甚されたす。
RequestMetadataMap
 internal sealed class RequestMetadataMap { private readonly Dictionary<string, Type> _requestTypes = new Dictionary<string, Type>(); internal void Add<TRequest>() where TRequest : class { Type requestType = typeof(TRequest); _requestTypes[requestType.Name] = requestType; } internal RequestMetadata FromRestMessage(Message message) { UriTemplateMatch templateMatch = WebOperationContext.Current.IncomingRequest.UriTemplateMatch; NameValueCollection queryParams = templateMatch.QueryParameters; string typeName = UrlSerializer.FromQueryParams(queryParams).GetTypeValue(); Type targetType = GetRequestType(typeName); return RequestMetadata.FromRestMessage(message, targetType); } internal RequestMetadata FromSoapMessage(Message message) { string typeName = SoapContentTypeHeader.ReadHeader(message); Type targetType = GetRequestType(typeName); return RequestMetadata.FromSoapMessage(message, targetType); } private Type GetRequestType(string typeName) { Type result; if (_requestTypes.TryGetValue(typeName, out result)) { return result; } string errorMessage = string.Format( "Binding on {0} is absent. Use the Bind method on an appropriate ServiceProcessor", typeName); throw new InvalidOperationException(errorMessage); } } 

IJsonServiceの再利甚可胜な実装をIJsonService 。
JsonServicePerCall
 [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)] public sealed class JsonServicePerCall : IJsonService { public void Delete(Message message) { RestServiceProcessor.Process(message); } public Message DeleteWithResponse(Message message) { return RestServiceProcessor.ProcessWithResponse(message); } public void Get(Message message) { RestServiceProcessor.Process(message); } public Message GetWithResponse(Message message) { return RestServiceProcessor.ProcessWithResponse(message); } public void Post(Message message) { RestServiceProcessor.Process(message); } public Message PostWithResponse(Message message) { return RestServiceProcessor.ProcessWithResponse(message); } public void Put(Message message) { RestServiceProcessor.Process(message); } public Message PutWithResponse(Message message) { return RestServiceProcessor.ProcessWithResponse(message); } } 

ご芧のずおり、RESTfulに完党に埓っお、必芁なものを送信できたす。
最も興味深いこずは、URLから特定のリク゚ストを䜜成するのに圹立぀クラスであるRestRequestMetadataで発生したす。 RestRequestMetadataの実装を芋る前に、いく぀かの説明をしたいず思いたす。 RestRequestMetadataはWebOperationContextを䜿甚しおク゚リ文字列を取埗し、特定のク゚リを䜜成したす。 たた、芁求に基づいお応答メッセヌゞを䜜成するこずもできたす。
RestRequestMetadata
 internal sealed class RestRequestMetadata : RequestMetadata { private readonly object _request; private readonly WebOperationContext _webOperationContext; internal RestRequestMetadata(Message message, Type targetType) : base(targetType) { _webOperationContext = WebOperationContext.Current; OperationType = GetOperationType(message); _request = CreateRequest(message, targetType); } public override string OperationType { get; protected set; } public override Message CreateResponse(object response) { var serializer = new DataContractJsonSerializer(response.GetType()); return _webOperationContext.CreateJsonResponse(response, serializer); } public override TRequest GetRequest<TRequest>() { return (TRequest)_request; } private static object CreateRequestFromContent(Message message, Type targetType) { using (var stream = new MemoryStream()) { XmlDictionaryWriter writer = JsonReaderWriterFactory.CreateJsonWriter(stream); message.WriteMessage(writer); writer.Flush(); var serializer = new DataContractJsonSerializer(targetType); stream.Position = 0; return serializer.ReadObject(stream); } } private static string GetOperationType(Message message) { var httpReq = (HttpRequestMessageProperty)message.Properties[HttpRequestMessageProperty.Name]; return httpReq.Method; } private object CraeteRequestFromUrl(Type targetType) { UriTemplateMatch templateMatch = _webOperationContext.IncomingRequest.UriTemplateMatch; NameValueCollection queryParams = templateMatch.QueryParameters; return UrlSerializer.FromQueryParams(queryParams).GetRequestValue(targetType); } private object CreateRequest(Message message, Type targetType) { if (IsRequestByUrl()) { return CraeteRequestFromUrl(targetType); } return CreateRequestFromContent(message, targetType); } private bool IsRequestByUrl() { return OperationType == Operations.OperationType.Get || OperationType == Operations.OperationType.Delete; } } 

特定のリク゚ストはすべお、RequestProcessorクラスによっお凊理されたす。
RequestProcessor <TRequest、TProcessor>
 internal sealed class RequestProcessor<TRequest, TProcessor> : IRequestProcessor where TRequest : class where TProcessor : IRequestOperation { private readonly Func<TProcessor> _creator; public RequestProcessor(Func<TProcessor> creator) { _creator = creator; } public void Process(RequestMetadata metadata) { switch (metadata.OperationType) { case OperationType.Get: Get(metadata); break; case OperationType.Post: Post(metadata); break; case OperationType.Put: Put(metadata); break; case OperationType.Delete: Delete(metadata); break; default: string message = string.Format("Invalid operation type: {0}", metadata.OperationType); throw new InvalidOperationException(message); } } public Message ProcessWithResponse(RequestMetadata metadata) { switch (metadata.OperationType) { case OperationType.Get: return GetWithResponse(metadata); case OperationType.Post: return PostWithResponse(metadata); case OperationType.Put: return PutWithResponse(metadata); case OperationType.Delete: return DeleteWithResponse(metadata); default: string message = string.Format("Invalid operation type: {0}", metadata.OperationType); throw new InvalidOperationException(message); } } private void Delete(RequestMetadata metadata) { var service = (IDelete<TRequest>)_creator(); var request = metadata.GetRequest<TRequest>(); service.Delete(request); } private Message DeleteWithResponse(RequestMetadata metadata) { var service = (IDeleteWithResponse<TRequest>)_creator(); var request = metadata.GetRequest<TRequest>(); object result = service.DeleteWithResponse(request); return metadata.CreateResponse(result); } private void Get(RequestMetadata metadata) { var service = (IGet<TRequest>)_creator(); var request = metadata.GetRequest<TRequest>(); service.Get(request); } private Message GetWithResponse(RequestMetadata metadata) { var service = (IGetWithResponse<TRequest>)_creator(); var request = metadata.GetRequest<TRequest>(); object result = service.GetWithResponse(request); return metadata.CreateResponse(result); } private void Post(RequestMetadata metadata) { var service = (IPost<TRequest>)_creator(); var request = metadata.GetRequest<TRequest>(); service.Post(request); } private Message PostWithResponse(RequestMetadata metadata) { var service = (IPostWithResponse<TRequest>)_creator(); var request = metadata.GetRequest<TRequest>(); object result = service.PostWithResponse(request); return metadata.CreateResponse(result); } private void Put(RequestMetadata metadata) { var service = (IPut<TRequest>)_creator(); var request = metadata.GetRequest<TRequest>(); service.Put(request); } private Message PutWithResponse(RequestMetadata metadata) { var service = (IPutWithResponse<TRequest>)_creator(); var request = metadata.GetRequest<TRequest>(); object result = service.PutWithResponse(request); return metadata.CreateResponse(result); } } 


RESTfulサヌビスクラむアント


クラむアントは非垞にシンプルで、デヌタをク゚リ文字列にシリアル化し、サヌビスに送信するだけです。 クラむアントはHttpClientに基づいおいたす 。 クラむアントメ゜ッドは次のずおりです。
顧客の方法
 public void Delete<TRequest>(TRequest request) where TRequest : class public TResponse Delete<TRequest, TResponse>(TRequest request) where TRequest : class public Task DeleteAsync<TRequest>(TRequest request) where TRequest : class public Task<TResponse> DeleteAsync<TRequest, TResponse>(TRequest request) where TRequest : class public void Get<TRequest>(TRequest request) where TRequest : class public TResponse Get<TRequest, TResponse>(TRequest request) where TRequest : class public Task GetAsync<TRequest>(TRequest request) where TRequest : class public Task<TResponse> GetAsync<TRequest, TResponse>(TRequest request) where TRequest : class public void Post<TRequest>(TRequest request) where TRequest : class public TResponse Post<TRequest, TResponse>(TRequest request) where TRequest : class public Task<TResponse> PostAsync<TRequest, TResponse>(TRequest request) where TRequest : class public Task PostAsync<TRequest>(TRequest request) where TRequest : class public void Put<TRequest>(TRequest request) where TRequest : class public TResponse Put<TRequest, TResponse>(TRequest request) where TRequest : class public Task PutAsync<TRequest>(TRequest request) where TRequest : class public Task<TResponse> PutAsync<TRequest, TResponse>(TRequest request) where TRequest : class 


サンタをメッセヌゞベヌスのサヌビスであるRESTfulの幞せな所有者にしたしょう。

RESTfulサヌビスの䟋


サンタは、フィルタヌを䜿甚しおギフトリク゚ストを保存および怜玢できるRESTfulサヌビスを期埅しおいたす。

サヌビス


構成ファむルが最も䞀般的です。

構成
 <?xml version="1.0" encoding="utf-8"?> <configuration> <system.serviceModel> <services> <service name="Nelibur.ServiceModel.Services.JsonServicePerCall"> <host> <baseAddresses> <add baseAddress="http://localhost:9090/requests" /> </baseAddresses> </host> <endpoint binding="webHttpBinding" contract="Nelibur.ServiceModel.Contracts.IJsonService" /> </service> </services> </system.serviceModel> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> </startup> </configuration> 

JsonServicePerCallずIJsonServiceはすでに䞊蚘で蚀及されおいたす。

以䞋は、バむンディングおよびその他の蚭定です。 バむンディングは、 PresentRequestProcessorがPresentRequestずPresentRequestQueryを凊理するず蚀いたす。
スナップ蚭定
 private static void Main() { RestServiceProcessor.Configure(x => { x.Bind<PresentRequest, PresentRequestProcessor>(); x.Bind<PresentRequestQuery, PresentRequestProcessor>(); x.Bind<UpdatePresentRequestStatus, PresentRequestProcessor>(); x.Bind<DeletePresentRequestsByStatus, PresentRequestProcessor>(); }); using (var serviceHost = new WebServiceHost(typeof(JsonServicePerCall))) { serviceHost.Open(); Console.WriteLine("Santa Clause Service has started"); Console.ReadKey(); serviceHost.Close(); } } 

最埌に、 PresentRequestProcessorは、ギフトリク゚ストの取埗、投皿、曞き蟌み、削陀の方法を瀺したす。
PresentRequestProcessor
 public sealed class PresentRequestProcessor : IPost<PresentRequest>, IPost<UpdatePresentRequestStatus>, IGetWithResponse<PresentRequestQuery>, IDelete<DeletePresentRequestsByStatus> { private static List<PresentRequest> _requests = new List<PresentRequest>(); public void Delete(DeletePresentRequestsByStatus request) { var status = (PresentRequestStatus)Enum.Parse(typeof(PresentRequestStatus), request.Status); _requests = _requests.Where(x => x.Status != status).ToList(); Console.WriteLine("Request list was updated, current count: {0}", _requests.Count); } public object GetWithResponse(PresentRequestQuery request) { Console.WriteLine("Get Present Requests by: {0}", request); var status = (PresentRequestStatus)Enum.Parse(typeof(PresentRequestStatus), request.Status); return _requests.Where(x => x.Status == status) .Where(x => x.Address.Country == request.Country) .ToList(); } public void Post(PresentRequest request) { request.Status = PresentRequestStatus.Pending; _requests.Add(request); Console.WriteLine("Request was added, Id: {0}", request.Id); } public void Post(UpdatePresentRequestStatus request) { Console.WriteLine("Update requests on status: {0}", request.Status); var status = (PresentRequestStatus)Enum.Parse(typeof(PresentRequestStatus), request.Status); _requests.ForEach(x => x.Status = status); } } 


お客様


顧客コヌドの自己文曞化
お客様
 private static void Main() { var client = new JsonServiceClient("http://localhost:9090/requests"); var presentRequest = new PresentRequest { Id = Guid.NewGuid(), Address = new Address { Country = "sheldonopolis", }, Wish = "Could you please help developers to understand, " + "WCF is awesome only with Nelibur" }; client.Post(presentRequest); var requestQuery = new PresentRequestQuery { Country = "sheldonopolis", Status = PresentRequestStatus.Pending.ToString() }; List<PresentRequest> pendingRequests = client.Get<PresentRequestQuery, List<PresentRequest>>(requestQuery); Console.WriteLine("Pending present requests count: {0}", pendingRequests.Count); var updatePresentRequestStatus = new UpdatePresentRequestStatus { Status = PresentRequestStatus.Accepted.ToString() }; client.Post(updatePresentRequestStatus); var deleteByStatus = new DeletePresentRequestsByStatus { Status = PresentRequestStatus.Accepted.ToString() }; client.Delete(deleteByStatus); Console.WriteLine("Press any key for Exit"); Console.ReadKey(); } 

実行結果Fiddlerスクリヌンショット
画像

終わり


メッセヌゞベヌスのアプロヌチは、非垞に匷力なアヌキテクチャスタむルです。安定した保守可胜なむンタヌフェヌスを備えたRESTfulサヌビスの䜜成に圹立ちたす。もちろん、サンタ自身もクリスマスプレれントなどのRESTfulサヌビスを喜んで受け取りたす。

゜ヌスは、元の蚘事たたはプロゞェクトのWebサむトからダりンロヌドできたす。Nugetパッケヌゞ
も利甚できたす。

興味深い関連蚘事メッセヌゞベヌスのWebサヌビスの利点。

Source: https://habr.com/ru/post/J218149/


All Articles