ASP.NET 核心 WebAPI XML 方法参数反序列化
ASP.NET Core WebAPI XML Method argument deserialization
我正在尝试在 .NET Core 3.1 上制作 WebAPI 控制器 支持 JSON 和 XML 作为 request/response 内容-类型。
控制器在接收 JSON 和 "application/json" 时完美运行,但是当它接收 XML 和 "application/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; }
}
P.S。项目包含用于 API 测试目的的 Swagger。
不要将 FromBody
属性用于 application/xml。
When a parameter has [FromBody], Web API uses the Content-Type header to select a formatter. In this example, the content type is "application/json" and the request body is a raw JSON string (not a JSON object).
您的 xml post 请求正文使用驼峰式大小写,导致模型绑定为空。
在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)
};
}
}
}
经过更多研究,我发现 swagger 生成了错误的 xml 示例,甚至没有注意到 类 或属性的自定义命名。
我编写了用于命名 xml 属性的自定义架构,因为它们是由 XML 属性命名的。
我遇到的唯一问题是 SchemaFilterContext 不提供枚举类型属性的描述。因此,为了命名枚举,我使用自定义属性作为 swagger 名称,并在 属性 上使用具有相同名称的 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);
}
}
我正在尝试在 .NET Core 3.1 上制作 WebAPI 控制器 支持 JSON 和 XML 作为 request/response 内容-类型。
控制器在接收 JSON 和 "application/json" 时完美运行,但是当它接收 XML 和 "application/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; }
}
P.S。项目包含用于 API 测试目的的 Swagger。
不要将 FromBody
属性用于 application/xml。
When a parameter has [FromBody], Web API uses the Content-Type header to select a formatter. In this example, the content type is "application/json" and the request body is a raw JSON string (not a JSON object).
您的 xml post 请求正文使用驼峰式大小写,导致模型绑定为空。
在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)
};
}
}
}
经过更多研究,我发现 swagger 生成了错误的 xml 示例,甚至没有注意到 类 或属性的自定义命名。
我编写了用于命名 xml 属性的自定义架构,因为它们是由 XML 属性命名的。 我遇到的唯一问题是 SchemaFilterContext 不提供枚举类型属性的描述。因此,为了命名枚举,我使用自定义属性作为 swagger 名称,并在 属性 上使用具有相同名称的 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);
}
}