WCF рдкрд░ рдкреНрд░рддрд┐рд╖реНрдард┐рдд рд╕рдВрджреЗрд╢ рдЖрдзрд╛рд░рд┐рдд рд╡реЗрдм рд╕реЗрд╡рд╛рдУрдВ рдХрд╛ рдирд┐рд░реНрдорд╛рдг

рдкрд░рд┐рдЪрдп


рдореИрдВрдиреЗ рдкрд╣рд▓реЗ рд╣реА WCF рдкрд░ SOAP рдореИрд╕реЗрдЬ рдмреЗрд╕реНрдб рд╡реЗрдм рд╕рд░реНрд╡рд┐рд╕ рдмрдирд╛рдиреЗ рдХрд╛ рддрд░реАрдХрд╛ рд▓рд┐рдЦрд╛ рдерд╛ред рдФрд░ рдЕрдм рдореИрдВ WCF рдкрд░ Restful Message Based web services рдХреЗ рдбрд┐рдЬрд╛рдЗрди рдФрд░ рдирд┐рд░реНрдорд╛рдг рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдмрд╛рдд рдХрд░рдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВред рдЗрд╕ рд▓реЗрдЦ рдХреЛ рд╕рдордЭрдиреЗ рдХреЗ рд▓рд┐рдП REST рдХреЗ рдореВрд▓рднреВрдд рдЬреНрдЮрд╛рди рдФрд░ WCF рдкрд░ RESTful рд╡реЗрдм рд╕реЗрд╡рд╛рдУрдВ рдХрд╛ рдирд┐рд░реНрдорд╛рдг рдХрд░рдирд╛ рдЖрд╡рд╢реНрдпрдХ рд╣реИред рд░реЗрд╕реНрдЯрдлреБрд▓ рд╡реЗрдм рд╕реЗрд╡рд╛рдУрдВ рдХреЗ рд╕рд╛рде рдЦреБрдж рдХреЛ рдкрд░рд┐рдЪрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдЖрдк рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВ: рдП рдЧрд╛рдЗрдб рдЯреВ рдбрд┐рдЬрд╛рдЗрдирд┐рдВрдЧ рдПрдВрдб рдмрд┐рд▓реНрдбрд┐рдВрдЧ рд░реЗрд╕реНрдЯрдлреБрд▓ рд╡реЗрдм рд╕рд░реНрд╡рд┐рд╕реЗрдЬ рд╡рд┐рде рдбрдмреНрд▓реНрдпреВрдПрдлрдПрдл 3.5 ред

рдЗрд╕ рд▓реЗрдЦ рдореЗрдВ, рдореИрдВ Restful рдбрд┐рдЬрд╛рдЗрди рд╕рдорд╕реНрдпрд╛рдУрдВ рдХреЛ рдЙрдЬрд╛рдЧрд░ рдХрд░рдиреЗ рдФрд░ рд╣рд▓ рдХрд░рдиреЗ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рдХрд░реВрдВрдЧрд╛ред рдЖрдк рд╕реАрдЦреЗрдВрдЧреЗ рдХрд┐ рдПрдХ RESTful рд╡реЗрдм рд╕реЗрд╡рд╛ рдХреИрд╕реЗ рдмрдирд╛рдИ рдЬрд╛рддреА рд╣реИ:


рд╕рд╛рдВрддрд╛ рдХреНрд▓реЙрдЬ рдХреЗ рд▓рд┐рдП WCF рд╡реЗрдм рд╕реЗрд╡рд╛ рдбрд┐рдЬрд╛рдЗрди рдХрд░рддреЗ рд╣реИрдВред рд╕рд╛рдВрддрд╛ рдЖрд░рдИрдПрд╕ рд╡рд╛рд╕реНрддреБрд╢рд┐рд▓реНрдк рд╢реИрд▓реА рд╕реЗ рдкреНрдпрд╛рд░ рдХрд░рддрд╛ рд╣реИ рдФрд░ рдУрдкрди рдбреЗрдЯрд╛ рдкреНрд░реЛрдЯреЛрдХреЙрд▓ (рдУрдбрд╛рдЯрд╛) рдХреЛ рдмрд┐рд▓реНрдХреБрд▓ рдирд╛рдкрд╕рдВрдж рдХрд░рддрд╛ рд╣реИ , рдЗрд╕рд▓рд┐рдП рдЙрд╕рдиреЗ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдЖрд╡рд╢реНрдпрдХрддрд╛рдУрдВ рдХреЛ рд╕рд╛рдордиреЗ рд░рдЦрд╛:

рдореБрдЦреНрдп рд╡реНрдпрд╛рд╡рд╕рд╛рдпрд┐рдХ рд╡рд╕реНрддреБрдУрдВ рдХреА рдкрд░рд┐рднрд╛рд╖рд╛


рд╣рдорд╛рд░рд╛ рд▓рдХреНрд╖реНрдп рдПрдХ рд╡реЗрдм рд╕реЗрд╡рд╛ рдХреЛ рд░реИрд╕реНрдЯрдлреБрд▓ рд╢реИрд▓реА рдореЗрдВ рдбрд┐рдЬрд╛рдЗрди рдХрд░рдирд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП рд╣рдо рд╡реНрдпрд╡рд╕рд╛рдп рдХреА рд╡рд╕реНрддреБрдУрдВ рдХреЛ рдпрдерд╛рд╕рдВрднрд╡ рд╕рд░рд▓ рд░рдЦреЗрдВред

