您的位置:首页 > 运维架构

WCF-REST IServiceBehavior 对Service 监控 用户访问验证/异常处理...

2013-05-24 21:23 627 查看
通过实现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
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