Netcore webapi action swagger response返回参数使用匿名类型

时间:2024-01-28 21:59:02

问题:action中返回匿名对象时,swagger只能按强类型生成返回值描述

解决办法:使用roslyn在内存中动态执行代码,使用json.net反序列化匿名对象,向swagger返回动态匿名对象

效果:

   

   

Swaager在描述控制器的方法时,可以使用以下方法

<response code="400">如果id为空</response>

[ProducesResponseType(typeof(ResponseT<User>), 200)]

Openapi json如下

生成效果如下图

上述方法必须存在强类型user

   

   

代码

@@@code

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]

public class AnonymousTypeProducesResponseTypeAttribute : Microsoft.AspNetCore.Mvc.ProducesResponseTypeAttribute

{

public AnonymousTypeProducesResponseTypeAttribute(string typeJson, int statusCode) : base(getAnonymousType(typeJson), statusCode)

{

}

/// <summary>

///

/// </summary>

/// <param name="typeJson">匿名类型描述串

/// </param>

/// <example>new {id=1,name=""}</example>

/// <param name="statusCode"></param>

static System.Type getAnonymousType(string typeJson)

{

var code = @"

public class Result {

public System.Type GetType2(){

var t = Newtonsoft.Json.JsonConvert.DeserializeAnonymousType(""{}"", #T#);

return t.GetType();

}

}

".Replace("#T#", typeJson.Replace("'", @""""));

   

List<PortableExecutableReference> refList = new List<PortableExecutableReference>();

refList.Add(MetadataReference.CreateFromFile(typeof(object).Assembly.Location));

refList.Add(MetadataReference.CreateFromFile(typeof(Newtonsoft.Json.JsonConvert).Assembly.Location));

refList.Add(MetadataReference.CreateFromFile(typeof(System.Runtime.AssemblyTargetedPatchBandAttribute).Assembly.Location));

refList.Add(MetadataReference.CreateFromFile(System.IO.Path.Combine(System.IO.Path.GetDirectoryName(typeof(object).Assembly.Location), "netstandard.dll")));

   

var tree = CSharpSyntaxTree.ParseText(code);

var compilation = CSharpCompilation.Create("test")

.WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary))

.AddReferences(refList.ToArray()).AddSyntaxTrees(tree);

using (var stream = new MemoryStream())

{

var emitResult = compilation.Emit(stream);

if (emitResult.Success)

{

stream.Seek(0, SeekOrigin.Begin);

var assembly = Assembly.Load(stream.ToArray());

var typeResult = assembly.GetType("Result");

var m = Activator.CreateInstance(typeResult);

var Type = m.GetType();

return typeResult.GetMethod("GetType2").Invoke(m, null) as Type;

}

}

   

return null;

}

}

@@#

使用

@@@code

[HttpGet("Dept/Get")]

[AnonymousTypeProducesResponseType(@"new {Code=0,Msg='',Data=new []{new {id=0,name=''}}}", 200)]

public object DeptGet()

{

return new

{

Code = 0,

Msg = string.Empty,

Data = _context. Group.Select(r => new

{

id = r. ID,

name = r. Name

}).ToList()

};

}

@@#

   

另一种借助元组实现的方法

花了大量时间获取属性名没有结果,泛型传递时的方法获取 不到TupleElementNamesAttribute,只能走动态方法获取 返回值 ,再利用TupleElementNamesAttribute获取 名称

@@@code

public class ResponseT<T>

{

//有效 [JsonProperty("haaa")]

public int Code { get; set; }

// 无效 [JsonConverter(typeof(Q.DevExtreme.Tpl.Json.IPAddressConverter))]

public string Msg { get; set; }

public T Data { get; set; }

   

public T[] GetData()

{

//泛型,丢失名称信息,没有TupleElementNamesAttribute属性

return new[] { Data };

}

public (int a, string b) GetData2()

{

//可以使用下列方法获得名称

//Type t = typeof(C);

//MethodInfo method = t.GetMethod(nameof(C.M));

//var attr = method.ReturnParameter.GetCustomAttribute<TupleElementNamesAttribute>();

   

//string[] names = attr.TransformNames;

   

return (1, "2");

}

}

@@#

   

使用

@@@code

[ProducesResponseType(typeof(ResponseT<(int id,string name)>), 400)]

@@E