рдЧрд┐рдлреНрдЯ рд░рд┐рдХреНрд╡реЗрд╕реНрдЯ рдХреНрд▓рд╛рд╕ (рдЗрд╕рдХреЗ рдмрд╛рдж рдкреНрд░реЗрдЬреЗрдВрдЯрдкреНрд░рд┐рд╕реЗрд╕реНрдЯ) рдкрд░ рд╡рд┐рдЪрд╛рд░ рдХрд░реЗрдВред 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 рдкрд░ рдкреНрд░рддрд┐рд╖реНрдард┐рдд рд╡реЗрдм рд╕реЗрд╡рд╛: рдбрд┐рдЬрд╛рдЗрди рдХреА рд╕рдорд╕реНрдпрд╛


рдЗрд╕ рдЪрд░рдг рдореЗрдВ рд╣рдо рд╡реЗрдм рд╕реЗрд╡рд╛ рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдХреЛ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░реЗрдВрдЧреЗред Save рд╡рд┐рдзрд┐ рд╕реЗ рд╢реБрд░реВ рдХрд░рддреЗ рд╣реИрдВред

PresentRequest рдХреЛ рд╕рд╣реЗрдЬрдирд╛

рдПрдХ рд╕рд╛рдзрд╛рд░рдг рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдЗрд╕ рддрд░рд╣ рджрд┐рдЦреЗрдЧрд╛:
 public void Save(PresentRequest request) 

рдЧреНрд░рд╛рд╣рдХ рд╕рднреА рдХреНрд╖реЗрддреНрд░реЛрдВ рдореЗрдВ рднрд░ рдЬрд╛рддрд╛ рд╣реИ рдФрд░ рд╡реЗрдм рд╕реЗрд╡рд╛ рдХреЗ рд▓рд┐рдП рдПрдХ рдЕрдиреБрд░реЛрдз рднреЗрдЬрддрд╛ рд╣реИред Save рд╡рд┐рдзрд┐ void , рдХреНрдпреЛрдВрдХрд┐ рд╣рдо рдЬрд╛рдирддреЗ рд╣реИрдВ рдХрд┐ рд╕реЗрд╡рд╛ рдЕрддреНрдпрдзрд┐рдХ рднрд░реА рд╣реБрдИ рд╣реЛрдЧреА, рдЗрд╕рд▓рд┐рдП рдПрдХ рд╡рд┐рд╢рд┐рд╖реНрдЯ Id рдХреА рдкреАрдврд╝реА рдЧреНрд░рд╛рд╣рдХ рдХреЗ рдХрдВрдзреЛрдВ рдкрд░ рдЖ рдЬрд╛рддреА рд╣реИред

WebInvoke рдбрд┐рдЬрд╝рд╛рдЗрди рд╢реИрд▓реА рдХреЗ рдЕрдиреБрд╕рд╛рд░, рд╣рдореЗрдВ WebInvoke рд╡рд┐рд╢реЗрд╖рддрд╛ рдХреЗ рд╕рд╛рде Save рд╡рд┐рдзрд┐ рдХреЛ рд╕рдЬрд╛рдиреЗ рдФрд░ рдЙрдЪрд┐рдд HTTP рд╡рд┐рдзрд┐ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИред рдпрд╣рд╛рдБ HTTP рддрд░реАрдХреЛрдВ рдкрд░ рдПрдХ рдЫреЛрдЯрд╛ рд╕рд╛ рдзреЛрдЦрд╛ рдкрддреНрд░ рд╣реИ:
рдЖрдкрд░реЗрд╢рди
HTTP
рдмрдирд╛рдПрдВ
PUT / POST
рдкрдврд╝реЗрдВ
рдкреНрд░рд╛рдкреНрдд
рдЕрджреНрдпрддрди
PUT / PATCH
рд╣рдЯрд╛рдПрдВ
рд╣рдЯрд╛рдПрдБ
рдирддреАрдЬрддрди, рд╣рдореЗрдВ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд ServiceContract рдорд┐рд▓рддреЗ рд╣реИрдВ:
 [ServiceContract] public interface IPresentRequestService { [WebInvoke(Method = "POST", UriTemplate = "requests")] [OperationContract] void Save(PresentRequest request); } 

рдиреЛрдЯ: ServiceContract рд╕реЗрд╡рд╛ рдХрд╛ рдореБрдЦреНрдп рднрд╛рдЧ рд╣реИ, рдЬреЛ рд╕реНрдерд┐рд░ рдФрд░ рд▓рдЪреАрд▓рд╛ рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдПред рд╕рднреА рдЧреНрд░рд╛рд╣рдХ ServiceContract рдкрд░ рдирд┐рд░реНрднрд░ рд╣реИрдВ, рдЗрд╕рд▓рд┐рдП рд╣рдореЗрдВ рдЕрдиреБрдмрдВрдз рдореЗрдВ рдХрд┐рд╕реА рднреА рдкрд░рд┐рд╡рд░реНрддрди рдХреЗ рд╕рд╛рде рдмрд╣реБрдд рд╕рд╛рд╡рдзрд╛рди рд░рд╣рдирд╛ рдЪрд╛рд╣рд┐рдПред

