ServiceStack: Can I "Flatten" the structure of the post body?

I have a POST endpoint that takes a URL path param and then the body is a list of submitted DTOs.

So right now the request DTO looks something along the lines of:

[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; }
}

And the service method looks something like:

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

If encoded via JSON, the client would have to encode the POST body this way:

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

This is redundant, I would like the client to submit the data like this:

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

Which would have been the case if my request DTO was simply List<SomeEntry>

My questions is: is there a way to "flatten" the request this way? Or designate one property of the request as the root of the message body? ie perhaps:

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

Is this doable in any way in ServiceStack?


I was able to sort of get this to sort of work by subclassing List<T> :

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

Then you can send a request like this:

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

But if you have any choice in the design, I wouldn't recommend this. Here's a couple of reasons to start with:

  • I found at least one problem with this at runtime: sending an empty array, eg [ ] in JSON, is resulting in a 400 status code with RequestBindingException
  • It's less flexible. What if you do need to add additional top-level properties to the request in the future? You would be stuck with them being path/query params. Having a regular class-containing-a-list allows you to add new optional properties at the top level of the request body, with backward compatibility

  • OK I've managed to achieve this. Not the prettiest solution but will do for now.

    I wrapped the content type filter for JSON:

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

    Then the custom deserializer looks like this:

    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 is populated after init by scanning the request DTOs and looking for a certain attribute, as in the example in my original question.

    链接地址: http://www.djcxy.com/p/68708.html

    上一篇: 带动态或DynamicTableEntity对象的ServiceStack发布请求

    下一篇: ServiceStack:我可以“展开”帖子主体的结构吗?