简体   繁体   English

在asp net core web api中模型绑定时忽略XML命名空间

[英]Ignore XML namespace when modelbinding in asp net core web api

I know this has been asked here and at various other places but I have not seen a simple answer.我知道这里和其他地方已经有人问过这个问题,但我还没有看到一个简单的答案。 Or at least, I have not been able to find any.或者至少,我找不到任何东西。

In short, I have an.Net Core Web Api endpoint that accepts XML.简而言之,我有一个接受 XML 的端点。 Using (in Startup):使用(在启动中):

services.AddControllers().AddXmlSerializerFormatters();

I want to modelbind it to a class.我想将它模型绑定到 class。 Example:例子:

[Route("api/[controller]")]
[ApiController]
public class PersonController : ControllerBase
{
    [HttpPost]
    [Consumes("application/xml")]
    [ApiConventionMethod(typeof(DefaultApiConventions), nameof(DefaultApiConventions.Post))]
    public async Task<ActionResult> PostPerson([FromBody] Person person)
    {
        return Ok();
    }
}

// Class/Model
[XmlRoot(ElementName = "Person")]
public class Person
{
    [XmlElement(ElementName = "Name")]
    public string Name { get; set; }

    [XmlElement(ElementName = "Id")]
    public int Id { get; set; }
}

Passing in:传入:

<Person><Name>John</Name><Id>123</Id></Person>

works fine.工作正常。 However, as soon as namespaces comes into play it either fails to bind the model:然而,一旦命名空间开始发挥作用,它要么无法绑定 model:

<Person xmlns="http://example.org"><Name>John</Name><Id>123</Id></Person>
<Person xmlns="http://example.org"><Name>John</Name><Id xmlns="http://example.org">123</Id></Person>

Or the model can be bound but the properties are not:或者可以绑定 model 但属性不是:

<Person><Name xmlns="http://example.org">John</Name><Id>123</Id></Person>
<Person><Name xmlns="http://example.org">John</Name><Id xmlns="http://example.org">123</Id></Person>

etc.等等

I understand namespaces.我了解名称空间。 I do realize that I can set the namespaces in the XML attribute for the root and elements.我确实意识到我可以在 XML 属性中为根和元素设置命名空间。 However, I (we) have a dozens of callers and they all set their namespaces how they want.但是,我(我们)有几十个调用者,他们都按照自己的意愿设置命名空间。 And I want to avoid to have dozens of different versions of the (in the example) Person classes (one for each caller).而且我想避免有几十个不同版本的(在示例中)Person 类(每个调用者一个)。 I would also mean that if a caller changes their namespace(s) I would have to update that callers particular version and redeploy the code.我还意味着,如果调用者更改了他们的命名空间,我将不得不更新调用者的特定版本并重新部署代码。

So, how can I modelbind incoming XML to an instance of Person without taking the namespaces into account?那么,如何在不考虑命名空间的情况下将传入的 XML 模型绑定到 Person 的实例?

I've done some tests overriding/creating an input formatter use XmlTextReader and set namespaces=false:我已经做了一些测试,覆盖/创建输入格式化程序使用 XmlTextReader 并设置命名空间 = false:

        XmlTextReader rdr = new XmlTextReader(s);
        rdr.Namespaces = false;
        

But Microsoft recommdes to not use XmlTextReader since.Net framework 2.0 so would rather stick to.Net Core (5 in this case).但微软建议不要使用 XmlTextReader,因为 .Net 框架 2.0 所以宁愿坚持使用 .Net Core(在这种情况下为 5)。

You can use custom InputFormatter,here is a demo:您可以使用自定义 InputFormatter,这是一个演示:

XmlSerializerInputFormatterNamespace: XmlSerializerInputFormatterNamespace:

public class XmlSerializerInputFormatterNamespace : InputFormatter, IInputFormatter, IApiRequestFormatMetadataProvider

    {
        public XmlSerializerInputFormatterNamespace()
        {
            SupportedMediaTypes.Add("application/xml");
        }
        public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
        {
            var xmlDoc = await XDocument.LoadAsync(context.HttpContext.Request.Body, LoadOptions.None, CancellationToken.None);
            Dictionary<string, string> d = new Dictionary<string, string>();
            foreach (var elem in xmlDoc.Descendants())
            {
                d[elem.Name.LocalName] = elem.Value;
            }
            return InputFormatterResult.Success(new Person { Id = Int32.Parse(d["Id"]), Name = d["Name"] }); 
        }
       

    }

