Web API OData-ODataMediaTypeFormatter MediaTypeResolver不再存在

时间:2019-01-10 21:33:09

标签: c# asp.net-web-api asp.net-web-api2 odata asp.net-web-api-odata

Web API OData v7。我正在为CSV,Excel等编写自定义格式化程序。我不知道如何将自定义格式化程序(ODataMediaTypeFormatter)指向修改输出的自定义类。

CustomFormatter:ODataMediaTypeFormatter-具有一个MessageWriterSettings.MediaTypeResolver,在第7版中不再存在

调试时,我进入GetPerRequestFormatterInstance,然后它死于A。找不到与响应的内容类型匹配的受支持的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链接到我的解析器?

2 个答案:

答案 0 :(得分:2)

我已经通过Microsoft.OData.Core的示例中说明的CsvOutputContextCsvWriterDemo解决了这个问题

示例代码已更新

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);
}

答案 1 :(得分:0)

this document中所述:

在ODataLib v7.0中,引入了依赖注入(简称“ DI”)支持,以通过消除冗余函数参数和类属性来简化ODataLib的API和实现。

  

要使DI与ODataLib一起正常工作,基本上,您必须在应用程序中做几件事:

     
      
  1. 基于您的DI框架实现您的容器构建器。
  2.   
  3. 从ODataLib和您的应用程序中注册所需的服务。
  4.   
  5. 在ODataLib中构建和使用容器(以检索服务)。
  6.   

Microsoft使用IServiceProvider接口作为容器的抽象。容器是只读的,您必须实现IContainerBuilder接口。然后注入您的容器。之后,将所需的服务注册到容器中。您可以使用ContainerBuilderExtensions类中定义的扩展方法来轻松注册服务。

在使用以下方法之前,您必须谨慎:

  

对于AddServicePrototype,我们目前仅支持以下服务类型:ODataMessageReaderSettingsODataMessageWriterSettingsODataSimplifiedOptions。该设计遵循原型模式,您可以在其中为每种服务类型注册一个全局单例实例(作为原型),然后您将为每个作用域/请求获得一个单独的克隆。修改该克隆不会影响单例实例以及后续克隆。就是说,现在您不需要克隆作家设置,就可以使用与请求相关的信息对其进行编辑,而只需针对任何特定请求进行修改即可。

     

AddDefaultODataServices方法使用ODataLib的默认实现注册一组服务类型。通常,在注册任何自定义服务之前,必须首先在容器构建器上调用此方法。请注意,注册顺序很重要! ODataLib将始终使用针对特定服务类型注册的最新服务实现。

所提到的文档中有一个服务列表,您可以覆盖; ODataMediaTypeResolver是其中之一。 在注册任何服务之前,请考虑该列表。

现在,您可以通过在构建器上调用BuildContainer来构建容器。这样就为您提供了一个实现IServiceProvider的容器实例。

为了使用ODataLib中的注册服务,必须通过某些入口点将容器传递到ODataLib中。

  

当前ODataLib中的入口点是ODataMessageReaderODataMessageWriterODataUriParser

1。序列化和反序列化:

您可以通过请求和响应消息将容器传递到ODataMessageReaderODataMessageWriter中。为此,您应该创建一个实现IODataRequestMessageIODataResponseMessage以及IContainerProvider的类,如下所示:

class ODataMessageWrapper : IODataRequestMessage, IODataResponseMessage, IContainerProvider, ...
{
    public IServiceProvider Container { get; set; }

    // rest of the implementation here
}

然后您可以使用ODataMessageWrapper类将容器传递到ODataLib中,如下所示:

ODataMessageWrapper responseMessage = new ODataMessageWrapper();
responseMessage.Container = Request.GetRequestContainer();
ODataMessageWriter writer = new ODataMessageWriter(responseMessage);

在上面的示例中,GetRequestContainer是在Microsoft.AspNet.OData.HttpRequestMessageExtensions.cs中实现的HttpRequestMessage的扩展。

现在,容器存储在ContainerODataMessageInfoODataInputContext的{​​{1}}属性及其子类中。为了实现自定义媒体类型,您可以通过这些属性访问容器。

  

如果您未能在ODataOutputContext中设置Container,它将保持为空。在这种情况下,ODataLib不会在内部失败,但是所有服务都将具有其默认实现,并且无法用自定义服务替换它们。也就是说,如果您要扩展性,请使用DI:-)

2。 URI解析:

要将容器传递到URI解析器,应使用IContainerProvider的构造函数重载。如果使用其他构造函数,则将禁用URI解析器中的DI支持。这样,容器将保存在ODataUriParser中并在URI解析器中使用。

ODataUriParserConfiguratino
  

当前public sealed class ODataUriParser { public ODataUriParser(IEdmModel model, Uri serviceRoot, Uri uri, IServiceProvider container); public ODataUriParser(IEdmModel model, Uri relativeUri, IServiceProvider container); } ODataUriResolverUriPathParser可以被覆盖,并且会影响URI解析器的行为。