基于WinService,开发Restful 风格的Wcf 服务端
开发目的:降低和IIS的耦合,使用Winservice做宿主,迁移方便快捷。
步骤:
1.添加WcfService,分别实现 Get 和 Post 方法
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Runtime.Serialization; 5 using System.ServiceModel; 6 using System.ServiceModel.Web; 7 using System.Text; 8 9 namespace WcfSelfHost 10 { 11 // NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IService1" in both code and config file together. 12 [ServiceContract] 13 public interface IService1 14 { 15 [OperationContract] 16 string Get(); 17 18 [OperationContract] 19 20 string Post(); 21 } 22 }
2.分别为每个方法添加路由信息,指定请求方式,确定返回信息格式
[ServiceContract] public interface IService1 { [OperationContract] [WebInvoke(Method = "GET", ResponseFormat = WebMessageFormat.Json, UriTemplate = "/method/")] string Get(); [OperationContract] [WebInvoke(Method = "Post", ResponseFormat = WebMessageFormat.Json, UriTemplate = "/method/")] string Post(); }
3.接口实现:
public class Service1 : IService1 { #region Implementation of IService1 public string Get() { return "this is get method!"; } public string Post() { return "this is post method"; } #endregion }
4.如何解决跨域问题(坑)?
如果使用ajax来访问,会有跨域问题,api正常返回数据,但是ajax无法接收。
经测试,发现最好用的方法,是通过对请求添加拦截器的方式来处理,可以100%的解决跨域问题:
MessageInspector.cs
public class MessageInspector:IDispatchMessageInspector { private ServiceEndpoint _serviceEndpoint; public MessageInspector(ServiceEndpoint serviceEndpoint) { _serviceEndpoint = serviceEndpoint; } /// <summary> /// Called when an inbound message been received /// </summary> /// <param name="request">The request message.</param> /// <param name="channel">The incoming channel.</param> /// <param name="instanceContext">The current service instance.</param> /// <returns> /// The object used to correlate stateMsg. /// This object is passed back in the method. /// </returns> public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext) { StateMessage stateMsg = null; HttpRequestMessageProperty requestProperty = null; if (request.Properties.ContainsKey(HttpRequestMessageProperty.Name)) { requestProperty = request.Properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty; } if (requestProperty != null) { var origin = requestProperty.Headers["Origin"]; if (!string.IsNullOrEmpty(origin)) { stateMsg = new StateMessage(); // if a cors options request (preflight) is detected, // we create our own reply message and don't invoke any // operation at all. if (requestProperty.Method == "OPTIONS") { stateMsg.Message = Message.CreateMessage(request.Version, null); } request.Properties.Add("CrossOriginResourceSharingState", stateMsg); } } return stateMsg; } /// <summary> /// Called after the operation has returned but before the reply message /// is sent. /// </summary> /// <param name="reply">The reply message. This value is null if the /// operation is one way.</param> /// <param name="correlationState">The correlation object returned from /// the method.</param> public void BeforeSendReply(ref Message reply, object correlationState) { var stateMsg = correlationState as StateMessage; if (stateMsg != null) { if (stateMsg.Message != null) { reply = stateMsg.Message; } HttpResponseMessageProperty responseProperty = null; if (reply.Properties.ContainsKey(HttpResponseMessageProperty.Name)) { responseProperty = reply.Properties[HttpResponseMessageProperty.Name] as HttpResponseMessageProperty; } if (responseProperty == null) { responseProperty = new HttpResponseMessageProperty(); reply.Properties.Add(HttpResponseMessageProperty.Name, responseProperty); } // Access-Control-Allow-Origin should be added for all cors responses responseProperty.Headers.Set("Access-Control-Allow-Origin", "*"); if (stateMsg.Message != null) { // the following headers should only be added for OPTIONS requests responseProperty.Headers.Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET,DELETE,PUT"); responseProperty.Headers.Set("Access-Control-Allow-Headers", "Content-Type, Accept, Authorization, x-requested-with"); } } } } class StateMessage { public Message Message; }
BehaviorAttribute.cs
public class BehaviorAttribute : Attribute, IEndpointBehavior, IOperationBehavior { public void Validate(ServiceEndpoint endpoint) { } public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { } /// <summary> /// This service modify or extend the service across an endpoint. /// </summary> /// <param name="endpoint">The endpoint that exposes the contract.</param> /// <param name="endpointDispatcher">The endpoint dispatcher to be /// modified or extended.</param> public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { // add inspector which detects cross origin requests endpointDispatcher.DispatchRuntime.MessageInspectors.Add( new MessageInspector(endpoint)); } public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { } public void Validate(OperationDescription operationDescription) { } public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation) { } public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation) { } public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters) { } }
对请求添加拦截:
private static ServiceHost host; private static ServiceHost crosspolicy; public static void Start() { host = new ServiceHost(typeof(Service1)); host.Description.Endpoints[0].Behaviors.Add(new BehaviorAttribute()); host.Opening += (o, e) => {}; host.Opened += (o, e) => { }; host.Closing += (o, e) => { Console.WriteLine(" data server Closing..."); }; host.Closed += (o, e) => { Console.WriteLine("data setver Closed."); }; host.Open(); Console.Read(); }
5.APP.config:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.web> <compilation debug="true"/> </system.web> <system.serviceModel> <services> <service name="WcfSelfHost.Service1"> <endpoint address="" binding="webHttpBinding" contract="WcfSelfHost.IService1" behaviorConfiguration="web"> </endpoint> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /> <host> <baseAddresses> <add baseAddress="http://127.0.0.1:12121/api/" /> </baseAddresses> </host> </service> </services> <behaviors> <serviceBehaviors> <behavior > <serviceMetadata httpGetEnabled="True"/> <serviceDebug includeExceptionDetailInFaults="False" /> </behavior> </serviceBehaviors> <endpointBehaviors> <behavior name="web"> <webHttp/> </behavior> </endpointBehaviors> </behaviors> </system.serviceModel> </configuration>
后续步骤:改造成winService,笔者使用批处理脚本来进行服务安装不再赘述。