рд╕реЗрд╡ рд╡рд┐рдзрд┐ рдореЗрдВ рдкреЗрд╢реЗрд╡рд░реЛрдВ рдФрд░ рд╡рд┐рдкрдХреНрд╖ рджреЛрдиреЛрдВ рд╣реИрдВред
рдкреЗрд╢реЗрд╡рд░реЛрдВ :

рдЬреНрдпрд╛рджрд╛рддрд░ рдбреЗрд╡рд▓рдкрд░реНрд╕ Mythical Man-Month рдХреА рдХрд┐рддрд╛рдм рд╕реЗ рдЬрд╛рдирддреЗ рд╣реИрдВ рдХрд┐ рд╕реЙрдлреНрдЯрд╡реЗрдпрд░ рдХреЗ рдкрд╣рд▓реЗ рд╕рдВрд╕реНрдХрд░рдг рдХреЛ рдлреЗрдВрдХ рджрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛ред рд╡рд╣реА рд╕реЗрд╡рд╛рдХрдВрдЯреНрд░реЗрдХреНрдЯ рдкрд░ рд▓рд╛рдЧреВ рд╣реЛрддрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП рд╣рдореЗрдВ рдЗрд╕реЗ рдпрдерд╛рд╕рдВрднрд╡ рд▓рдЪреАрд▓рд╛ рдмрдирд╛рдиреЗ рдХрд╛ рдкреНрд░рдпрд╛рд╕ рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдПред
рд╡рд┐рдкрдХреНрд╖ :

рдореИрдВ рдЬрд╛рдирддрд╛ рд╣реВрдВ рдХрд┐ KnownTypeAttribute рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ, рд▓реЗрдХрд┐рди рд╣рдореЗрдВ рдмрд╕ рдирд┐рд░рдВрдХреБрд╢рддрд╛ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХреЗ рд▓рд┐рдП рдПрдХ рдмреЗрдХрд╛рд░ рд╡рд░реНрдЧ рдкрджрд╛рдиреБрдХреНрд░рдо рдмрдирд╛рдирд╛ рд╣реЛрдЧрд╛ред

Create , Update рдФрд░ Delete рдСрдкрд░реЗрд╢рдВрд╕ рдореЗрдВ рд╕рдорд╛рди рдирд┐рдпрдо рдФрд░ рд╡рд┐рдкрдХреНрд╖ рд╣реИрдВред рдСрдкрд░реЗрд╢рди рдЧреЗрдЯ - рдЕрд▓рдЧ рд╣реИ рдФрд░ рдкреНрд░рдХрдЯ рд╣реЛрддрд╛ рд╣реИ, IMHO, рдмрдирд╛рдП рд░рдЦрдиреЗ рдХреЗ рд▓рд┐рдП рд╕рдмрд╕реЗ рдХрдард┐рди рддрд░реАрдХрд╛ рд╣реИред

PresentRequests рд╣реЛ рд░рд╣реА рд╣реИ

рдЧреЗрдЯ рдСрдкрд░реЗрд╢рди рдХреЗ рд▓рд┐рдП, рдкреИрд░рд╛рдореАрдЯрд░ рдХреНрд╡реЗрд░реА рд╕реНрдЯреНрд░рд┐рдВрдЧ рдореЗрдВ рднреЗрдЬреЗ рдЬрд╛рддреЗ рд╣реИрдВред рд╣рдорд╛рд░реЗ рдорд╛рдорд▓реЗ рдореЗрдВ, рд╕реНрдерд┐рддрд┐ рдФрд░ рджреЗрд╢ рджреНрд╡рд╛рд░рд╛ 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); } 

