我有一个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中是否可行?
答案 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" }, ... ]
但如果您对设计有任何选择,我不会推荐这个。以下是几个理由:
[ ]
导致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
,如我原始问题中的示例所示。