WCF-REST IServiceBehavior 對Service 監控 用戶訪問驗證/異常處理...

通過實現IServiceBehavior 接口,可以對Service 訪問時做全局性監控,比如:異常處理、客戶端語言版本、以及安全驗證

在工廠Opening 過程中,給ServiceHost 添加 Behavior

private void Host_Opening(object sender, EventArgs e)
        {
            ServiceHost host = sender as ServiceHost;
            
            if (host == null)
            {
                return;
            }

            RestServiceBehavior b = host.Description.Behaviors.Find<RestServiceBehavior>();
            if (b == null)
            {
                host.Description.Behaviors.Add(new RestServiceBehavior());
            }            
        }

RestServiceBehavior.cs

namespace H.Utility.WCF
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ServiceModel.Description;
    using System.ServiceModel;
    using System.Collections.ObjectModel;
    using System.ServiceModel.Dispatcher;
    using System.ServiceModel.Channels;

    /// <summary>
    /// 1. 對異常信息的包裝,將Service端未捕獲的異常轉化爲JSON返還給客戶端  --- RestServiceErrorHandler
    /// 2. 多語言的處理,將客戶端Request信息裏的語言信息讀出,然後設置當前線程的Culture --- RestServiceParameterInspector
    /// 3. 使用url參數上的數據來修改Get方式時的http頭 --- RestServiceMessageInspector
    /// </summary>
    public class RestServiceBehavior : IServiceBehavior
    {
        private Type m_BizExceptionType = null;
        private Type m_ExceptionHandler = null;

        public RestServiceBehavior()
        {

        }

        public RestServiceBehavior(string customBizExceptionTypeName, string exceptionHandlerTypeName)
        {
            if (customBizExceptionTypeName != null && customBizExceptionTypeName.Trim().Length > 0)
            {
                m_BizExceptionType = Type.GetType(customBizExceptionTypeName, true);
            }
            if (exceptionHandlerTypeName != null && exceptionHandlerTypeName.Trim().Length > 0)
            {
                m_ExceptionHandler = Type.GetType(exceptionHandlerTypeName, true);
            }
        }

        public RestServiceBehavior(Type customBizExceptionType, Type exceptionHandlerType)
        {
            m_BizExceptionType = customBizExceptionType;
            m_ExceptionHandler = exceptionHandlerType;
        }

        public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
        {

        }

        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers)
            {
                foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints)
                {
                    endpointDispatcher.DispatchRuntime.MessageInspectors.Insert(0, new RestServiceMessageInspector());
                    foreach (DispatchOperation dispatchOperation in endpointDispatcher.DispatchRuntime.Operations)
                    {
                        dispatchOperation.ParameterInspectors.Add(new RestServiceParameterInspector());
                    }
                }
                channelDispatcher.ErrorHandlers.Add(new RestServiceErrorHandler());
            }
        }

        public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {

        }
    }
}
RestServiceErrorHandler.cs

namespace H.Utility.WCF
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ServiceModel.Dispatcher;
    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using System.Net;
    using System.ServiceModel.Security;
    using System.Web;
    using System.Threading;
    using System.ServiceModel.Web;
    using System.Xml;
    using System.Runtime.Serialization.Json;
    using System.Runtime.Serialization;
    using System.Configuration;

    /// <summary>
    /// 處理異常
    /// </summary>
    public class RestServiceErrorHandler : IErrorHandler
    {

        public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
        {
            HttpStatusCode statusCode;
            if (error is SecurityAccessDeniedException)
            {
                statusCode = HttpStatusCode.Unauthorized;
            }
            else if (error is ServerTooBusyException)
            {
                statusCode = HttpStatusCode.ServiceUnavailable;
            }
            else
            {
                statusCode = HttpStatusCode.InternalServerError;
            }

            RestServiceError errorData = new RestServiceError()
            {
                StatusCode = (int)statusCode,
                StatusDescription = HttpWorkerRequest.GetStatusDescription((int)statusCode),
                Faults = new List<Error>()
            };

            var errorEntity = new Error();
            errorEntity.ErrorCode = "00000";

            //
            errorEntity.ErrorDescription = error.ToString();

            errorData.Faults.Add(errorEntity);


            if (version == MessageVersion.None)
            {
                WebMessageFormat messageFormat = WebOperationContext.Current.OutgoingResponse.Format ?? WebMessageFormat.Xml;
                WebContentFormat contentFormat = WebContentFormat.Xml;
                string contentType = "text/xml";

                if (messageFormat == WebMessageFormat.Json)
                {
                    contentFormat = WebContentFormat.Json;
                    contentType = "application/json";
                }

                WebBodyFormatMessageProperty bodyFormat = new WebBodyFormatMessageProperty(contentFormat);

                HttpResponseMessageProperty responseMessage = new HttpResponseMessageProperty();
                responseMessage.StatusCode = HttpStatusCode.OK;
                responseMessage.StatusDescription = HttpWorkerRequest.GetStatusDescription((int)responseMessage.StatusCode);
                responseMessage.Headers[HttpResponseHeader.ContentType] = contentType;
                responseMessage.Headers["X-HTTP-StatusCode-Override"] = "500";

                fault = Message.CreateMessage(MessageVersion.None, null, new RestServiceErrorWriter() { Error = errorData, Format = contentFormat });
                fault.Properties[WebBodyFormatMessageProperty.Name] = bodyFormat;
                fault.Properties[HttpResponseMessageProperty.Name] = responseMessage;
            }
        }

        class RestServiceErrorWriter : BodyWriter
        {
            public RestServiceErrorWriter()
                : base(true)
            { }

            public RestServiceError Error { get; set; }

            public WebContentFormat Format { get; set; }

            protected override BodyWriter OnCreateBufferedCopy(int maxBufferSize)
            {
                return base.OnCreateBufferedCopy(maxBufferSize);
            }

            protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
            {
                if (Format == WebContentFormat.Json)
                {
                    new DataContractJsonSerializer(typeof(RestServiceError)).WriteObject(writer, Error);
                }
                else
                {
                    new DataContractSerializer(typeof(RestServiceError)).WriteObject(writer, Error);
                }
            }
        }

        public bool HandleError(Exception error)
        {
            return true;
        }
    }
}
RestServiceMessageInspector.cs

namespace H.Utility.WCF
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Web;
    using System.ServiceModel.Dispatcher;
    using System.Security.Cryptography;
    using H.BizEntity;

    /// <summary>
    /// 使用url參數上的數據來修改Get方式時的http頭,同時如果有傳當前用戶信息,還會驗證簽名,並作授權驗證
    /// 此方法優先級比IEndpointBehavior 低
    /// </summary>
    public class RestServiceMessageInspector : IDispatchMessageInspector
    {
        
        #region IDispatchMessageInspector Members
        /// <summary>
        /// 授權驗證
        /// </summary>
        /// <param name="request"></param>
        /// <param name="channel"></param>
        /// <param name="instanceContext"></param>
        /// <returns></returns>
        public object AfterReceiveRequest(ref Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
        {
            throw new BizException("未經授權訪問...");
            return null;
        }

        private string GetQueryStringValue(IncomingWebRequestContext context, string key)
        {
            try
            {
                var queryStrings = context.UriTemplateMatch.QueryParameters;
                return queryStrings[key];
            }
            catch
            {
                return null;
            }
        }

        public void BeforeSendReply(ref Message reply, object correlationState)
        {
            HttpResponseMessageProperty response = reply.Properties[HttpResponseMessageProperty.Name] as HttpResponseMessageProperty;
            response.Headers["Cache-Control"] = "No-Cache";
        }

        #endregion
    }
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章