65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2012年7月9日

CPOL

1分钟阅读

viewsIcon

50082

downloadIcon

579

如何以声明方式添加条件化 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

© . All rights reserved.