OData веб-API - ODataMediaTypeFormatter MediaTypeResolver больше не существует
Веб-API OData v7. Я пишу пользовательский форматтер для CSV, Excel и т. Д. У меня нет связи с тем, как я указываю свой пользовательский форматер (ODataMediaTypeFormatter) на мои пользовательские классы, где я изменяю вывод.
CustomFormatter: ODataMediaTypeFormatter - имел MessageWriterSettings.MediaTypeResolver, которого больше не существует в v. 7
Когда я отлаживаю, я получаю GetPerRequestFormatterInstance, и после этого он умирает с Поддерживаемым типом MIME, который не может быть найден, который соответствует типу содержимого ответа.
Я не могу понять поток - как связать его с моим собственным (ODataWriter) писателем (CSV, или что я хочу создать).
Например, из примера на git:
public class CustomFormatter : ODataMediaTypeFormatter
{
private readonly string csvMime = ;
public CustomFormatter(params ODataPayloadKind[] kinds)
: base(kinds) {
//----no longer exists in 7
//MessageWriterSettings.MediaTypeResolver = new MixResolver();
SupportedEncodings.Add(Encoding.UTF8);
SupportedEncodings.Add(new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/csv"));
}
}
public class MixResolver : ODataMediaTypeResolver
{
public override IEnumerable<ODataMediaTypeFormat> GetMediaTypeFormats(ODataPayloadKind payloadKind)
{
if (payloadKind == ODataPayloadKind.Resource || payloadKind == ODataPayloadKind.ResourceSet)
{
return CsvMediaTypeResolver.Instance.GetMediaTypeFormats(payloadKind);
}
return base.GetMediaTypeFormats(payloadKind);
}
}
public class CsvMediaTypeResolver : ODataMediaTypeResolver
{
private static readonly CsvMediaTypeResolver instance = new CsvMediaTypeResolver();
private readonly ODataMediaTypeFormat[] mediaTypeFormats =
{
new ODataMediaTypeFormat(new ODataMediaType("text", "csv"), new CsvFormat())
};
public class CsvMediaTypeResolver : ODataMediaTypeResolver
{
private static readonly CsvMediaTypeResolver instance = new CsvMediaTypeResolver();
private readonly ODataMediaTypeFormat[] mediaTypeFormats = { new ODataMediaTypeFormat(new ODataMediaType("text", "csv"), new CsvFormat())};
private CsvMediaTypeResolver() { }
public static CsvMediaTypeResolver Instance { get { return instance; } }
public override IEnumerable<ODataMediaTypeFormat> GetMediaTypeFormats(ODataPayloadKind payloadKind)
{
if (payloadKind == ODataPayloadKind.Resource || payloadKind == ODataPayloadKind.ResourceSet)
{
return mediaTypeFormats.Concat(base.GetMediaTypeFormats(payloadKind));
}
return base.GetMediaTypeFormats(payloadKind);
}
}
public class CsvWriter : ODataWriter
{
// Etc..
}
Разъединение происходит с ODataMediaTypeFormatter и CsvMediaTypeResolver. Как связать ODataMediaTypeFormatter с моим распознавателем?
0 ответов
Я решил это CsvOutputContext
а также CsvWriterDemo
объяснено в примерах в Microsoft.OData.Core
Пример кода обновлен
public CsvOutputContext(
ODataFormat format,
ODataMessageWriterSettings settings,
ODataMessageInfo messageInfo,
bool synchronous)
: base(format, settings, messageInfo.IsResponse, synchronous,
messageInfo.Model, messageInfo.UrlResolver)
{
this.stream = messageInfo.GetMessageStream();
this.Writer = new StreamWriter(this.stream);
}
}
private static void CsvWriterDemo()
{
EdmEntityType customer = new EdmEntityType("ns", "customer");
var key = customer.AddStructuralProperty("Id", EdmPrimitiveTypeKind.Int32);
customer.AddKeys(key);
customer.AddStructuralProperty("Name", EdmPrimitiveTypeKind.String);
ODataEntry entry1 = new ODataEntry()
{
Properties = new[]
{
new ODataProperty(){Name = "Id", Value = 51},
new ODataProperty(){Name = "Name", Value = "Name_A"},
}
};
ODataEntry entry2 = new ODataEntry()
{
Properties = new[]
{
new ODataProperty(){Name = "Id", Value = 52},
new ODataProperty(){Name = "Name", Value = "Name_B"},
}
};
var stream = new MemoryStream();
var message = new Message { Stream = stream };
// Set Content-Type header value
message.SetHeader("Content-Type", "text/csv");
var settings = new ODataMessageWriterSettings
{
// Set our resolver here.
MediaTypeResolver = CsvMediaTypeResolver.Instance,
DisableMessageStreamDisposal = true,
};
using (var messageWriter = new ODataMessageWriter(message, settings))
{
var writer = messageWriter.CreateODataFeedWriter(null, customer);
writer.WriteStart(new ODataFeed());
writer.WriteStart(entry1);
writer.WriteEnd();
writer.WriteStart(entry2);
writer.WriteEnd();
writer.WriteEnd();
writer.Flush();
}
stream.Seek(0, SeekOrigin.Begin);
string msg;
using (var sr = new StreamReader(stream)) { msg = sr.ReadToEnd(); }
Console.WriteLine(msg);
}
Как описано в этом документе:
В ODataLib v7.0 добавлена поддержка внедрения зависимостей (или кратко "DI") для упрощения API и реализации ODataLib за счет устранения избыточных параметров функций и свойств классов.
Чтобы обеспечить правильную работу DI с ODataLib, в вашем приложении необходимо сделать несколько вещей:
- Реализуйте свой конструктор контейнеров на основе вашей платформы DI.
- Зарегистрируйте необходимые службы как из ODataLib, так и из вашего приложения.
- Создайте и используйте контейнер (для получения сервисов) в ODataLib.
Microsoft использует IServiceProvider
интерфейс как абстракцию контейнера. В то время как контейнер доступен только для чтения, вам нужно реализоватьIContainerBuilder
интерфейс. Затем введите свой контейнер. После этого зарегистрируйте необходимые сервисы в контейнере. Вы можете использовать методы расширения, определенные вContainerBuilderExtensions
класс для регистрации услуг как легкость.
Вы должны быть осторожны перед использованием этих методов:
За
AddServicePrototype
, в настоящее время мы поддерживаем только следующие типы услуг:ODataMessageReaderSettings
,ODataMessageWriterSettings
а такжеODataSimplifiedOptions
. This design follows the Prototype Pattern where you can register a globally singleton instance (as the prototype) for each service type then you will get an individual clone per scope/request. Modifying that clone will not affect the singleton instance as well as the subsequent clones. That is to say now you don't need to clone a writer setting before editing it with the request-related information just feel safe to modify it for any specific request.The
AddDefaultODataServices
method registers a set of service types with default implementations that come from ODataLib. Typically you MUST call this method first on your container builder before registering any custom service. Please note that the order of registration matters! ODataLib will always use the last service implementation registered for a specific service type.
There is a list of services in the mentioned document that you can override; ODataMediaTypeResolver
is one of them. Consider to the list, before any service registeration.
Now you can build a container by calling BuildContainer
on your builder. That gives you a container instance that implements IServiceProvider
.
In order to use registered services in ODataLib, you must pass the container into ODataLib through some entry point.
Currently entry points in ODataLib are
ODataMessageReader
,ODataMessageWriter
, andODataUriParser
.
1. Serialization and Deserialization:
You could pass container into ODataMessageReader
or ODataMessageWriter
through request and response message. To do so you should create a class that implements IODataRequestMessage
and IODataResponseMessage
, and IContainerProvider
like below:
class ODataMessageWrapper : IODataRequestMessage, IODataResponseMessage, IContainerProvider, ...
{
public IServiceProvider Container { get; set; }
// rest of the implementation here
}
And then you can use the ODataMessageWrapper
class to pass the container into ODataLib as below:
ODataMessageWrapper responseMessage = new ODataMessageWrapper();
responseMessage.Container = Request.GetRequestContainer();
ODataMessageWriter writer = new ODataMessageWriter(responseMessage);
In the above example GetRequestContainer
is an extension of HttpRequestMessage
implemented in https://github.com/OData/WebApi/blob/master/src/Microsoft.AspNet.OData/Extensions/HttpRequestMessageExtensions.cs.
Теперь контейнер хранится в Container
свойства ODataMessageInfo
, ODataInputContext
, а также ODataOutputContext
и их подклассы. Чтобы реализовать настраиваемые типы мультимедиа, вы можете получить доступ к контейнеру через эти свойства.
Если вы не можете установить
Container
вIContainerProvider
, он останется нулевым. В этом случае ODataLib не выйдет из строя внутри, но все службы будут иметь свои реализации по умолчанию, и не будет возможности заменить их пользовательскими. Тем не менее, если вам нужна расширяемость, используйте DI:-)
2. Разбор URI:
Чтобы передать контейнер в парсер URI, вы должны использовать перегрузки конструктора ODataUriParser
. Если вы используете другие конструкторы, поддержка DI в парсерах URI будет отключена. Таким образом контейнер будет сохранен вODataUriParserConfiguratino
и используется в парсере URI.
public sealed class ODataUriParser
{
public ODataUriParser(IEdmModel model, Uri serviceRoot, Uri uri, IServiceProvider container);
public ODataUriParser(IEdmModel model, Uri relativeUri, IServiceProvider container);
}
В настоящее время
ODataUriResolver
,UriPathParser
а такжеODataSimplifiedOptions
может быть переопределен и повлияет на поведение парсеров URI.