рдЗрд╕ рдкрджреНрдзрддрд┐ рдХреЗ рд╕рд╛рде рд╕рдмрд╕реЗ рдмрдбрд╝реА рд╕рдорд╕реНрдпрд╛рдУрдВ рдореЗрдВ рд╕реЗ рдПрдХ рд╣рд╕реНрддрд╛рдХреНрд╖рд░ рд╣реИред рд╣рдореЗрдВ рд╡рд┐рдзрд┐ рд╣рд╕реНрддрд╛рдХреНрд╖рд░ рдореЗрдВ рдХрд┐рд╕реА рднреА рдкрд░рд┐рд╡рд░реНрддрди рдХреЗ рдмрд╛рдж рд╕реЗрд╡рд╛ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреЛ рдЕрдкрдбреЗрдЯ рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред рдпрд╣ рд╡рд┐рдзрд┐ рдирд╛рдЬреБрдХ рд╣реИ рдФрд░ рдЗрд╕рдореЗрдВ рдПрдХ рдЧрдВрдз рд╣реИред рдЗрд╕ рдкреНрд░рдХрд╛рд░, рдбрд┐рдлрд╝реЙрд▓реНрдЯ рд░реВрдк рд╕реЗ рдмрдирд╛рдП рд░рдЦрдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ 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 рд╡рд┐рдзрд┐ рдХреЗ рд╣рд╕реНрддрд╛рдХреНрд╖рд░ рдореЗрдВ рднреА рджрд░реНрд╢рд╛рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рдЪреВрдВрдХрд┐ рдорд╛рдкрджрдВрдбреЛрдВ рдХреЗ рдмреАрдЪ рдХреЛрдИ рд╕рдВрдмрдВрдз рдирд╣реАрдВ рд╣реИ WCF рдкреИрд░рд╛рдореАрдЯрд░ рдХреЗ рдЖрдзрд╛рд░ рдкрд░ рдПрдХ рдХреНрд╡реЗрд░реА рдСрдмреНрдЬреЗрдХреНрдЯ рдирд╣реАрдВ рдмрдирд╛рддрд╛ рд╣реИред
рдЖрдЗрдП рдПрдХ рдЙрджрд╛рд╣рд░рдг рджреЗрдЦреЗрдВ: URL SantaClaus.org/requests?country=sheldonopolis&status= рджреЗрд╢ рдФрд░ рд╕реНрдерд┐рддрд┐ рджреНрд╡рд╛рд░рд╛ PresentReuqest рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП PresentReuqest ред
рдпрд╣рд╛рдБ WCF рд╕реЗрд╡рд╛ рдореЗрдВ рд╕рдВрдмрдВрдзрд┐рдд рд╡рд┐рдзрд┐ рд╣реИ:
 public List<PresentRequest> Get(string country, string status) { throw new NotImplementedException(); } 

рд╡рд┐рдзрд┐ рд╣рд╕реНрддрд╛рдХреНрд╖рд░ рдХреЗ рдЕрдиреБрд╕рд╛рд░, рджреЗрд╢ рдФрд░ рд╕реНрдерд┐рддрд┐ рдХреЗ рдмреАрдЪ рдХреЛрдИ рд╕рдВрдмрдВрдз рдирд╣реАрдВ рд╣реИред рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ, рд╣рдо рдирд╣реАрдВ рдЬрд╛рдирддреЗ рдХрд┐ country рдФрд░ status рдХреНрдпрд╛ рдорддрд▓рдм рд╣реИ, рд╣рдо рдХреЗрд╡рд▓ рдЕрдиреБрдорд╛рди рд▓рдЧрд╛ рд╕рдХрддреЗ рд╣реИрдВред рдореЗрд░реА рд░рд╛рдп рдореЗрдВ, WCF рдПрдХ рдХреНрд╡реЗрд░реА рдСрдмреНрдЬреЗрдХреНрдЯ (рдХреНрд░рдордмрджреНрдз) рдХреЗ рдЖрдзрд╛рд░ рдкрд░ рдПрдХ рдХреНрд╡реЗрд░реА рд╕рдордп рд╕реАрдорд╛ рдмрдирд╛рдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдП, рдФрд░ рдПрдХ рдХреНрд╡реЗрд░реА рд╕реНрдЯреНрд░рд┐рдВрдЧ (deserialization) рдХреЗ рдЖрдзрд╛рд░ рдкрд░ рдПрдХ рдХреНрд╡реЗрд░реА рдСрдмреНрдЬреЗрдХреНрдЯ рднреА рдмрдирд╛рдирд╛ рдЪрд╛рд╣рд┐рдПред рдЗрд╕ рдкреНрд░рдХрд╛рд░, рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдЕрдиреБрд░реЛрдз рд╡рд╕реНрддреБ рднреЗрдЬрдиреЗ рдХреЗ рд▓рд┐рдП:
 public class PresentRequestQuery { public string Country { get; set; } public string Status { get; set; } } 

country=sheldonopolis&status=pending рд▓рд┐рдП рдХреНрд░рдордмрджреНрдз рдХрд┐рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдП, рдФрд░ рдХреНрд╡реЗрд░реА рд╕реНрдЯреНрд░рд┐рдВрдЧ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рдмрд╛рдж PresentRequestQuery рдПрдХ рдЙрджрд╛рд╣рд░рдг рдореЗрдВ deserialized рдХрд┐рдпрд╛ рдЬрд╛рдирд╛ рдЪрд╛рд╣рд┐рдП рдФрд░ Get рд╡рд┐рдзрд┐ рдЗрд╕ рддрд░рд╣ рджрд┐рдЦрдирд╛ рдЪрд╛рд╣рд┐рдП:
 public List<PresentRequest> Get(PresentRequestQuery query) { throw new NotImplementedException(); } 

