鑒於一些理由需要攔截WCF方法,比如參數的檢測。一個實際的場景是CSRF防范中需要對CsrfToken的檢測。
要實現攔截,在形式上要實現兩點:
1:為WCF方法增加特性類;
2:捕獲WCF方法;
第一點很好實現,實現一個Attribute類。第二點則可以利用WCF框架中的IOperationInvoker接口,讓該Attribute類同時也實現該接口,同時在該接口方法的ApplyDispatchBehavior方法中,為dispatchOperation實現一個新的OperationInvoker。
public class MyOperationInterceptorAttribute : Attribute, IOperationBehavior { public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters) { } public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation) { } public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation) { dispatchOperation.Invoker = new } public void Validate(OperationDescription operationDescription) { } } public class MyInvoker : IOperationInvoker { IOperationInvoker m_OldInvoker; public object Invoke(object instance, object[] inputs, out object[] outputs) { try { //do something object result = m_OldInvoker.Invoke(instance, inputs, out outputParams); return result; } catch (Exception err) { outputs = new object[0]; //do catch return null; } finally { } } public virtual object[] AllocateInputs() { return m_OldInvoker.AllocateInputs(); } public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state) { return m_OldInvoker.InvokeBegin(instance, inputs, callback, state); } public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result) { return m_OldInvoker.InvokeEnd(instance, out outputs, result); } public bool IsSynchronous { get { return m_OldInvoker.IsSynchronous; } } }
如果我們將服務宿主在IIS中,則這種實現的好處是我們可以完全不需要更改配置文件就能實現對WCF方法的攔截。
這種方式還有一種好處是,我們可以解析客戶端發送過來的內容,並統一進行處理,而無需為WCF方法增加參數。如,我們對CSRF方法的時候,客戶端發送過來的內容中會含有CSRFToken,傳統的做法是為WCF方法一個CSRFToken的參數,如下:
public void DoSomething(string arg0, string csrfToken) { Check(csrfToken); ... }
並且每個需要處理CSRF的方法中都要進行如上編碼,而現在只要變成:
[MyOperationInterceptor] public void DoSomething(string arg0) { ... }
可以看到少了參數,少了方法內的處理,當然,我們需要在MyInvoker類的Invoke方法的//do something處解析前台內容,獲取CSRFToken,並進行Check,獲取CSRFToken如下實現:
string GetToken() { //rest or soap string token = HttpContext.Current.Request.QueryString["token"]; if(string.IsNullOrEmpty(token)) { XDocument doc = XDocument.Parse(OperationContext.Current.RequestContext.RequestMessage.ToString()); var rootElements = (from p in doc.Descendants("root") select p).Elements(); var args = (from p in rootElements select new {Name=p.Name.LocalName, Value=p.Value}).ToList(0; token = (from p in args where p.Name="token" select p.Value).First(); } return token; }
在這種攔截的應用中,有時候我們難免要將攔截過程中的一些內容傳遞到WCF方法中,如果有這樣的需求,則可以將這些內容放置到OperationContet.Current.IncomingMessageProperties中,它是一個IDictionary<string, object>,當然,要對這個參數進行存取的時候,我們需要用到OperationContextScope,於是,整個MyInvoker類的Invoke方法變成如下的形式:
try { using(OperationContextScope scope = new OperationContextScope()) { //get token and check OperationContet.Current.IncomingMessageProperties.Add("SomeKey", SomeObjectInstance); //do something object result = m_OldInvoker.Invoke(instance, inputs, out outputParams); return result; } } catch (Exception err) { outputs = new object[0]; //do catch return null; } finally { }
在具體的WCF方法中,如果要使用這個SomeObjectInstance,則可以這樣獲得:
SomeObjectInstance = OperationContet.Current.IncomingMessageProperties["SomeKeyas TheInstanceType;