带有OData v4的Web Api 2 - 返回复杂对象的绑定函数

时间:2016-09-25 17:18:11

标签: json asp.net-web-api2 odata asp.net-web-api-odata odata-v4

在这个简单的例子中,我试图从Web Api 2 + OData v4服务获取一个序列化为JSON的对象。控制器具有绑定功能Test,它返回一个annon数组。对象。

public class ProductsController : ODataController
{
    [HttpGet]
    public IHttpActionResult Test(int key)
    {
        var res = new[]
        {
            new { Name = "a", Value = new[] { 1, 2, 3 } },
            new { Name = "b", Value = new[] { 2, 4, 5 } }

            // this also produces same result
            // new { Name = "a", Value = "c" },
            // new { Name = "b", Value = "c" }
        };

        return this.Ok(res);
    }
}

Edm是用这段代码构建的:

ODataModelBuilder builder = new ODataConventionModelBuilder();

builder.EntitySet<Product>("Products");
var productType = builder.EntityType<Product>();

var f = productType.Function("Test").Returns<object>();

当我向服务发出请求时(例如http://localhost:9010/odata/Products(33)/Default.Test)我得到一个奇怪的响应 - 一个包含两个空对象的数组,如下所示:

{
  "@odata.context": "http://localhost:9010/odata/$metadata#Collection(System.Object)",
  "value": [
    {},
    {}
  ]
}

在我的真实应用程序中,我使用Newtonsoft的Json转换器将对象序列化为JSON字符串 - 这很好,但是这个问题仍然困扰着我。我怀疑它与OData的默认序列化程序有关,但我不清楚如何配置它。

那么,是否有可能以这种方式配置edm函数的返回参数,以便我能正确地序列化复杂对象?

谢谢!

2 个答案:

答案 0 :(得分:5)

正如lukkea所说,OData不适用于匿名类型 旁注,在WebApiConfig中,如果要返回集合,则应将“返回”更改为“ReturnsCollection”。

无论如何,我们假设你写了以下内容。

return this.Ok(Newtonsoft.Json.JsonConvert.SerializeObject(res));

var f = productType.Function("Test").Returns<string>();

你会得到以下信息:

{
    "@odata.context": "http://localhost/Test/odata/$metadata#Edm.String",
    "value": 
        "[
            {\"Name\":\"a\",\"Value\":[1,2,3]},
            {\"Name\":\"b\",\"Value\":[2,4,5]}
        ]"
}

请注意,数组中仍有2个项目,但这次它们不是空的 由于OData在前一个示例中不知道返回类型,因此它返回没有值的2个对象。

您有2个选项。

  1. 将匿名类型作为序列化的json字符串返回,然后在客户端反序列化该json。
  2. 创建一个类并返回该类型。
  3. 选项1

    // ON SERVER
    return this.Ok(Newtonsoft.Json.JsonConvert.SerializeObject(res));
    
    var f = productType.Function("Test").Returns<string>();
    
    // ON CLIENT
    string jsonString = odataContext.Products.ByKey(33).Test().GetValue();  
    var objectList = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(jsonString);  
    
    string firstObjectName = objectList[0].Name;
    

    选项2

    // ON SERVER  
    public class TestObject
    {
        public string Name { get; set; }
        public List<int> Integers { get; set; }
    }
    
    var res = new List<TestObject>
    {
         new TestObject { Name = "a", Integers = new List<int> { 1, 2, 3 } },
         new TestObject { Name = "b", Integers = new List<int> { 2, 4, 5 } }
    };
    
    return this.Ok(res);  
    
    var f = productType.Function("Test").ReturnsCollection<TestObject>();
    

    如果您想要返回一个具有非强类型的额外属性的人,那么您需要ODataOpenType

答案 1 :(得分:0)

使用动态响应确实很棘手,但这并不难,而且您当然不必求助于通过字符串编码返回对象。

关键是 dynamic 响应意味着我们不能使用标准的EnableQueryAttribute在方法响应上应用特定的投影或过滤,并且我们不能返回{{ 1}},因为该响应对象旨在使运行时能够操纵如何将响应对象序列化为HTTP响应。

ApiController.Ok(T内容);
创建具有指定值的System.Web.Http.Results.OkNegotiatedContentResult。
内容 :要在实体主体中协商和格式化的内容值。

Content Negotiation
内容协商本质上是一种机制,用于封装确定如何您的方法响应如何通过http传输以及繁重的工作以物理方式编码响应的过程。

通过使用内容协商,即使在请求中指定输出为XML(而不是标准JSON)的调用方,您的方法也只需要返回查询或原始c#对象。处理物理序列化的概念和解释调用者意图的逻辑被抽象出来,因此您完全不必担心它。

您可以使用2个选项来自定义输出:

  1. ApiController.JsonResult(T content);
    这样,您可以指定要序列化的对象图,而不会响应OkNegotiatedContentResult内容协商

    EnableQueryAttribute
    • 此响应不在通常的 OData响应包装器中,因此它不包括标准的 return this.JsonResult(res); 属性,例如@odata
    • 如果您的响应对象与OData模型中的“操作/功能”定义中指定的类型不匹配,则对该端点的调用将导致。但是,如果响应对象与OData模型中指定的类型不匹配,则将导致在 HTTP 406 不可接受 中,因此请确保将响应注册为响应对象继承或实现的@odata.context或其他类型的接口。< / li>
  2. HttpResponseMessage
    一起绕过OData响应管理,并直接从您的方法中返回object。这样,您负责序列化响应内容以及响应头。

    但是,这绕过了所有OData机制,包括响应验证和格式化,这意味着您可以返回任何所需内容。

    HttpResponseMessage
相关问题