Netcore webapi action swagger response返回引數使用匿名型別

秦秋隨發表於2021-04-16

問題: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

相關文章