[英]Add property to all responses in asp.net core
I have an API with multiple endpoints.我有一个具有多个端点的 API。 I'd like to add a property to all endpoint responses, without adding it to each endpoint response model individually.
我想为所有端点响应添加一个属性,而不是单独将它添加到每个端点响应模型中。
Ex:前任:
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
}
}
Calling either endpoint returns a JSON representation of MyClass
or MyOtherClass
as appropriate - ie调用任一端点会根据需要返回
MyClass
或MyOtherClass
的 JSON 表示 - 即
{ "MyProperty":"Hello" } or { "MyOtherProperty":"World" }
I want to add a property, say a string ApiName
, to all endpoints in the API, so that the result of the above code would be either (as appropriate)我想向 API 中的所有端点添加一个属性,比如字符串
ApiName
,以便上述代码的结果是(视情况而定)
{ "MyProperty":"Hello", "ApiName":"My awesome API" }
or或者
{ "MyOtherProperty":"World", "ApiName":"My awesome API" }
Is there a way to hook into the JSON-stringified result just before returning and add a top-level property like that?有没有办法在返回之前挂钩 JSON 字符串化的结果并添加这样的顶级属性? If so, I presume I'd have to wire it up in
startup.cs
, so I've been looking at app.UseEndpoints(...)
methods, but haven't found anything that's worked so far.如果是这样,我想我必须在
startup.cs
中连接它,所以我一直在研究app.UseEndpoints(...)
方法,但到目前为止还没有找到任何有效的方法。 Either it's not added the property, or it's replaced the original result with the new property.要么没有添加属性,要么将原始结果替换为新属性。
Thanks in advance!提前致谢!
Use Newtonsoft.Json in your net web api在您的网络 api 中使用 Newtonsoft.Json
Register a custom contract resolver in Startup.cs:在 Startup.cs 中注册一个自定义合约解析器:
builder.Services.AddControllers()
.AddNewtonsoftJson(options => options.SerializerSettings.ContractResolver = CustomContractResolver.Instance);
The implementation:实施:
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;
}}
See more Json.net Add property to every class containing of a certain type查看更多Json.net 将属性添加到包含某种类型的每个类
You can add a base class with the shared property.您可以添加具有共享属性的基类。 Should work for both XML and JSON.
应该适用于 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
}
}
My 0.02 cents says to implement an abstract base class.我的 0.02 美分说要实现一个抽象基类。
Abstract class inheritance look similar to a standard inheritance.抽象类继承看起来类似于标准继承。
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";
}
However the abstract class will allow you to implement additional features in the event you need them in the future.但是,抽象类将允许您在将来需要它们时实现附加功能。
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;
}
}
Probably should have implemented a generic return object, then you could just loop through the task results.可能应该已经实现了一个通用的返回对象,然后你可以遍历任务结果。 I suppose you still can if you have the task return only the JSON string.
我想如果你的任务只返回 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();
});
}
And of course the aweosome...awesome:-) results:当然还有令人敬畏的...令人敬畏的:-)结果:
Another thought is that you could implement your two different tasks as abstract methods, but that's a different conversation all together.另一个想法是您可以将两个不同的任务实现为抽象方法,但这是一个不同的对话。
In addition to all of the great answers, I prefer to use Action Filter and ExpandoObject.除了所有出色的答案之外,我更喜欢使用 Action Filter 和 ExpandoObject。 In Program File you should add your custom action Filter.
在程序文件中,您应该添加自定义操作过滤器。
builder.Services.AddControllers(opt =>
{
opt.Filters.Add<ResponseHandler>();
});
and ResponseHandler acts like below:和 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)
{
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.