【问题标题】:ASP.NET Core WebAPI XML Method argument deserializationASP.NET Core WebAPI XML 方法参数反序列化
【发布时间】:2020-07-11 07:49:06
【问题描述】:

我正在尝试在 .NET Core 3.1 上创建一个 WebAPI 控制器,它支持 JSON 和 XML 作为请求/响应内容类型。

控制器在接收带有“application/json”的 JSON 时可以正常工作,但是当它接收带有“application/xml”的 XML 时,方法参数是使用默认值创建的,而不是在请求正文中发布的值。

示例项目 - https://github.com/rincew1nd/ASPNetCore_XMLMethods

启动中的附加 XML 序列化程序:

services.AddControllers().AddXmlSerializerFormatters();

带有方法和测试模型的控制器:

    [ApiController]
    [Route("[controller]")]
    public class TestController : ControllerBase
    {
        [HttpPost, Route("v1")]
        [Consumes("application/json", "application/xml")]
        [Produces("application/json", "application/xml")]
        public TestRequest Test([FromBody] TestRequest data)
        {
            return data;
        }
    }

    [DataContract]
    public class TestRequest
    {
        [DataMember]
        public Guid TestGuid { get; set; }
        [DataMember]
        public string TestString { get; set; }
    }

附:项目包含用于 API 测试目的的 Swagger。

【问题讨论】:

  • xml请求的示例数据需要TestGuid而不是testGuidTestString而不是testString

标签: asp.net-core asp.net-web-api xml-serialization asp.net-core-3.1


【解决方案1】:

不要为 application/xml 使用 FromBody 属性。

当参数有 [FromBody] 时,Web API 使用 Content-Type 标头来选择格式化程序。在此示例中,内容类型为“application/json”,请求正文为原始 JSON 字符串(不是 JSON 对象)。

Using [FromBody]

【讨论】:

  • 谢谢,但我在测试示例项目时使用内容类型并接受请求中的标头。我希望用户指定在 api 中发送的内容。示例帖子: curl -X POST "localhost:60557/Test/v1" -H "accept: application/json" -H "Content-Type: application/xml" -d "\t3fa85f64-5717-4562-b3fc-2c963f66afa6\tstring"
【解决方案2】:

您的 xml 发布请求正文使用驼峰式案例,导致模型绑定为空。

starup.cs 中添加using Swashbuckle.AspNetCore.SwaggerGen; 并尝试配置如下代码:

public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers().AddXmlSerializerFormatters();

        services.AddSwaggerGen(c =>
        {
            c.SwaggerDoc("v1", new OpenApiInfo { Title = "Neocase <-> 1C Integration", Version = "v1" });
            c.SchemaFilter<XmlSchemaFilter>();
        });

    }
public class XmlSchemaFilter : Swashbuckle.AspNetCore.SwaggerGen.ISchemaFilter
    {
        public void Apply(OpenApiSchema model, SchemaFilterContext context)
        {
            if (model.Properties == null) return;

            foreach (var entry in model.Properties)
            {
                var name = entry.Key;
                entry.Value.Xml = new OpenApiXml
                {
                    Name = name.Substring(0, 1).ToUpper() + name.Substring(1)
                };
            }
        }
    }

【讨论】:

  • 谢谢。我忘记了xml的区分大小写,也不知道在哪里指定它。
【解决方案3】:

经过更多研究,我发现 swagger 生成错误的 xml 示例,甚至没有注意到类或属性的自定义命名。

我编写了自定义模式来命名 xml 属性,因为它们是由 XML 属性命名的。 我面临的唯一问题是 SchemaFilterContext 不提供 Enum 类型属性的描述。因此,要命名枚举,我使用自定义属性作为招摇名称,并在具有相同名称的属性上使用 XMLElementAttribute(是的,它很垃圾但有效)。

public class XmlSchemaFilter : ISchemaFilter
{
    public void Apply(OpenApiSchema schema, SchemaFilterContext context)
    {
        //Try to find XmlRootAttribute on class
        var xmlroot = context.Type.GetAttributeValue((XmlRootAttribute xra) => xra);
        if (xmlroot != null)
        {
            schema.Xml = new OpenApiXml
            {
                Name = xmlroot.ElementName
            };
        }

        //Try to find XmlElementAttribute on property
        if (context.MemberInfo != null)
        {
            var xmlelement = context.MemberInfo.GetAttributeValue((XmlElementAttribute xea) => xea);
            if (xmlelement != null)
            {
                schema.Xml = new OpenApiXml
                {
                    Name = xmlelement.ElementName
                };
            }
        }

        //Try to find XmlEnumNameAttribute on enums
        if (context.Type.IsEnum)
        {
            var enumname = context.Type.GetAttributeValue((XmlEnumNameAttribute xea) => xea);
            if (enumname != null)
            {
                schema.Xml = new OpenApiXml
                {
                    Name = enumname.ElementName
                };
            }
        }
    }
}
public static class AttributeHelper
{
    public static TValue GetAttributeValue<TAttribute, TValue>(
        this Type type,
        Func<TAttribute, TValue> valueSelector)
        where TAttribute : Attribute
    {
        var att = type.GetCustomAttributes(
            typeof(TAttribute), true
        ).FirstOrDefault() as TAttribute;
        if (att != null)
        {
            return valueSelector(att);
        }
        return default(TValue);
    }
    public static TValue GetAttributeValue<TAttribute, TValue>(
        this MemberInfo mi,
        Func<TAttribute, TValue> valueSelector)
        where TAttribute : Attribute
    {
        var att = mi.GetCustomAttributes(
            typeof(TAttribute), true
        ).FirstOrDefault() as TAttribute;
        if (att != null)
        {
            return valueSelector(att);
        }
        return default(TValue);
    }
}

【讨论】:

    猜你喜欢
    • 2013-09-21
    • 1970-01-01
    • 1970-01-01
    • 2021-01-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-26
    相关资源
    最近更新 更多