Swagger/Swashbuckle 无法识别请求模型中的 JObject

Swagger/Swashbuckle do not recognize JObject in request model

我的环境:Asp.NETWebAPI、NET Framework 4.5.2、Swashbuckle.Core 5.6.0

出于某种原因,我的控制器必须继承以前相同的控制器,像这样

public class User100Controller : ApiController
{
    [HttpGet]
    [AllowAnonymous]
    public virtual string Get()
    {
        return "1.0.0";
    }
}

public class User101Controller : User100Controller
{
    [HttpGet]
    [AllowAnonymous]
    public override string Get()
    {
        return "1.0.1";
    }
}
直接

运行,swaggerui页面正确显示

sample picture, pls right click

但是如果我添加一个带有参数的 Post 动作,swagger ui 不会识别请求模型

UserModelUserModelSex

/// <summary>
/// UserModel
/// </summary>
public class UserModel
{
    /// <summary>
    /// Name
    /// </summary>
    public string name { get; set; } = string.Empty;

    /// <summary>
    /// Age
    /// </summary>
    public int age { get; set; } = 0;
}

/// <summary>
/// UserModelSex
/// </summary>
public class UserModelSex : UserModel
{
    /// <summary>
    /// Sex
    /// </summary>
    public int sex { get; set; } = -1;
}

User100Controller

[HttpPost]
[SwaggerResponse(200, "success", typeof(UserModel))]
public virtual IHttpActionResult SaveUser([FromBody] UserModel model)
{
    if (string.IsNullOrEmpty(model.name) || model.age == 0)
    {
        return Ok("error");
    }

    //...
    return Ok("success");
}

sample picture, pls right click

这是正确的效果,现在如果我在 User101ontroller 中覆盖此 SaveUser 操作,并传递新的请求模型 UserModelSex,我得到报错,因为Override Method必须和parent Method有相同的param List,所以我改成这样

public class User100Controller : ApiController
{
    [HttpPost]
    [SwaggerResponse(200, "success", typeof(UserModel))]
    public virtual IHttpActionResult SaveUser([FromBody] JObject json)
    {
        var model = json.ToObject<UserModel>();
        if (string.IsNullOrEmpty(model.name) || model.age == 0)
        {
            return Ok("error");
        }

        //...
        return Ok("success");
    }
}

public class User101Controller : User100Controller
{
    [HttpPost]
    [SwaggerResponse(200, "success", typeof(UserModelSex))]
    public override IHttpActionResult SaveUser([FromBody] JObject json)
    {
        var model = json.ToObject<UserModelSex>();
        if (string.IsNullOrEmpty(model.name) || model.age == 0 || model.sex == -1)
        {
            return Ok("error");
        }

        //...
        return Ok("success");
    }
}

招摇ui 不认识 JObject

sample picture, pls right click

如何在请求模型中显示UserModelUserModeSex,如图2

最后还是自己解决了

  • 添加SwaggerRequestModelAttribute.cs
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class SwaggerRequestModelAttribute : Attribute
{
    public Type RequestModel { get; private set; }

    public string ModelName { get; private set; }

    public SwaggerRequestModelAttribute(Type requestModel)
    {
        RequestModel = requestModel;
        ModelName = requestModel.Name;
    }
}
  • 然后,将 [SwaggerRequestModel] 标记为控制器
[HttpPost]
[SwaggerRequestModel(typeof(UserModel))]
public virtual IHttpActionResult SaveUser([FromBody] JObject json)
{
    var model = json.ToObject<UserModel>();
    if (string.IsNullOrEmpty(model.name) || model.age == 0)
    {
        return Ok("error");
    }

    //...
    return Ok("success");
}
  • 添加ModelInBodyOperationFilter.cs
public class ModelInBodyOperationFilter : IOperationFilter
{
    public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
    {
        if (operation.parameters == null) operation.parameters = new List<Parameter>();

        var attribute = apiDescription.GetControllerAndActionAttributes<SwaggerRequestModelAttribute>();
        if (attribute.Any())
        {
            if (operation.parameters.Count > 0 && operation.parameters[0].schema.type == "object")
            {
                if (!schemaRegistry.Definitions.ContainsKey(attribute.First().ModelName))
                    schemaRegistry.GetOrRegister(attribute.First().RequestModel);

                operation.parameters.RemoveAt(0);
                operation.parameters.Add(new Parameter
                {
                    name = "-",
                    @in = "body",
                    required = true,
                    schema = new Schema { @ref = $"#/definitions/{attribute.First().RequestModel.Namespace}.{attribute.First().ModelName}" }
                });
            }
        }
    }
}
  • 另外,在 SwaggerConfig.Register()
c.UseFullTypeNameInSchemaIds();
c.OperationFilter<ModelInBodyOperationFilter>();
  • 最后,运行 应用并预览

sample picture, pls right click