ã¯ããã«
WCFã§SOAPã¡ãã»ãŒãžããŒã¹ã®WebãµãŒãã¹ãäœæããæ¹æ³ã«ã€ããŠã¯ãã§ã«æžããŠã
ãŸã ã ãããŠä»ãWCFã§ã®RESTfulã¡ãã»ãŒãžããŒã¹ã®WebãµãŒãã¹ã®èšèšãšæ§ç¯ã«ã€ããŠã話ããããšæããŸãã ãã®èšäºãçè§£ããã«ã¯ã
RESTã®åºæ¬ç¥èãšãWCFã§RESTful WebãµãŒãã¹ãäœæããæ¹æ³ãå¿
èŠã§ãã RESTful WebãµãŒãã¹ã«æ
£ããã«ã¯ãã
WCF 3.5ã§RESTful WebãµãŒãã¹ãèšèšããã³æ§ç¯ããããã®ã¬ã€ãããã芧ãã ããã
ãã®èšäºã§ã¯ãRESTfulãªèšèšã®åé¡ãçºèŠããŠè§£æ±ºããããšããŸãã 以äžã®RESTful WebãµãŒãã¹ãæ§ç¯ããæ¹æ³ãåŠç¿ããŸãã
- å®å®ãããŠãããŒãµã«ã€ã³ã¿ãŒãã§ã€ã¹ãåããŠããŸãã
- DTOãã¿ãŒã³ã«åŸã£ãŠããŒã¿ãéä¿¡ããŸãã
ãµã³ã¿ã¯ããŒã¹ã®WCF WebãµãŒãã¹ãèšèšããŸãããã ãµã³ã¿ã¯ãRESTã¢ãŒããã¯ãã£ã¹ã¿ã€ã«ã倧奜ãã§ããã
Open Data ProtocolïŒODataïŒãå«ããªã®ã§ã次ã®èŠä»¶ãæç€ºããŸããã
- ãµãŒãã¹ã«ã¯RESTful APIãå¿
èŠã§ã
- ãµãŒãã¹ã«ã¯æ¬¡ã®æ©èœãå¿
èŠã§ãã
- ã®ãããªã¯ãšã¹ããä¿åããŠããŸãã
- ã®ãããªã¯ãšã¹ããæŽæ°ããŸãã
- ã¹ããŒã¿ã¹ããã³åœå¥ã®ã®ãããªã¯ãšã¹ãã®åä¿¡ã
- IDã«ããã®ãããªã¯ãšã¹ããåé€ããŸãã
ã³ã¢ããžãã¹ãªããžã§ã¯ãã®å®çŸ©
ç§ãã¡ã®ç®æšã¯ãRESTfulã¹ã¿ã€ã«ã§WebãµãŒãã¹ãèšèšããããšã§ãããã®ãããããžãã¹ãªããžã§ã¯ããã§ããã ãã·ã³ãã«ã«ä¿ã¡ãŸãããã
ã®ãããªã¯ãšã¹ãã¯ã©ã¹ïŒä»¥éãPresentRequestïŒãæ€èšããŠãã ããã PresentRequestã¯
éçŽã§ãããæ¬²æ±ã«é¢ãããã¹ãŠã®å¿
èŠãªæ
å ±ãå«ãŸããŠããŸãã
PresentRequestpublic 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ã¡ãœããã«ã¯ãé·æãšçæã®äž¡æ¹ããããŸãã
é·æ ïŒ
- ã¡ãœããã¯æœè±¡ãªã®ã§ã
PresentRequest
ãã£ãŒã«ããç°¡åã«è¿œå ã§ããŸãã - èŠæ±ã¯ãURLãã©ã¡ãŒã¿ãŒãšããŠã§ã¯ãªãããªããžã§ã¯ããšããŠéä¿¡ãããŸã
ã»ãšãã©ã®éçºè
ã¯ã
Mythical Man-Monthãšããæ¬ããããœãããŠã§ã¢ã®æåã®ããŒãžã§ã³ã
ç Žæ£ãããããšãç¥ã£ãŠããŸãã åãããšã
ServiceContractã«ãåœãŠã¯ãŸãã®ã§ãå¯èœãªéãæè»ã«ããå¿
èŠããããŸãã
çæ ïŒ
- ç°ãªã
PresentRequest
ãªããžã§ã¯ããšåæ°ã®Save
ã¡ãœãããå¿
èŠã§ãã ãããã OOPã¯ã©ãã§ããïŒ
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
æäœã®çæ ïŒ
- æ¹æ³ãçè§£ããã®ã¯é£ãã
- å¿
èŠãª
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ã€ã®ããŒããããŸãã
- ãªã¯ãšã¹ãã¿ã€ã
- ãªã¯ãšã¹ãããŒã¿ããªããžã§ã¯ããã£ãŒã«ã
次ã®ã·ãªã¢ã«åã¢ã«ãŽãªãºã ã衚瀺ãããŸãã
- èŠæ±ã¿ã€ããå®çŸ©ãã
- JSONã§ãªã¯ãšã¹ããªããžã§ã¯ããã·ãªã¢ã«åãã
- 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ãµãŒãã¹ã§ã¯ããã®ã³ã³ãã©ã¯ãã䜿çšããŸããã
ISoapServiceSeriviceContract
ïŒ
[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ãµãŒãã¹ã®å©ç¹ã