Person:人:

public class Person
{
    public string Name { get; set; }

    public int Id { get; set; }
}

startup:启动:

services.AddMvc(options =>
            {
                options.RespectBrowserAcceptHeader = true; // false by default
                options.InputFormatters.Insert(0, new XmlSerializerInputFormatterNamespace());
            }).SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
           .AddXmlSerializerFormatters()
          .AddXmlDataContractSerializerFormatters();

result:结果: 在此处输入图像描述

So, in order to be able to modelbind XML to a class without taking namespaces into consideration I created new InputFormatter.因此,为了能够在不考虑命名空间的情况下将 XML 模型绑定到 class,我创建了新的 InputFormatter。 And I use XmlTextReader in order to ignore namespaces.我使用 XmlTextReader 来忽略命名空间。 Microsoft recommends to use XmlReader rather than XmlTextReader. Microsoft 建议使用 XmlReader 而不是 XmlTextReader。 But since XmlTextReader is there still (in.Net 6.0 Preview 3) I'll use it for now.但是由于 XmlTextReader 仍然存在(在.Net 6.0 Preview 3 中),我现在就使用它。

Simply create an inputformatter that inherits from XmlSerializerInputFormatter like so:只需创建一个继承自 XmlSerializerInputFormatter 的 inputformatter,如下所示:

public class XmlNoNameSpaceInputFormatter : XmlSerializerInputFormatter
{
    private const string ContentType = "application/xml";
    public XmlNoNameSpaceInputFormatter(MvcOptions options) : base(options)
    {
        SupportedMediaTypes.Add(ContentType);
    }

    public override bool CanRead(InputFormatterContext context)
    {
        var contentType = context.HttpContext.Request.ContentType;
        return contentType.StartsWith(ContentType);
    }

    public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
    {
        var type = GetSerializableType(context.ModelType);
        var request = context.HttpContext.Request;

        using (var reader = new StreamReader(request.Body))
        {
            var content = await reader.ReadToEndAsync();
            Stream s = new MemoryStream(Encoding.UTF8.GetBytes(content));

            XmlTextReader rdr = new XmlTextReader(s);
            rdr.Namespaces = false;
            var serializer = new XmlSerializer(type);
            var result = serializer.Deserialize(rdr);
            return await InputFormatterResult.SuccessAsync(result);
        }
    }
}

Then add it to the inputformatters like so:然后将其添加到 inputformatters 中,如下所示:

        services.AddControllers(o => 
        {
            o.InputFormatters.Add(new XmlNoNameSpaceInputFormatter(o));
        })
        .AddXmlSerializerFormatters();

Now we can modelbind Person or any other class no matter if there is namespaces or not in the incoming XML.现在我们可以对 Person 或任何其他 class 进行模型绑定,无论传入的 XML 中是否有命名空间。 Thanks to @yiyi-you感谢@yiyi-you

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

相关问题 ASP.NET Web API 2 XML Post请求忽略XML名称空间属性 - ASP.NET Web API 2 XML Post request ignore XML namespace attribute ASP.NET WEB API 2 - 每个请求触发两次模型绑定 - ASP.NET WEB API 2 - ModelBinding Firing twice per request ASP.NET Core Web API 中使用的命名空间应该是什么? - What should be the namespace used in ASP.NET Core Web API? 具有集合属性的类在作为 XML 发布到 ASP.Net Core 3.1 Web API 时未正确绑定 - Class with Collection Properties are not binding properly when posted as XML to ASP.Net Core 3.1 Web API .NET Core WEB API Modelbinding:如何使用 JSON Schema 验证 - .NET Core WEB API Modelbinding: how to make use of JSON Schema validation ASP.NET Core Web API - JSON 忽略在 Core-6 Swagger 中不起作用 - ASP.NET Core Web API - JSON Ignore not working in Core-6 Swagger ASP.NET 核心模型绑定拾取默认枚举值,以防枚举项不可用 - ASP.NET Core modelbinding pickups up default enum value in case when the enum item is not available Asp.net 内核 Controller 的自定义模型绑定问题 - Custom ModelBinding Issues With Asp.net Core Controller 忽略 ASP.NET Web ZDB974238714CA8DE634A7CE1D083A14 中的 controller - Ignore controller in ASP.NET Web API 返回XML时发生错误-ASP.net Web API - Error occurred when returning XML - ASP.net web API
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM