фон
Я разрабатываю REST API для веб-приложения на C # .NET с использованием WCF. Я настроил его для использования 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 , что означает отсутствие динамических изменений при использовании отражения.
Попытки решения
Декларативное изменение атрибутов 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>
.
Заключение
Есть идеи?