1

我有一个 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 中是否可行?

4

2 回答 2

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
  • 它不太灵活。如果您将来确实需要向请求中添加额外的顶级属性怎么办?您会被它们作为路径/查询参数卡住。拥有一个包含常规类的列表允许您在请求正文的顶层添加新的可选属性,并具有向后兼容性
于 2013-10-04T16:50:36.483 回答
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);
    }
}

_messageBodyPropertyMap在初始化之后通过扫描请求 DTO 并查找某个属性来填充,如我原始问题中的示例所示。

于 2013-10-04T17:52:12.707 回答