ServiceStack Json Serializer忽略属性

时间:2013-07-26 12:40:33

标签: servicestack jsonserializer servicestack-text

我有业务要求只在响应有效负载中发送权限属性。例如,我们的响应DTO可能有几个属性,其中一个是SSN。如果用户没有查看SSN的权限,那么我永远不会希望它在Json响应中。第二个要求是,如果客户端有权查看或更改属性,我们会发送空值。由于第二个要求设置用户无法查看为null的属性将不起作用。我还是要返回空值。

我有一个可行的解决方案。我通过反映我的DTO创建一个expandoObject,并只添加我需要的属性。这在我的测试中有效。

我已经看过实施ITextSerializer了。我可以使用它并将我的响应DTO包装在另一个具有要跳过的属性列表的对象中。然后我可以滚动自己的SerializeToString()和SerializeToStream()。到目前为止,我还没有看到任何其他方式。我无法使用JsConfig并生成SerializeFn,因为要跳过的属性会随着每个请求而改变。

所以我认为实施ITextSerializer是一个不错的选择。有没有很好的例子可以实现?我真的很想使用已经在序列化器中完成的所有艰苦工作并利用其出色的性能。我认为在一个理想的世界中,我只需要在WriteType.WriteProperties()中添加一个检查,并且该属性是一个要编写的属性,但这是内部的,实际上,大多数都是这样,我不能真正采取他们的优势。

如果有人有一些见解,请告诉我!也许我正在使ITextSerialzer的实施变得更加困难?

谢谢!

Pull请求#359将属性“ExcludePropertyReference”添加到JsConfig和JsConfigScope。您现在可以在我需要的范围内排除引用。

1 个答案:

答案 0 :(得分:0)

我会犹豫是否要编写自己的Serializer。我会尝试找到可以插入现有ServiceStack代码的解决方案。这样你就不必再担心更新dll和破坏变化了。

一个可能的解决方案是使用您可以反映的自定义属性来装饰您的属性并隐藏属性值。这可以在序列化之前的服务中完成。这仍然包括他们用户无权查看的值,但我认为如果你将这些属性归零,他们甚至都不会被JSON序列化。 如果您保持所有属性相同,那么您将保留强类型DTO的好处

这是一些hacky代码,我很快想出来证明这一点。我会将它移动到一个插件中,并通过某种属性缓存使反射更快,但我认为你会得到这个想法。

使用以下路线点击网址两次以查看其效果。

  • / test?role
  • / test?role = Admin(hack假装是经过身份验证的请求)

[System.AttributeUsage(System.AttributeTargets.Property)]
public class SecureProperty : System.Attribute
{
    public string Role {get;set;}

    public SecureProperty(string role)
    {
        Role = role;
    }
}

[Route("/test")]
public class Test : IReturn
{
    public string Name { get; set; }

    [SecureProperty("Admin")]
    public string SSN { get; set; }

    public string SSN2 { get; set; }

    public string Role {get;set;}
}

public class TestService : Service
{
    public object Get(Test request)
    {   
        // hack to demo roles.
        var usersCurrentRole = request.Role;

        var props = typeof(Test).GetProperties()
        .Where(
            prop => ((SecureProperty[])prop
                .GetCustomAttributes(typeof(SecureProperty), false))
                .Any(att => att.Role != usersCurrentRole)
        );

        var t = new Test() {
            Name = "Joe", 
            SSN = "123-45-6789", 
            SSN2 = "123-45-6789" };

        foreach(var p in props) {
            p.SetValue(t, "xxx-xx-xxxx", null);
        }

        return t;
    }
}

Require().StartHost("http://localhost:8080/", 
    configurationBuilder: host => { });

我在ScriptCS创建了这个演示。看看吧。