条件 WCF DataContract 序列化(使用 DataContractSurrogate)





5.00/5 (1投票)
如何以声明方式添加条件化 DataContract 序列化。
引言
在为我们的系统开发 Web 服务时,我们希望设计一个通用的服务层和通用的数据契约,以满足不同基于角色的用户的需求。用户角色区分非常重要,因为有些敏感数据点只能与特权用户共享。一种设计方法是使数据转换器角色特定。转换器可以确定用户角色,并相应地序列化数据。这种方法很简单,但不够优雅,并且会在各种转换器中引入大量的额外角色检查。
另一种方法是使服务层和转换器与角色无关,并使用更声明性的方法,即使用 Data Contract Surrogates(数据契约代理)。Data Contract Surrogate 是一项高级功能,可用于在 Data Contracts 序列化/反序列化期间引入特殊行为。
实现
让我们从定义一个属性 ConditionalDataMemberAttribute
开始。此属性将用于装饰需要有条件序列化的 DataMembers。稍后会讨论此属性的使用方法。
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
public class ConditionalDataMemberAttribute : Attribute
下一步是创建一个自定义服务行为,称为 ConditionalDataBehavior
。
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class ConditionalDataBehavior : Attribute, IServiceBehavior
在服务行为 ConditionalDataBehavior
中,我们为每个操作重写 DataContractSurrogate,使用我们自定义的数据契约代理实现 ConditionalDataContractSurrogate
。
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ServiceEndpoint ep in serviceHostBase.Description.Endpoints) {
foreach (OperationDescription od in ep.Contract.Operations) {
ApplyDataContractSurrogate(od);
}
}
}
private static void ApplyDataContractSurrogate(OperationDescription description)
{
var dcsOperationBehavior = description.Behaviors.Find<datacontractserializeroperationbehavior>();
if (dcsOperationBehavior != null) {
dcsOperationBehavior.DataContractSurrogate = new ConditionalDataContractSurrogate(dcsOperationBehavior.DataContractSurrogate);
}
}
类 ConditionalDataContractSurrogate
实现接口 IDataContractSurrogate
。我们在方法 GetObjectToSerialize
中添加我们自定义的实现。在序列化期间,此方法会在需要序列化的每个对象上调用。这提供了一个在序列化之前检查每个对象的机会。如果调用上下文未被授权(在 IsAuthorized
中检查)接收该对象,那么我们将阻止实际值被序列化。
public object GetObjectToSerialize(object obj, Type targetType)
{
if (obj == null) return null;
var type = obj.GetType();
type.GetProperties().ToList()
.ForEach(prop => {
try {
var attr = prop.GetCustomAttributes(typeof(ConditionalDataMemberAttribute), false);
if (attr.Any()) {
var role = ((ConditionalDataMemberAttribute)attr[0]).Role;
//Is the user authorized
if (!IsAuthorized(role)) {
var proptype = prop.PropertyType;
//Set the property value to its default value
prop.GetSetMethod().Invoke(obj,
new[] { proptype.IsValueType ? Activator.CreateInstance(proptype) : null });
}
}
} catch { }
});
return _baseSerializer != null ? _baseSerializer.GetObjectToSerialize(obj, targetType) : obj;
}
使用代码
步骤 1: 使用 ConditionalDataBehavior
属性装饰你的 WebService。
[ServiceBehavior(Namespace = "http://mywebservice.com/v1")]
[ConditionalDataBehavior]
public class MyWebService
步骤 2: 使用 ConditionalDataMember
属性装饰数据契约。
[DataContract]
public class UserInformation
{
[DataMember]
public string FirstName { get; set; }
[DataMember]
public string LastName { get; set; }
[DataMember]
[ConditionalDataMember(Role = "Level2")]
public string Email { get; set; }
[DataMember]
[ConditionalDataMember(Role = "Level2")]
public string Phone { get; set; }
}
步骤 3: 添加你自己的 IsAuthroized
函数实现。
private bool IsAuthorized(string role)
{
//Implement your own Authorization check
// currentUser.Roles.HasDesiredRole
return true;
}
示例
//Object Returned by Translator
new UserInformation {
FirstName = "Joe",
LastName = "Doe",
Email = "joe@doe.com",
PhoneNumber = "408000000"
};
<!-- Data values received by Level 2 user -->
<userinfo>
<firstname>Joe</firstname>
<lastname>Doe</lastname>
<email>joe@doe.com</email>
<phone>408000000</phone>
</userinfo>
<!-- Data values received by Level 3 user -->
<userinfo>
<firstname>Joe</firstname>
<lastname>Doe</lastname>
<email></email>
<phone></phone>
</userinfo>
参考文献
数据契约代理 - http://msdn.microsoft.com/en-us/library/ms733064.aspx。