【问题标题】:Web API Custom method with [FromBody] parameter, called via DataServiceContext.Execute带有 [FromBody] 参数的 Web API 自定义方法,通过 DataServiceContext.Execute 调用
【发布时间】:2015-04-15 09:02:15
【问题描述】:

在服务端,我有一个非常简单的配置,通过 URL 传递参数时效果很好:

(sn-p)

builder.EntitySet<MappedAuthorizationGroup>("MappedAuthorizationGroups");
var function = builder.Function("TestMethod");
//parameter removed here, because I want to POST it ([FromBody])
//function.Parameter<string>("id"); 
function.ReturnsCollectionFromEntitySet<MappedAuthorizationGroup>("MappedAuthorizationGroups");

控制器方法的修饰还是相当简单的:

[HttpPost]
[ODataRoute("TestMethod")]  //(id={id})")]  // Again, parameter removed because i want to POST it
public IQueryable<MappedAuthorizationGroup> TestMethod([FromBody]string id)
{
    ... code ...
}

现在,这里有几个关于如何通过 AJAX 调用它的答案,但我已经在使用 microsoft odata 客户端 (DataServiceContext) 进行所有“正常”Odata 调用(CRUD 操作)......所以我想也可以将它用于这些自定义调用(我假设使用 .Execute()?)

我非常简单的客户端代码:

var client = new Default.Container(new Uri("thecorrectURI"));

var methodURI = new Uri(client.BaseUri, "TestMethod");   

var recs = client.Execute<MappedAuthorizationGroup>(methodURI, "POST", new BodyOperationParameter("id", "C26DFAF6-8F32-429B-9DB3-2F8CF0ABBD3A"));

但是,当它到达服务器时,这不会正确填充应该是 [FromBody] 的字符串“id”参数。经过调查,这似乎是因为 1 或 2 个原因:

  1. 内容类型错误。但是,如果我将内容类型设置为“application/x-www-form-urlencoded; charset=utf-8”,我会在服务器上得到一个不同的错误,因为它需要 json,我认为这应该只适用于单个原始参数。

  2. BodyOperationParameter() 序列化为 JSON,但由于某种原因,这在服务器端不起作用。许多帖子提到它只与简单的“=myvalue”传递兼容。这个对吗?如何从 DataServiceContext 对象执行此操作?我是否必须从更通用的对象(如 HttpClient 对象)进行此调用,在该对象中设置正确的标头和正文内容非常容易?

我想将它移动到正文而不是简单 URL 的原因是,这个参数实际上是一个序列化的 GUID 列表,很可能比在 URL 中设置的要多。

【问题讨论】:

    标签: .net client odata asp.net-web-api


    【解决方案1】:

    好的,有很多事情可以解决这个问题。每个都记录在网络上的“某处”,但它们都没有真正结合在一个我可以在这种情况下使用的单一解决方案中(这似乎应该非常普遍......嗯)。无论如何:

    odata 控制器端的方法修饰是正确的,如:

    [HttpPost]
    [ODataRoute("TestMethod")]
    public IQueryable<YourEntity> TestMethod([FromBody]string id)
    

    (我将参数名称保留为“id”以消除创建另一个路由的需要)

    在客户端/消费者方面,我必须对我的原始代码进行相当多的更改。首先,我不能使用 odata 客户端上下文的东西,因为它不能让你对参数序列化有足够的控制,这是一个问题。此外,它似乎只想发送 JSON,并将内容类型设置为其他内容(对于表单帖子),导致另一个错误。可能是因为我们的 OData 服务通常使用 JSON 来处理所有标准的 CRUD 调用,并且似乎没有一种简洁的方式可以说“只期望某些自定义调用的非 JSON 数据”)。同时,将body内容参数序列化为JSON也不行。

    因此,使用 HttpClient 可以更好地控制请求。但是请求正文内容的格式非常特殊。我不得不将它作为“=stringvalue”发送,UTF-8 编码。

    取回数据也是一个问题,因为 IEnumerable 似乎返回包裹在一个“值”对象中......因此反序列化对 List 的响应 JSON 将失败。用一个名为“value”的 List 属性创建一个类,并反序列化到这个对象,终于成功了。

    最终实现这个工作的客户端代码(对我来说)看起来有点像:

    using (var client = new HttpClient())
    {
        client.BaseAddress = new Uri("http://localhost/mywebsite/odata");
    
        // Content string *MUST* start with =
        var content = new StringContent("=C26DFAF6-8F32-429B-9DB3-2F8CF0ABBD3A", Encoding.UTF8, "application/x-www-form-urlencoded");
    
        HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Post, "http://localhost/mywebsite/odata/TestMethod");
        message.Content = content;
    
        // Send the request
        HttpResponseMessage response = client.SendAsync(message).Result;
    
        // Get the result
        string retval = response.Content.ReadAsStringAsync().Result;
    
        // Deserialize the response
        var jsonSerializer = new JavaScriptSerializer();
    
        var groups = jsonSerializer.Deserialize<TestContainer>(retval);
    }
    

    还有用于帮助反序列化的虚拟“容器”:

    public class TestContainer
    {
        public List<YourModel> value    { get; set; }
    }
    

    【讨论】:

      猜你喜欢
      • 2019-06-09
      • 1970-01-01
      • 2015-11-11
      • 2016-11-16
      • 1970-01-01
      • 1970-01-01
      • 2015-08-07
      • 2015-05-18
      • 2016-05-26
      相关资源
      最近更新 更多