【问题标题】:Add property to all responses in asp.net core将属性添加到 asp.net core 中的所有响应
【发布时间】:2022-06-11 20:54:52
【问题描述】:

我有一个带有多个端点的 API。我想为所有端点响应添加一个属性,而不是将其单独添加到每个端点响应模型中。

例如:

public class MyClass
{
    public string MyProperty { get; set; } = "Hello";
}

public class MyOtherClass
{
    public string MyOtherProperty { get; set; } = "World";
}

public class MyController : ControllerBase
{
    [HttpPost]
    public async Task<ActionResult<MyClass>> EndpointOne(POSTData data)
    {
        // implementation omitted
    }

    [HttpPost]
    public async Task<ActionResult<MyOtherClass>> EndpointTwo(POSTOtherData otherData)
    {
        // implementation omitted
    }
}

调用任一端点会根据需要返回 MyClassMyOtherClass 的 JSON 表示 - 即

{ "MyProperty":"Hello" } or { "MyOtherProperty":"World" }

我想向 API 中的所有端点添加一个属性,比如字符串 ApiName,以便上述代码的结果将是(视情况而定)

{ "MyProperty":"Hello", "ApiName":"My awesome API" } 

{ "MyOtherProperty":"World", "ApiName":"My awesome API" }

有没有办法在返回之前挂钩 JSON 字符串化的结果并添加这样的顶级属性?如果是这样,我想我必须将它连接到startup.cs,所以我一直在研究app.UseEndpoints(...) 方法,但到目前为止还没有找到任何有效的方法。要么没有添加属性,要么将原始结果替换为新属性。

提前致谢!

【问题讨论】:

标签: c# asp.net-core


【解决方案1】:

在您的网络 api 中使用 Newtonsoft.Json

在 Startup.cs 中注册自定义合约解析器:

builder.Services.AddControllers()
    .AddNewtonsoftJson(options => options.SerializerSettings.ContractResolver = CustomContractResolver.Instance);

实现:

public class CustomContractResolver : DefaultContractResolver {
    public static CustomContractResolver Instance { get; } = new CustomContractResolver();

    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        var properties = base.CreateProperties(type, memberSerialization);
        // add new property
        ...
        properties.Add(newProp);
        return properties;
    }}

查看更多Json.net Add property to every class containing of a certain type

【讨论】:

    【解决方案2】:

    您可以添加具有共享属性的基类。应该适用于 XML 和 JSON。

    public class MyApiClass
    {
        public string ApiName => "MyAwesomeApi";
    }
    
    public class MyClass : MyApiClass
    {
        public string MyProperty { get; set; } = "Hello";
    }
    
    public class MyOtherClass : MyApiClass
    {
        public string MyOtherProperty { get; set; } = "World";
    }
    
    public class MyController : ControllerBase
    {
        [HttpPost]
        public async Task<ActionResult<MyClass>> EndpointOne(POSTData data)
        {
            // implementation omitted
        }
    
        [HttpPost]
        public async Task<ActionResult<MyOtherClass>> EndpointTwo(POSTOtherData otherData)
        {
            // implementation omitted
        }
    }
    

    【讨论】:

      【解决方案3】:

      我的 0.02 美分说要实现一个抽象基类。

      抽象类继承看起来类似于标准继承。

          public class MyClass:MyAbstractClass
          {
              [JsonPropertyName("Class Property")]
              public string MyProperty { get; set; } = "Hello";
              }
          public class MyOtherClass:MyAbstractClass
          {
              [JsonPropertyName("Class Property")]
              public string MyOtherProperty { get; set; } = "World";
              }
      

      但是,抽象类将允许您在将来需要时实现其他功能。

          public abstract class MyAbstractClass{
      
              [JsonPropertyName("API Name")]
              public string ApiName{get;set;}="My Aweomse API";
      
              //Just a thought if you want to keep track of the end point names
              //while keeping your object names the same
              [JsonIgnore(Condition = JsonIgnoreCondition.Always)]
              public string EndPointName{
                  get{
                      return get_endpoint_name();
                      }}
              private string get_endpoint_name(){
                  return this.GetType().Name;
                  }
      
              //May as well make it easy to grab the JSON
              [JsonIgnore(Condition = JsonIgnoreCondition.Always)]
              public string As_JSON{
                  get {
                      return to_json();
                      }}
      
              private string to_json(){
                  
                  object _myObject = this;
                  string _out;
      
                  JsonSerializerOptions options = 
                      new JsonSerializerOptions { 
                              WriteIndented = true };
                  
                  _out = 
                      JsonSerializer.Serialize(_myObject, options);
      
                  return _out;
              }
          }
      

      可能应该已经实现了一个通用的返回对象,然后你可以循环访问任务结果。如果任务只返回 JSON 字符串,我想你仍然可以。

          public static void run(){
      
              Task<MyClass> _t0 = task0();
              Task<MyOtherClass> _t1 = task1();
              Task[] _tasks = new Task[]{_t0,_t1};
      
              Task.WhenAll(_tasks).Wait();
              
              Console.WriteLine(""
              +$"{_t1.Result.ApiName}:\n"
              +$"End Point: {_t1.Result.EndPointName}:\n"
              +$"JSON:\n{_t1.Result.As_JSON}");
              
              Console.WriteLine(""
              +$"{_t0.Result.ApiName}:\n"
              +$"End Point: {_t0.Result.EndPointName}:\n"
              +$"JSON:\n{_t0.Result.As_JSON}");
              
          }
      
          private static Task<MyClass> task0(){
              return Task.Run(()=>{
                  Console.WriteLine("Task 0 Doing Something");
                  return new MyClass();
              });
          }
          private static Task<MyOtherClass> task1(){
              return Task.Run(()=>{
                  Console.WriteLine("Task 1 Doing Something");
                  return new MyOtherClass();
              });
          }
      

      当然还有很棒的...很棒的:-)结果:

      另一种想法是,您可以将两个不同的任务实现为抽象方法,但这是完全不同的对话。

      【讨论】:

        【解决方案4】:

        除了所有出色的答案之外,我更喜欢使用 Action Filter 和 ExpandoObject。 在 Program File 中,您应该添加自定义操作过滤器。

        builder.Services.AddControllers(opt =>
        {
            opt.Filters.Add<ResponseHandler>();
        });
        

        和 ResponseHandler 的行为如下:

        public class ResponseHandler : IActionFilter
            {
                public void OnActionExecuted(ActionExecutedContext context)
                {
                    IDictionary<string, object> expando = new ExpandoObject();
        
                    foreach (var propertyInfo in (context.Result as ObjectResult).Value.GetType().GetProperties())
                    {
                        var currentValue = propertyInfo.GetValue((context.Result as ObjectResult).Value);
                        expando.Add(propertyInfo.Name, currentValue);
                    }
                    dynamic result = expando as ExpandoObject;
        
        
                    result.ApiName = context.ActionDescriptor.RouteValues["action"].ToString();
                    context.Result = new ObjectResult(result);
                }
        
                public void OnActionExecuting(ActionExecutingContext context)
                {
        
                }
            }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-06-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-04-01
          • 2018-02-21
          相关资源
          最近更新 更多