рд╣рдореЗрдВ рдЙрддрдиреЗ рд╣реА рддрд░реАрдХреЗ рдмрдирд╛рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ рдЬрд┐рддрдиреЗ рдХрд┐ рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдкреНрд░рд╢реНрди рд╣реЛрдВред рдпрд╣рд╛рдВ WCF рдХреА рдЧрд╛рдЗрдб рд╕реЗ рд▓реЗрдХрд░ рдбрд┐рдЬрд╛рдЗрдирд┐рдВрдЧ рдФрд░ рдмрд┐рд▓реНрдбрд┐рдВрдЧ рд░рд┐рд╕реНрдЯрдлреБрд▓ рд╡реЗрдм рд╕рд░реНрд╡рд┐рд╕реЗрдЬ рдХрд╛ рдПрдХ рдирдореВрдирд╛ рдХреЛрдб рд╣реИ:
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 рдХреНрд╡реЗрд░реА рд╕реНрдЯреНрд░рд┐рдВрдЧ рдХреНрд░рдорд╛рдВрдХрди рдХрд╛ рд╕рдорд░реНрдерди рдХреНрдпреЛрдВ рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИ, рдЕрд░реНрдерд╛рдд рдХреНрд╡реЗрд░реА рд╕реНрдЯреНрд░рд┐рдВрдЧ рд╕реЗ рдСрдмреНрдЬреЗрдХреНрдЯ рдмрдирд╛ рд░рд╣рд╛ рд╣реИред рдпрд╣ рд╕рд░рд▓ рдЪрд╛рд▓ рдПрдХ рдЕрдзрд┐рдХ рд╕реНрдерд┐рд░ рд╡рд┐рдзрд┐ рд╣рд╕реНрддрд╛рдХреНрд╖рд░ рдмрдирд╛рдиреЗ рдореЗрдВ рдорджрдж рдХрд░ рд╕рдХрддреА рд╣реИред рджреВрд╕рд░реА рдУрд░, рдЧреЗрдЯ рд╡рд┐рдзрд┐ рдореЗрдВ рдЗрд╕ рддрд░рд╣ рдХреЗ рд╣рд╕реНрддрд╛рдХреНрд╖рд░ рд╣реЛ рд╕рдХрддреЗ рд╣реИрдВред рддреЛ рд╡рд┐рдзрд┐ рдХрд╛ рдкреНрд░рдХрд╛рд░ рдкреБрди: рдкреНрд░рдпреЛрдЬреНрдп рдФрд░ рдмрд╣реБрд░реВрдкреА рд╣реИ ред
 Message Get (Message request); 

рдСрдкрд░реЗрд╢рди Get :

рдХреГрдкрдпрд╛ рдзреНрдпрд╛рди рд░рдЦреЗрдВ рдХрд┐ WCF SOAP рд╕реЗрд╡рд╛ рдореЗрдВ рдПрдХ рдмрд╣реБрд░реВрдкрддрд╛ рд╣реИ, рдЕрдзрд┐рдХ рд╕рдЯреАрдХ рд░реВрдк рд╕реЗ рдпрд╣ рдПрдХ рд╡рд┐рд╢реЗрд╖ рдмрд╣реБрд░реВрдкрддрд╛ ( рддрджрд░реНрде рдмрд╣реБрд░реВрдкрддрд╛ ) рд╣реИ рдЬреЛ рдХрд┐ KnownTypeAttribute рдорд╛рдзреНрдпрдо рд╕реЗ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рд▓реЗрдХрд┐рди, рдореЗрд░реА рд░рд╛рдп рдореЗрдВ, WCF рдХреЛ рдкреИрд░рд╛рдореАрдЯреНрд░рд┐рдХ рдмрд╣реБрд░реВрдкрддрд╛ рдХрд╛ рд╕рдорд░реНрдерди рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдПред

рдирд┐рд╖реНрдХрд░реНрд╖


рд░реИрд╕реНрдЯрдлреБрд▓ рдлреНрд░реЗрдорд╡рд░реНрдХ рдХреЗ рд░реВрдк рдореЗрдВ рдбрдмреНрд▓реНрдпреВрд╕реАрдПрдл рдореЗрдВ рдХрдИ рд╡рд╛рд╕реНрддреБрд╢рд┐рд▓реНрдк рд╡рд┐рд╢реЗрд╖рддрд╛рдПрдВ рд╣реИрдВ рдЬреЛ рдкреБрди: рдкреНрд░рдпреЛрдЬреНрдп рдФрд░ рд╕реНрдерд┐рд░ рд╕реЗрд╡рд╛рдУрдВ рдХреЛ рдмрдирд╛рдирд╛ рдореБрд╢реНрдХрд┐рд▓ рдмрдирд╛ рджреЗрддреА рд╣реИрдВред рджреВрд╕рд░реА рдУрд░, рдбрдмреНрд▓реНрдпреВрд╕реАрдПрдл рдХреЗ рдкрд╛рд╕ рдЗрди рд╕рдорд╕реНрдпрд╛рдУрдВ рдХреЛ рд╣рд▓ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЖрд╡рд╢реНрдпрдХ рд╕рдм рдХреБрдЫ рд╣реИред

WCF рдкрд░ рдмреЗрд╣рддрд░ рд╡реЗрдм рд╕реЗрд╡рд╛: рдмреЗрд╣рддрд░ рдбрд┐рдЬрд╛рдЗрди


рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ, рдЖрдЗрдП Get рд╡рд┐рдзрд┐ рдХреА рдХрдорд┐рдпреЛрдВ рдХреЛ рдареАрдХ Get ред рдореБрдЭреЗ рд▓рдЧрддрд╛ рд╣реИ рдХрд┐ рдПрдХ рдзрд╛рд░рд╛рд╡рд╛рд╣рд┐рдХ рд╕рдВрджреЗрд╢ рд╕реЗрд╡рд╛ рджреГрд╖реНрдЯрд┐рдХреЛрдг рд╣рдорд╛рд░реА рдорджрдж рдХрд░ рд╕рдХрддрд╛ рд╣реИред

URL рд╕реАрд░рд┐рдпрд▓рд╛рдЗрдЬрд╝реЗрд╢рди рдФрд░ рдбрд┐рд╕рд░рд▓рд╛рдЗрдЬрд╝реЗрд╢рди


рд╣рдо рдкрд╣рд▓реЗ рд╕реЗ рд╣реА PresentRequestQuery рд╡рд░реНрдЧ рджреЗрдЦ рдЪреБрдХреЗ рд╣реИрдВ, рд▓реЗрдХрд┐рди рдЕрдм рдЗрд╕реЗ рдХреНрд░рдордмрджреНрдз рдХрд░рддреЗ рд╣реИрдВред
 public class PresentRequestQuery { public string Country { get; set; } public string Status { get; set; } } 

рдЬреИрд╕рд╛ рдХрд┐ рд╣рдо рдЬрд╛рдирддреЗ рд╣реИрдВ, Get рдкреИрд░рд╛рдореАрдЯрд░ рдХреЛ рдПрдХ рд╕реНрдЯреНрд░рд┐рдВрдЧ рд╕реНрдЯреНрд░рд┐рдВрдЧ рдХреЗ рд░реВрдк рдореЗрдВ рднреЗрдЬрддрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП рд╣рдорд╛рд░реА рдХреНрд░рдорд╛рдВрдХрди рд╡рд┐рдзрд┐ рдХреЛ рдПрдХ рдорд╛рдиреНрдп рдХреНрд╡реЗрд░реА рд╕реНрдЯреНрд░рд┐рдВрдЧ рдмрдирд╛рдирд╛ рдЪрд╛рд╣рд┐рдПред рдХреНрд░рдорд╛рдВрдХрди рд╕реЗ рдЙрддреНрдкрдиреНрди рдЖрджрд░реНрд╢ рдХреНрд╡реЗрд░реА рд╕реНрдЯреНрд░рд┐рдВрдЧ рдЗрд╕ рддрд░рд╣ рджрд┐рдЦрдирд╛ рдЪрд╛рд╣рд┐рдП: country=sheldonopolis&status=pending рдФрд░ рд╣рдо рдХреБрдЫ рдЗрд╕реА рддрд░рд╣ рдХрд╛ рдирд┐рд░реНрдорд╛рдг рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВред рдЖрджрд░реНрд╢ рдХреНрд░рдорд╛рдВрдХрди рдкрд░рд┐рдгрд╛рдо рдореЗрдВ рдПрдХ рдЦрд╛рдореА рд╣реИ: рдорд╛рдкрджрдВрдбреЛрдВ рдХреЗ рдмреАрдЪ рд╕рдВрдЪрд╛рд░ рдХреА рдХрдореА, рдЗрд╕рд▓рд┐рдП рд╣рдо URL рдХреЛ рдЕрдиреБрд░реЛрдз рд╡рд╕реНрддреБ рдореЗрдВ рд╢рд╛рдорд┐рд▓ рдирд╣реАрдВ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВред рд╣рдорд╛рд░реЗ рдХреНрд░рдорд╛рдВрдХрди рддрдВрддреНрд░ рдХреЛ рдЗрд╕ рд╕рдорд╕реНрдпрд╛ рдХреЛ рднреА рд╣рд▓ рдХрд░рдирд╛ рдЪрд╛рд╣рд┐рдПред

рд╕рд╛рдорд╛рдиреНрдпрддрдпрд╛, рдПрдХ рдХреНрд╡реЗрд░реА рд╕реНрдЯреНрд░рд┐рдВрдЧ рдЕрд▓рдЧ-рдЕрд▓рдЧ рдХреБрдВрдЬреА-рдореВрд▓реНрдп рдЬреЛрдбрд╝реЗ рдХрд╛ рдПрдХ рд╕рдВрдЧреНрд░рд╣ рд╣реИ: key1=value1&key2=value2&key3=value3 ред
рд╣рдорд╛рд░реЗ рдорд╛рдорд▓реЗ рдореЗрдВ, рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рджреЛ рдХреБрдВрдЬреА рд╣реИрдВ:
рдореИрдВ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдХреНрд░рдорд╛рдВрдХрди рдПрд▓реНрдЧреЛрд░рд┐рдереНрдо рджреЗрдЦрддрд╛ рд╣реВрдВ:
  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 рдПрдХ рдЙрджрд╛рд╣рд░рдг рдореЗрдВ deserialized рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдмрд╣реБрдд рд╕рд░рд▓ рд╣реИ:
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 рдХреЗрд╡рд▓ Uri.EscapeDataString рдФрд░ JsonDataContractSerializer рдХреЛ рдХреЙрд▓ Uri.EscapeDataString рд╣реИ, рд╡рд╣ 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); } 

