3

我有一个实现 REST API 的 ASP.NET MVC4 网站,我从客户端应用程序中使用它。我的 ApiController 方法以 XML 形式获取和返回复杂对象。

我最近发现了 RestSharp,并开始将我的客户项目转移到那里。但是,我遇到了真正的问题。它似乎几乎起作用了——它是如此接近以至于我几乎可以尝到成功的滋味——但我就是不能让它 100% 起作用。

我穿过电线的物体看起来像这样:

// The object I'm passing across the wire
public class Example
{
    bool IsActive { get; set; }
    string Name { get; set; }
}

我的 ApiController 方法如下所示:

// My ApiController method
public HttpResponseMessage PostExample(Example example)
{
    if (ModelState.IsValid)
    {
        db.Examples.Add(example);

        db.SaveChanges();

        HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.Created, example);

        return response;
    }
    else
    {
        return Request.CreateResponse(HttpStatusCode.BadRequest);
    }
}

当我尝试将对象发布到我的网站时会出现问题,如下所示:

var example = new Example () { IsActive = true, Name = "foo" };

var request = new RestSharp.RestRequest("/api/example", RestSharp.Method.POST);

request.AddBody(example, XmlNamespace);

var client = new RestClient();

client.BaseUrl = "foo.com";

var response = client.Execute<Example>(request);

上面的代码确实命中了我的 ApiController 中的 PostExample 方法,并且它有一个 Example 对象作为参数。但是Example 对象的属性值与我传递给 Execute 方法的值不同!在一种情况下,IsActive 成员是 false 而不是 true,尽管我也看到了 Name 成员为 null 的情况,它应该有一个值。

我使用 Fiddler 进行了一些调查,似乎在 RestSharp 生成的 XML 中创建了正确的值。但是,XML 与 Web 服务器在执行 GET 时发出的格式并不完全相同。差异是微妙的,但似乎在它工作和不工作之间有所不同。Web 服务器端的框架似乎对这些格式差异很敏感,因此错误地解释了 XML。

这是我从 RestSharp 获得的 XML:

<Example xmlns="http://schemas.datacontract.org/2004/07/ExampleNamespace">
  <Name>foo</Name>
  <IsActive>true</IsActive>
</Example>

这是我在网络服务器上执行 GET 时得到的(或使用 DataContractSerializer 进行序列化时,这是我之前所做的):

<Example xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/ExampleNamespace">
  <IsActive>true</IsActive>
  <Name>foo</Name>
</TagDto>

RestSharp 版本与 DataContractSerializer 的版本有以下区别:

  1. 字段的顺序不同
  2. RestSharp 不包含额外的命名空间 XMLSchema-instance 命名空间
  3. DataContractSerializer 不包含任何空格或换行符(为了便于阅读,我在上面添加了这些)

我很惊讶其中任何一个都会产生很大的不同,但显然它们确实如此。另请注意,在我在 AddBody 调用中添加显式命名空间之前,生成的 XML 中缺少该命名空间(显然),并且传递给我的 ApiController 的 Example 对象是null.

无论如何,我注意到 RestSharp 允许您覆盖序列化程序,并提供了一种使用 .NET XML 序列化程序的方法。我尝试使用它(无济于事)。

这是我在调用 AddBody 之前添加的内容:

request.XmlSerializer = new RestSharp.Serializers.DotNetXmlSerializer(XmlNamespace);

..这就是我得到的:

<?xml version="1.0" encoding="utf-8"?>
<Example>
  <Name>foo</Name>
  <IsActive>true</IsActive>
</Example>

这显然不好,尤其是因为它以 XML 声明开头,我想这会导致问题。没有办法关闭它,因为 RestSharp 派生类没有提供这样做的方法。此外,没有命名空间 - 无论我如何尝试在 RestSharp 中设置命名空间(在 DotNetXmlSerializer 的构造函数中,通过设置命名空间成员或通过将命名空间传递给添加正文)。在我看来,这门课只不过是个陷阱。

看起来我唯一的选择是创建自己的序列化程序类并在内部使用 DataContractSerializer。是这样吗,还是我错过了什么?

(顺便说一句,我可以将请求的 RequestFormat 设置为 JSON 并且它可以正常工作 - 但我仍然想知道如何使用 XML 来实现它)。

4

2 回答 2

2

我遇到了一些关于 AddBody 调用未正确序列化 JSON 值的问题,因此可能与您的问题有一些相似之处。而不是 AddBody,您可以尝试:

request.AddParameter("text/xml", xmlAsString, ParameterType.RequestBody);

如果可行,您可以查看将第二个参数更改为 xml 对象并查看序列化程序是否执行您想要的操作。

另一种选择可能XmlMediaTypeFormatter.ReadFromStreamAsync是没有正确选择正确的序列化程序;您可以尝试覆盖该功能。

于 2012-08-14T09:58:12.047 回答
1

上面的问题是因为 WebAPI 使用的是 DataContractSerializer (而不是你所追求的 XmlSerializer)。要切换它,请按如下方式修改 Global.asax。

 var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
 xml.UseXmlSerializer = true;

但是,我建议您对 WebAPI 使用 RESTSharp 格式化程序(而不是使用 .Net 格式化程序)。如果您是 DTO 有循环引用,这将特别有用(.net fx 序列化程序不能很好地处理这个问题)。

在 Global.asax 中,通过输入修改格式化程序

GlobalConfiguration.Configuration.Formatters.XmlFormatter = //RestSharp XML serializer here

WebAPI 中序列化的快速概述在这里,值得一读

于 2012-08-15T13:59:58.060 回答