的背景
我正在使用 WCF 为 C#.NET Web 应用程序开发 REST API。我将其配置为使用 XmlSerializer 而不是其默认的 DataContractSerializer,以便更好地控制 XML 格式。我创建了一个通用数据协定,它使用请求状态、错误消息和命名空间等通用数据包装ResponseContract<TResponse, TErrorCode>
响应。一个示例方法:<Api>
<Response>
ResponseContract<ItemListContract, ItemListErrorCode> GetItemList(...)
上述方法的示例响应:
<?xml version="1.0" encoding="utf-8"?>
<Api xmlns="http://example.com/api/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Response Status="OKAY" ErrorCode="OKAY" ErrorText="">
<Data Template="ItemList">
<Pages Template="Pagination" Size="10" Index="1" Count="13" Items="126" />
<Items>
<Item example="..." />
<Item example="..." />
<Item example="..." />
</Items>
</Data>
</Response>
</Api>
问题
ResponseContract
这对于方法都具有相同泛型类型的服务非常有效。WCF orXmlSerializer
期望每个合约在其命名空间中具有唯一名称,但服务现在返回具有相同 XML 根名称的不同类型的通用合约:
ResponseContract<ItemListContract, ItemListErrorCode> GetItemList(...)
ResponseContract<ItemContract, ItemErrorCode> GetItem(...)
结果异常:
The top XML element 'Api' from namespace 'http://example.com/api/' references distinct types Company.Product.ApiServer.Contracts.ResponseContract`2[Company.Product.ApiServer.Contracts.Items.ItemListContract,Company.Product.ApiServer.Interfaces.Items.ItemListErrorCode] and Company.Product.ApiServer.Contracts.ResponseContract`2[Company.Product.ApiServer.Contracts.Items.ItemContract,Company.Product.ApiServer.Items.ItemErrorCode]. Use XML attributes to specify another XML name or namespace for the element or types.
服务必须允许不同的返回类型。这很难实现,因为ResponseContract<TResponse, TErrorCode>
(设置名称和命名空间)是通用的,并且由所有 API 方法返回。我还需要维护WSDL 元数据的完整性,这意味着不会使用反射进行动态更改。
尝试的解决方案
无法以声明方式更改 XML 属性,因为
<Api>
根元素及其属性是完全通用的(在 中ResponseContract
)。在运行时使用反射更改属性名称空间(例如,'http://example.com/api/Items/GetItemList')没有效果。可以获取属性,但对它们的更改无效。这无论如何都会破坏 WSDL。
在实现 IXmlSerializable 时,写入器在被调用
<Api>
时已经定位在开始标记之后。WriteXml()
只能覆盖 的<Api>
子节点的序列化,这无论如何都不会造成问题。这无论如何都行不通,因为在IXmlSerializable
调用方法之前引发了异常。将常量命名空间与
typeof()
或类似的连接以使其唯一是行不通的,因为命名空间必须是一个常量。默认
DataContractSerializer
可以在名称中插入类型名称(如<ApiOfIdeaList>
),但DataContractSerializer
的输出臃肿且不可读且缺少属性,这对于外部重用者来说是不可行的。扩展
XmlRootAttribute
以生成不同的命名空间。不幸的是,在调用它时没有可用的类型信息,只有通用ResponseContract
数据。可以生成一个随机名称空间来规避该问题,但动态更改模式会破坏 WSDL 元数据。制作
ResponseContract
一个基类而不是一个包装契约应该可以工作,但会导致大量重复的通用数据。例如,<Pages>
在<Item>
上面的例子中也是合约,它们有自己的等价物<Api>
和<Response>
元素。
结论
有任何想法吗?