【问题标题】:Post XML to asp.net core 2.0 web api将 XML 发布到 asp.net core 2.0 web api
【发布时间】:2018-02-28 08:56:33
【问题描述】:

我正在尝试将 XML 发布到 asp.net core 2:

$.ajax({
    type: "POST",
    url: 'api/Test',
    data: "<test>hello<test>",
    contentType: "application/xml",
    success: function (response) { alert(response); },
});

我应该如何编写动作,让它接受 xml 作为参数?

  • IActionResult Post([FromBody]string xml) -> xml 为空
  • IActionResult Post([FromBody]XElement xml) -> xml 为空
  • IActionResult Post(XElement xml) -> InvalidOperationException:无法创建“System.Xml.Linq.XElement”类型的实例。模型绑定的复杂类型不能是抽象类型或值类型,并且必须具有无参数构造函数。
  • IActionResult Post(string xml) -> xml 为空

在 Startup.ConfigureServices 中:

services.AddMvc()
    .AddXmlSerializerFormatters();

如何编写控制器以使其接受 XML 作为参数?我知道我可以从 HttpContext.Request 中读取它,但我希望它是参数

【问题讨论】:

    标签: ajax xml asp.net-core asp.net-core-webapi asp.net-core-2.0


    【解决方案1】:

    这些解决方案有效,但在 .NET Core 3 中有一个缺点 - 它们会导致异常(在调用 XDocument.LoadAsync 内部):

    System.InvalidOperationException:不允许同步操作。改为调用 ReadAsync 或将 AllowSynchronousIO 设置为 true。

    这是我使用 FileBufferingReadStream 修改后的解决方案(灵感来自 Microsoft.AspNetCore.Mvc.Formatters.XmlSerializerInputFormatter 的代码)

    public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
    {
        Check.NotNull(context, nameof(context));
    
        var xmlDoc = await LoadXmlFromRequestAsync(context.HttpContext);
        return InputFormatterResult.Success(xmlDoc);
    }
    
    private static async Task<XDocument> LoadXmlFromRequestAsync(HttpContext httpContext)
    {
        Check.NotNull(httpContext, nameof(httpContext));
    
        //Code from Microsoft.AspNetCore.Mvc.Formatters.XmlSerializerInputFormatter to use FileBufferingReadStream to avoid synchronous read issue:
        //https://github.com/dotnet/aspnetcore/issues/18723 - Synchronous call inside XDocument.LoadAsync causes --> System.InvalidOperationException: Synchronous operations are disallowed. Call ReadAsync or set AllowSynchronousIO to true instead.
        int memoryThreshold = 30720;
        long contentLength = httpContext.Request.ContentLength.GetValueOrDefault();
        if (contentLength > 0 && contentLength < memoryThreshold)
        {
            memoryThreshold = (int)contentLength;
        }
    
        var readStream = new FileBufferingReadStream(httpContext.Request.Body, memoryThreshold);
        httpContext.Response.RegisterForDispose(readStream);
        await readStream.DrainAsync(CancellationToken.None);
        readStream.Seek(0, SeekOrigin.Begin);
    
        try
        {
            using (var streamReader = new StreamReader(readStream))
            {
                var xmlDoc = await XDocument.LoadAsync(streamReader, LoadOptions.None, CancellationToken.None);
                return xmlDoc;
            }
        }
        finally
        {
            await readStream.DisposeAsync();
        }
    }
    

    【讨论】:

      【解决方案2】:

      只是对Liero给出的答案进行了更改,您应该使用StreamReader,这样您就可以支持多种编码。使用 UTF-8、UTF-16 和 ASCI 声明标头测试了我的解决方案。

      从 XDocumentInputFormatter 更改方法:

      public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
      {
          var xmlDoc = await XDocument.LoadAsync(context.HttpContext.Request.Body, LoadOptions.None, CancellationToken.None);
          return InputFormatterResult.Success(xmlDoc);
      }
      

      到下面

      public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context) {
              // Use StreamReader to convert any encoding to UTF-16 (default C# and sql Server).
              using (var streamReader = new StreamReader(context.HttpContext.Request.Body)) {
                  var xmlDoc = await XDocument.LoadAsync(streamReader, LoadOptions.None, CancellationToken.None);
                  return InputFormatterResult.Success(xmlDoc);
              }
          }
      

      【讨论】:

        【解决方案3】:

        我最终创建了自定义 InputFormatter,这非常简单,但如果有更好的选择,非常欢迎您写一个答案!

        public class XDocumentInputFormatter : InputFormatter, IInputFormatter, IApiRequestFormatMetadataProvider
        {
            public XDocumentInputFormatter()
            {
                SupportedMediaTypes.Add("application/xml");
            }
        
            protected override bool CanReadType(Type type)
            {
                if (type.IsAssignableFrom(typeof(XDocument))) return true;
                return base.CanReadType(type);
            }
        
            public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
            {
                var xmlDoc = await XDocument.LoadAsync(context.HttpContext.Request.Body, LoadOptions.None, CancellationToken.None);
                return InputFormatterResult.Success(xmlDoc);
            }
        }
        

        在startup.cs中注册XDocumentInputFormatter

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc(options => options.InputFormatters.Insert(0, new XDocumentInputFormatter()));
        }
        

        【讨论】:

        • services.AddMvc(options => { // 允许输入 xml 格式 options.InputFormatters.Add(new XmlSerializerInputFormatter(options)); }) ...
        猜你喜欢
        • 2020-12-04
        • 2017-01-16
        • 2018-10-26
        • 2019-12-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-09-24
        • 2020-12-03
        相关资源
        最近更新 更多