简体   繁体   English

将 XML 发布到 asp.net core 2.0 web api

[英]Post XML to asp.net core 2.0 web api

I'm trying to post XML to asp.net core 2:我正在尝试将 XML 发布到 asp.net core 2:

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

How should I write the action, so it accepts the xml as parameter?我应该如何编写动作,以便它接受 xml 作为参数?

  • IActionResult Post([FromBody]string xml) -> xml is null IActionResult Post([FromBody]string xml) -> xml 为空
  • IActionResult Post([FromBody]XElement xml) -> xml is null IActionResult Post([FromBody]XElement xml) -> xml 为空
  • IActionResult Post(XElement xml) -> InvalidOperationException: Could not create an instance of type 'System.Xml.Linq.XElement'. IActionResult Post(XElement xml) -> InvalidOperationException: 无法创建类型为“System.Xml.Linq.XElement”的实例。 Model bound complex types must not be abstract or value types and must have a parameterless constructor.模型绑定复杂类型不能是抽象类型或值类型,并且必须具有无参数构造函数。
  • IActionResult Post(string xml) -> xml is null IActionResult Post(string xml) -> xml 为空

in the Startup.ConfigureServices:在 Startup.ConfigureServices 中:

services.AddMvc()
    .AddXmlSerializerFormatters();

How to write the controller so that it accepts XML as a parameter?如何编写控制器使其接受 XML 作为参数? I know I can read it from HttpContext.Request, but I want it to be parameter我知道我可以从 HttpContext.Request 读取它,但我希望它是参数

I've ended up creating custom InputFormatter, which was pretty straitforward, but if there is better alternative, you are very welcome to write an answer!我最终创建了自定义 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);
    }
}

Register the XDocumentInputFormatter in startup.cs在 startup.cs 中注册 XDocumentInputFormatter

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

Just a change for the answer given by Liero, you should use a StreamReader, so you can support multiple encodings.只是对 Liero 给出的答案进行了更改,您应该使用 StreamReader,这样您就可以支持多种编码。 Tested my solution with UTF-8, UTF-16 and ASCI declaration header.使用 UTF-8、UTF-16 和 ASCI 声明标头测试了我的解决方案。

Change the Method from XDocumentInputFormatter:从 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);
}

To below到下面

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

These solutions works, but have one disadvantage in .NET Core 3 - they causes exception (inside calling XDocument.LoadAsync):这些解决方案有效,但在 .NET Core 3 中有一个缺点 - 它们会导致异常(在调用 XDocument.LoadAsync 时):

System.InvalidOperationException: Synchronous operations are disallowed. System.InvalidOperationException:不允许同步操作。 Call ReadAsync or set AllowSynchronousIO to true instead.调用 ReadAsync 或将 AllowSynchronousIO 设置为 true。

Here is my modified solution with FileBufferingReadStream (inspired by code from Microsoft.AspNetCore.Mvc.Formatters.XmlSerializerInputFormatter)这是我使用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();
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM