ServiceStack:我可以“扁平化”邮政体的结构吗?

时间:2013-10-04 05:01:12

标签: c# rest servicestack

我有一个POST端点,它接受一个URL路径参数,然后正文是一个提交的DTO列表。

所以现在请求DTO看起来像是:

[Route("/prefix/{Param1}", "POST")]
public class SomeRequest
{
    public string          Param1  { get; set; }
    public List<SomeEntry> Entries { get; set; }
}

public class SomeEntry
{
    public int    ID    { get; set; }
    public int    Type  { get; set; }
    public string Value { get; set; }
}

服务方法类似于:

public class SomeService : Service
{
    public SomeResponse Post(SomeRequest request)
    {
    }
}

如果通过JSON编码,客户端必须以这种方式对POST主体进行编码:

{
    "Entries":
    [
        {
            "id":    1
            "type":  42
            "value": "Y"
        },
        ...
    ]
}

这是多余的,我希望客户提交这样的数据:

[
    {
        "id":    1
        "type":  42
        "value": "Y"
    },
    ...
]

如果我的请求DTO只是List<SomeEntry>

,那就是这种情况

我的问题是:有没有办法以这种方式“压扁”请求?或者将请求的一个属性指定为消息正文的根?也许是:

[Route("/prefix/{Param1}", "POST")]
public class SomeRequest
{
    public string          Param1  { get; set; }
    [MessageBody]
    public List<SomeEntry> Entries { get; set; }
}

这在ServiceStack中是否可行?

2 个答案:

答案 0 :(得分:1)

我能够通过继承List<T>

来解决这个问题
[Route("/prefix/{Param1}", "POST")]
public class SomeRequest : List<SomeEntry>
{
    public string          Param1  { get; set; }
}

然后你可以发送这样的请求:

POST /prefix/someParameterValue
Content-Type: application/json
[ { "ID": 1, "Type": 2, "Value": "X" }, ... ]

但如果您对设计有任何选择,我不会推荐这个。以下是几个理由:

  • 我在运行时发现了至少一个问题:发送一个空数组,例如: JSON中的[ ]导致400状态代码为RequestBindingException
  • 灵活性较差。如果您确实需要在将来为请求添加其他顶级属性,该怎么办?您将被困在路径/查询参数中。拥有一个包含常规类的列表允许您在请求体的顶层添加新的可选属性,并具有向后兼容性

答案 1 :(得分:0)

好的,我已经成功实现了这一目标。不是最漂亮的解决方案,但现在会做。

我为JSON包装了内容类型过滤器:

var serz   = ContentTypeFilters.GetResponseSerializer("application/json");
var deserz = ContentTypeFilters.GetStreamDeserializer("application/json");
ContentTypeFilters.Register("application/json", serz, (type, stream) => MessageBodyPropertyFilter(type, stream, deserz));

然后自定义反序列化器如下所示:

private object MessageBodyPropertyFilter(Type type, Stream stream, StreamDeserializerDelegate original)
{
    PropertyInfo prop;
    if (_messageBodyPropertyMap.TryGetValue(type, out prop))
    {
        var requestDto = type.CreateInstance();
        prop.SetValue(requestDto, original(prop.PropertyType, stream), null);
        return requestDto;
    }
    else
    {
        return original(type, stream);
    }
}
在初始化之后,通过扫描请求DTO并查找某个属性来填充

_messageBodyPropertyMap,如我原始问题中的示例所示。