рдПрдХ рдкреНрд░рддрд┐рд╖реНрдард┐рдд рд╢реИрд▓реА рдореЗрдВ рдХрдо рд╕реЗ рдХрдо рдЪрд╛рд░ рд╡рд┐рдзрд┐рдпреЛрдВ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИ: Get, Post, Put, Delete ServiceContract рдФрд░ ServiceContract рдХреБрдЫ рдЗрд╕ рддрд░рд╣ рд╣реЛ рд╕рдХрддрд╛ рд╣реИ:
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 рдореЗрдВ рд▓рдЪреАрд▓рд╛рдкрди, рд╕реНрдерд┐рд░рддрд╛ рдФрд░ рд░рдЦрд░рдЦрд╛рд╡ рдореЗрдВ рдЖрд╕рд╛рдиреА рд╣реИред рд╣рдо рдХрд┐рд╕реА рднреА рдбреЗрдЯрд╛ рдХреЛ рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рдХреНрдпреЛрдВрдХрд┐ рдпрд╣ рд╕реЗрд╡рд╛ рдХреЗрд╡рд▓ Message рд╡рд░реНрдЧ рдкрд░ рдирд┐рд░реНрднрд░ рдХрд░рддреА рд╣реИ, рдЬреЛ WCF ( MSDN ) рдХреЗ рд▓рд┐рдП рдореВрд▓рднреВрдд рд╣реИред рдПрдХ рдФрд░ рдлрд╛рдпрджрд╛ CRUD рд╣реИред IJsonService рдФрд░ URL рдХреНрд░рдорд╛рдВрдХрди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реБрдП, рд╣рдо рдкреИрд░рд╛рдореАрдЯреНрд░рд┐рдХ рдмрд╣реБрд░реВрдкрддрд╛ рдХреЗ рд╕рд╛рде рдкреБрди: рдкреНрд░рдпреЛрдЬреНрдп рд╕реЗрд╡рд╛рдПрдВ рдмрдирд╛ рд╕рдХрддреЗ рд╣реИрдВред

рдПрдХ рдкреНрд░рддрд┐рд╖реНрдард┐рдд рд╕реЗрд╡рд╛ рдХреЛ рд▓рд╛рдЧреВ рдХрд░рдирд╛


рдореИрдВ рдпрд╣рд╛рдВ рд╕рднреА рдХреЛрдб рдирд╣реАрдВ рджреВрдВрдЧрд╛, рдХреНрдпреЛрдВрдХрд┐ рдпрд╣ рдкрд╣рд▓реЗ рд╣реА рдЙрджреНрдзреГрдд рдХрд┐рдпрд╛ рдЬрд╛ рдЪреБрдХрд╛ рд╣реИ ред рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рдЕрдиреБрд░реЛрдз, рдЕрджреНрдпрддрди, рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдФрд░ рд╣рдЯрд╛рдиреЗ рдХреЗ рддрд░реАрдХреЗ рдХрд╛ рдПрдХ рдЙрджрд╛рд╣рд░рдг рд╣реИред
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 рдХреЗ рдЕрдиреБрд╕рд╛рд░ред
рд╕рдмрд╕реЗ рджрд┐рд▓рдЪрд╕реНрдк рдмрд╛рдд RestRequestMetadata рдореЗрдВ RestRequestMetadata , рдПрдХ рд╡рд░реНрдЧ рдЬреЛ рдПрдХ URL рд╕реЗ рдПрдХ рд╡рд┐рд╢рд┐рд╖реНрдЯ рдЕрдиреБрд░реЛрдз рдмрдирд╛рдиреЗ рдореЗрдВ рдорджрдж рдХрд░рддрд╛ рд╣реИред RestRequestMetadata рдХреЗ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдкрд░ рдПрдХ рдирдЬрд╝рд░ рдбрд╛рд▓рдиреЗ рд╕реЗ рдкрд╣рд▓реЗ, рдореИрдВ рдХреБрдЫ рд╕реНрдкрд╖реНрдЯреАрдХрд░рдг рджреЗрдирд╛ рдЪрд╛рд╣рддрд╛ рд╣реВрдВред RestRequestMetadata рдПрдХ рдХреНрд╡реЗрд░реА рд╕реНрдЯреНрд░рд┐рдВрдЧ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдФрд░ рдПрдХ рд╡рд┐рд╢рд┐рд╖реНрдЯ рдХреНрд╡реЗрд░реА рдмрдирд╛рдиреЗ рдХреЗ рд▓рд┐рдП RestRequestMetadata рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реИред рдпрд╣ рдЕрдиреБрд░реЛрдз рдХреЗ рдЖрдзрд╛рд░ рдкрд░ рдПрдХ рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рд╕рдВрджреЗрд╢ рднреА рдмрдирд╛ рд╕рдХрддрд╛ рд╣реИред
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 рд╡рд░реНрдЧ рджреНрд╡рд╛рд░рд╛ рд╕рдВрд╕рд╛рдзрд┐рдд рдХрд┐рдП рдЬрд╛рддреЗ рд╣реИрдВред
рдЕрдиреБрд░реЛрдзрдкреНрд░реЛрд╕реЗрд╕рд░ <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); } } 


