我正在使用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或XmlSerializer
期望每个合同在其名称空间中具有唯一名称,但该服务现在返回具有相同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 metadata完整性,这意味着不使用反射进行动态更改。
以声明方式更改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>
元素。
有什么想法吗?
答案 0 :(得分:0)
我为这个获得了风滚草徽章!
我放弃了所描述的方法,因为我找不到可行的解决方案。相反,每个合同都从通用QueryStatus<TErrorCode>
继承可空的BaseContract<TContract, TErrorCode>
属性。此属性将自动填充主合同,null
用于转包。