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


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

    解决办法:使用roslyn在内存中动态执行代码,构建匿名对象,再从匿名对象解析构造多个临时类,向swagger返回临时类类型(直接使用匿名类会出现 重名问题)

    效果:





     





           

               

           

               

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

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

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

    Openapi json如下



    生成效果如下图



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

               

               

    代码

    @@@code

    using System;

    using System.CodeDom.Compiler;

    using System.Collections.Generic;

    using System.IO;

    using System.Linq;

    using System.Reflection;

    using System.Runtime.CompilerServices;

    using System.Text.RegularExpressions;

    using System.Threading.Tasks;

    using Microsoft.AspNetCore.Mvc.ApiExplorer;

    using Microsoft.AspNetCore.Mvc.Formatters;

    using Microsoft.CodeAnalysis;

    using Microsoft.CodeAnalysis.CSharp;

    using Microsoft.CSharp;

     

    namespace Q.DevExtreme.Tpl.Attributes

    {

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

    public class ProducesResponseAnonymousTypeAttribute : Microsoft.AspNetCore.Mvc.ProducesResponseTypeAttribute

    {

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

    {

    //TODO:内部实际 还是强类型,不支持多个匿名类型引用

    }

    static long seq = DateTime.Now.Millisecond;

     

    /// <summary>

    /// 匿名类型转类型

    /// </summary>

    /// <param name="t"></param>

    /// <returns></returns>

    public static void AnonymousType2Type(System.Text.StringBuilder sb, Type type)

    {

    List<(Type, string, bool)> list = new List<(Type, string, bool)>();

    foreach (var m in type.GetProperties())

    {

    //判断是否为数组

    bool isCollection = m.PropertyType.GetInterface("ICollection") != null;

    string arrayFlag = isCollection ? "[]" : "";

    if (m.PropertyType.Name.Contains("AnonymousType", StringComparison.OrdinalIgnoreCase))

    {

    //创建子类,记录名称

    string tempClassName = $"temp_{System.Threading.Interlocked.Increment(ref seq)}";

    list.Add((m.PropertyType, tempClassName, isCollection));

    sb.AppendLine($"public {tempClassName}{arrayFlag} {m.Name} {{get;set;}}");

    }

    else

    sb.AppendLine($"public {m.PropertyType.Name}{arrayFlag} {m.Name} {{get;set;}}");

    }

    sb.AppendLine("}");

    foreach (var x in list)

    {

    if (x.Item3)

    {

    sb.AppendLine($"class {x.Item2} {{");

    AnonymousType2Type(sb, x.Item1.GetElementType());

    }

    else

    {

    sb.AppendLine($"class {x.Item2} {{");

    AnonymousType2Type(sb, x.Item1);

    }

    }

    }

    /// <summary>

    /// 从字符中创建匿名类型

    /// </summary>

    /// <param name="typeJson"></param>

    /// <returns></returns>

    static Type createAnonymousType(string typeJson)

    {

    var code = @"

    public class Result {

    public System.Type GetType5( ){

    var t=#T#;

    return t.GetType();

    }

     

    }

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

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

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

     

    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();

    var Type2 = typeResult.GetMethod("GetType5").Invoke(m, null) as Type;

    return Type2;

    }

    }

     

    return null;

    }

     

    /// <summary>

    /// 从字符串创建临时类型

    /// </summary>

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

    /// </param>

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

    /// <remarks>swagger不支持多个相同的匿名类型</remarks>

    static System.Type getAnonymousType(string typeJson)

    {

    // var x = Newtonsoft.Json.JsonConvert.DeserializeAnonymousType("{}", new { Code = 0, Msg = "", Data = new[] { new { Id = 1, Name = "" } } });

    Type anonymousType = createAnonymousType(typeJson);

    var x = Newtonsoft.Json.JsonConvert.DeserializeObject("{}", anonymousType);

    string tempClassName = $"temp_{System.Threading.Interlocked.Increment(ref seq)}";

    System.Text.StringBuilder sbType = new System.Text.StringBuilder(@$"

    using System;

    class {tempClassName} {{

    ");

    AnonymousType2Type(sbType, x.GetType());

    // Console.WriteLine(sbType.ToString());

     

    var code = sbType.ToString();

     

    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(tempClassName);

     

    return typeResult;

    }

    }

     

    return null;

    }

    }

    }

    @@#

    使用

    @@@code

    [ProducesResponseAnonymousType(@"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)]

    @@#

  • 相关阅读:
    flask
    ORACLE EXP不能导出空表的原因分析及解决方法
    安装sqlService需要重启解决方法
    sqlserver2008日志已满解决方法
    Oracle中删除用户下所有对象
    sqlserver生成表行数据insert语句
    对回调函数的理解
    github笔记
    Ubuntu摆脱QQ版本过旧问题!安装 wineinternational版 qq
    Servlet完全教程
  • 原文地址:https://www.cnblogs.com/QinQouShui/p/14667531.html
Copyright © 2020-2023  润新知