問題:action中返回匿名物件時,swagger只能按強型別生成返回值描述
解決辦法:使用roslyn在記憶體中動態執行程式碼,使用json.net反序列化匿名物件,向swagger返回動態匿名物件
效果:
Swaager在描述控制器的方法時,可以使用以下方法
<response code="400">如果id為空</response>
或
[ProducesResponseType(typeof(ResponseT<User>), 200)]
Openapi json如下
生成效果如下圖
上述方法必須存在強型別user
程式碼
[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;
}
}
@@#
使用
[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獲取 名稱
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");
}
}
@@#
使用
[ProducesResponseType(typeof(ResponseT<(int id,string name)>), 400)]
@@E