基于WinService,开发Restful 风格的Wcf 服务端

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于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,笔者使用批处理脚本来进行服务安装不再赘述。

以上是关于基于WinService,开发Restful 风格的Wcf 服务端的主要内容,如果未能解决你的问题,请参考以下文章

RESTful风格以及统一响应结果

restful风格

restful风格接口和spring的运用

restful风格。

SpringMVC-------4.restful风格和异常处理

Springboot第五章 接口架构风格 — RESTful