рд░реЗрд╕реНрдЯрдлреБрд▓ рд╕рд░реНрд╡рд┐рд╕ рдХреНрд▓рд╛рдЗрдВрдЯ


рдХреНрд▓рд╛рдЗрдВрдЯ рдХрд╛рдлреА рд╕рд░рд▓ рд╣реИ, рдмрд╕ рдбреЗрдЯрд╛ рдХреЛ рдХреНрд╡реЗрд░реА рд╕реНрдЯреНрд░рд┐рдВрдЧ рдореЗрдВ рдХреНрд░рдордмрджреНрдз рдХрд░рддрд╛ рд╣реИ рдФрд░ рдЗрд╕реЗ рд╕реЗрд╡рд╛ рдореЗрдВ рднреЗрдЬрддрд╛ рд╣реИред рдХреНрд▓рд╛рдЗрдВрдЯ 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 рдХрд╛ рдЦреБрд╢ рдорд╛рд▓рд┐рдХ рдмрдирд╛рддреЗ рд╣реИрдВред

рдЙрддреНрдХреГрд╖реНрдЯ рд╕реЗрд╡рд╛ рдЙрджрд╛рд╣рд░рдг


рд╕рд╛рдВрддрд╛ рдХреЛ рдЕрдм рднреА рдПрдХ рд░реИрд╕реНрдЯрдлреБрд▓ рд╕реЗрд╡рд╛ рдХреА рдЙрдореНрдореАрдж рд╣реИ рдЬреЛ рдлрд╝рд┐рд▓реНрдЯрд░ рджреНрд╡рд╛рд░рд╛ рдЙрдкрд╣рд╛рд░ рдЕрдиреБрд░реЛрдзреЛрдВ рдХреЛ рдмрдЪрд╛ рд╕рдХрддрд╛ рд╣реИ рдФрд░ рдЦреЛрдЬ рд╕рдХрддрд╛ рд╣реИред

рд╕реЗрд╡рд╛


рдХреЙрдиреНрдлрд╝рд┐рдЧрд░реЗрд╢рди рдлрд╝рд╛рдЗрд▓ рд╕рдмрд╕реЗ рдЖрдо рд╣реИ:

рд╡рд┐рдиреНрдпрд╛рд╕
 <?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(); } 

рдирд┐рд╖реНрдкрд╛рджрди рдкрд░рд┐рдгрд╛рдо: рдлрд╝рд┐рдбрд▓рд░ рд╕реНрдХреНрд░реАрдирд╢реЙрдЯ :
рдЫрд╡рд┐

рдЕрдВрдд


рд╕рдВрджреЗрд╢-рдЖрдзрд╛рд░рд┐рдд рджреГрд╖реНрдЯрд┐рдХреЛрдг рдПрдХ рдореЗрдЧрд╛-рд╢рдХреНрддрд┐рд╢рд╛рд▓реА рд╡рд╛рд╕реНрддреБрдХрд▓рд╛ рд╢реИрд▓реА рд╣реИред рдпрд╣ рдПрдХ рд╕реНрдерд┐рд░, рдмрдирд╛рдП рд░рдЦрдиреЗ рдпреЛрдЧреНрдп рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдХреЗ рд╕рд╛рде рдПрдХ рд░реЗрд╕реНрдЯрдлреБрд▓ рд╕реЗрд╡рд╛ рдмрдирд╛рдиреЗ рдореЗрдВ рдорджрдж рдХрд░ рд╕рдХрддрд╛ рд╣реИ, рдФрд░ рдирд┐рд╢реНрдЪрд┐рдд рд░реВрдк рд╕реЗ рд╕рд╛рдВрддрд╛ рдЦреБрдж рдХреЛ рдХреНрд░рд┐рд╕рдорд╕ рдХреА рдЙрдкрд╕реНрдерд┐рддрд┐ рдХреЗ рд░реВрдк рдореЗрдВ рдРрд╕реА рд░реЗрд╕реНрдЯрдлреБрд▓ рд╕реЗрд╡рд╛ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреА рдХреГрдкрд╛ рдХрд░реЗрдЧрд╛ред

рд╕реНрд░реЛрддреЛрдВ рдХреЛ рдореВрд▓ рд▓реЗрдЦ рдпрд╛ рдкрд░рд┐рдпреЛрдЬрдирд╛ рдХреА рд╡реЗрдмрд╕рд╛рдЗрдЯ рд╕реЗ рдбрд╛рдЙрдирд▓реЛрдб рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ ред рдирдЧреЗрдЯ рдкреИрдХреЗрдЬ
рднреА рдЙрдкрд▓рдмреНрдз рд╣реИ ред

рджрд┐рд▓рдЪрд╕реНрдк рд╕рдВрдмрдВрдзрд┐рдд рд▓реЗрдЦ: рд╕рдВрджреЗрд╢ рдЖрдзрд╛рд░рд┐рдд рд╡реЗрдм рд╕реЗрд╡рд╛рдУрдВ рдХреЗ рд▓рд╛рдн ред

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


All Articles