使用Request.CreateResponse序列化多个返回类型

时间:2014-02-21 09:12:05

标签: serialization asp.net-web-api

我有一个API的动作方法,它返回一个HttpResponseMessage,但是根据Accept标头,它可以返回许多不同格式的数据。

这就是我目前所做的,它确实有效,但它不是很理想,因为我必须记住在MappedItem方法中包含任何新类,并且会有很多。

    [HttpGet]
    public HttpResponseMessage Get(int id)
    {
        var result = _builder.Build(id);

        return MappedItem(result);
    }

    protected HttpResponseMessage MappedItem<T>(T item)
    {
        // Maps the class to the media type defined in the Accept header
        var destinationType = GetDestinationType();
        var type = typeof(T);
        var mapped = Mapper.Map(item, type, destinationType);

        if (mapped is ApiModelV1) {
            return Request.CreateResponse(HttpStatusCode.OK, mapped as ApiModelV1);
        }

        return Request.CreateResponse(HttpStatusCode.OK, mapped);
    }

如果我只是序列化为JSON,它在没有if (mapped is ApiModelV1)部分的情况下工作正常,但如果我序列化为XML则抛出异常。有没有人知道以更通用的方式做到这一点的方法?

2 个答案:

答案 0 :(得分:1)

一种可能性是使用KnownType属性装饰您的基类,并列出所有可能的派生类,以向XML序列化程序指示这些类型的存在:

[KnownType(typeof(ApiModelV1))]
[KnownType(typeof(ApiModelV2))]
public class BaseClass
{
    ...
}

或者,如果您不想使用此类属性污染模型,则可以使用自定义XML序列化程序并指明已知类型:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );

        var knownTypes = new Type[]
        {
            typeof(ApiModelV1),
            typeof(ApiModelV2),
        };

        config.Formatters.XmlFormatter.SetSerializer<BaseClass>(
            new DataContractSerializer(typeof(BaseClass), knownTypes)
        );
    }
}

答案 1 :(得分:0)

好的,我找到了一个解决方案,虽然我不得不求助于使用反射,因为我无法使用已知类型正常工作。

protected HttpResponseMessage MappedItem<T>(T item)
{
    var destinationType = GetDestinationType();
    var type = typeof(T);
    var mapped = Mapper.Map(item, type, destinationType);

    MethodInfo method = this.GetType().GetMethod("CreateResponse", BindingFlags.Public | BindingFlags.Instance);
    method = method.MakeGenericMethod(mapped.GetType());

    return (HttpResponseMessage)method.Invoke(this, new[] {mapped});
}

public HttpResponseMessage CreateResponse<T>(T obj)
{
    return Request.CreateResponse(HttpStatusCode.OK, obj);
}

它并不理想,但它比我想要序列化的每种类型都有很多if (mapped is ...)要好得多。