控制器返回后修改 JSON 对象
Posted
技术标签:
【中文标题】控制器返回后修改 JSON 对象【英文标题】:Modify JSON Object After Controller Returns 【发布时间】:2018-03-26 18:41:30 【问题描述】:我将 C# 与 MVC 一起使用,并尝试在控制器返回后、但在它到达客户端之前将数据添加到 JSON 消息中。
有没有我可以使用的 C# 框架/工具?
示例
控制器返回特定订单的订单数据,其中包括 Unix 时间的日期(int 表示秒)。
消息在返回给调用者之前被截获。
另一个从 Unix 时间转换而来的公历日期/时间字段被附加到 JSON 消息中。
这可能吗?
【问题讨论】:
为什么不在服务器端控制器上修改然后返回呢? 查看Action Filters 所以你想从你的控制器返回json数据给客户端?这是问题吗?你能显示你想要返回的代码或数据样本吗 如果你使用的是 Json.net,你可以在使用 DateTimeConverterBase 序列化数据时编写一个转换器来执行此操作。 @LG8 是的,您需要在所有端点上还是只在特定端点上? 【参考方案1】:有两种解决方案,但都不推荐!!!都需要使用反射和递归来解析并在结果上注入值。
主要问题,你怎么知道一个数字是unix时间跨度而不是一个数字!在我的示例中,我比较结果日期是否在 2000 年和当前年份之间。 一个简单的匹配是使用 Unix 时间垃圾邮件的命名约定,例如:
public int UnixSaleDate get; set;
如果存在命名约定,请在下面的代码 sn-p 中更改:
if (value.Type == JTokenType.Integer && IsValidDateTime(value)) // <= Change it
if (value.Type == JTokenType.Integer && name.Contains("Unix")) // <= With these
选项 1:WebApi 操作过滤器
可以在使用的女巫动作上指定,这很方便。
public class ActionInjectDateTimeFromUnixActionWebApiFilter : ActionFilterAttribute
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
var objectContent = actionExecutedContext.Response.Content as ObjectContent;
if (objectContent != null)
//var type = objectContent.ObjectType; //type of the returned object
var value = objectContent.Value; //holding the returned value
var injection = RecursiveInjectUnixToDateTime(JObject.FromObject(value)); // recursively inject value
objectContent.Value = injection;
// Overwrite return value
actionExecutedContext.Response.Content = objectContent;
/// <summary>
/// Recursive DateTime Injection
/// </summary>
/// <param name="obj">'obj' need to be a JObject</param>
/// <returns></returns>
private dynamic RecursiveInjectUnixToDateTime(JToken obj)
var expObj = new ExpandoObject() as IDictionary<string, Object>;
foreach (JProperty p in obj)
string name = p.Name;
JToken value = p.Value;
switch (value.Type)
case JTokenType.Integer:
// Make sure your int is a date time after, after all isn't sure you are returning an int or a UNIX date time
if (value.Type == JTokenType.Integer && IsValidDateTime(value))
expObj.Add(name + "_DateTime", UnixToDateTime((long) value));
else
expObj.Add(name, value);
break;
case JTokenType.Object:
// Go deep
// expObj[name] = RecursiveInjectUnixToDateTime(p.Value);
expObj.Add(name, RecursiveInjectUnixToDateTime(p.Value));
break;
case JTokenType.Array:
// Go deep
var arr = new List<dynamic>();
foreach (var val in value.ToArray())
arr.Add(RecursiveInjectUnixToDateTime(val));
// expObj[name] = arr;
expObj.Add(name, arr);
break;
default:
// expObj[name] = value;
expObj.Add(name, value);
break;
return expObj;
/// <summary>
/// Validate if long value is a valid date time between 2000 and current year
/// </summary>
/// <param name="longUnix"></param>
/// <returns></returns>
private bool IsValidDateTime(JToken longUnix)
long unixDateTime = (long)longUnix;
if (unixDateTime > 0)
try
var date = UnixToDateTime(unixDateTime);
return date.Year >= 2000 && date.Year <= DateTime.UtcNow.Year;
catch (Exception ex)
return false;
return false;
private DateTime UnixToDateTime(long unixTimestamp)
DateTime unixYear0 = new DateTime(1970, 1, 1);
long unixTimeStampInTicks = unixTimestamp * TimeSpan.TicksPerSecond;
DateTime dtUnix = new DateTime(unixYear0.Ticks + unixTimeStampInTicks);
return dtUnix;
在控制器中:
[HttpGet]
[ActionInjectDateTimeFromUnixActionWebApiFilter]
[Route("something1")]
public IHttpActionResult GetSomething1()
return
Ok(
new
unix = 1267480860,
some_int = 2,
obj = new
description = "Yah Right"
,
items = new List<object>
new
id = 1,
name = "dude",
unix2 = 1403473486
);
[HttpGet]
[ActionInjectDateTimeFromUnixActionWebApiFilter]
[Route("something2")]
public Object GetSomething2()
return new
unix = 1267480860,
some_int = 2,
obj = new
description = "Yah Right"
,
items = new List<object>
new
id = 1,
name = "dude",
unix2 = 1403473486
;
对两个控制器操作的响应:
"unix_DateTime": "2010-03-01T22:01:00",
"some_int": 2,
"obj":
"description": "Yah Right"
,
"items": [
"id": 1,
"name": "dude",
"unix2_DateTime": "2014-06-22T21:44:46"
]
选项 2:DelegatingHandler
女巫是最糟糕的解决方案!!!更多细节以及如何 DelegatingHandler for response in WebApi
shebang 的其余部分,从选项 1 代码 sn-p 中重用它用于日期时间注入(注入方法)。
祝你好运,明智地选择。
【讨论】:
感谢您的示例!我希望存在一个框架,可以让我轻松更新各种消息类型的字段。但是,看起来我必须尝试单独使用每个控制器。【参考方案2】:从控制器返回数据后,您无法对 C# 中的数据执行任何操作。现在它正在发送给客户。您要么必须在类中添加一个公历时间字段并在返回之前对其进行初始化,要么在客户端处理它。在 javascript 中向对象添加字段非常简单。考虑以下对象:
var obj =
foo: "hello"
现在,让我们添加到这个对象。我们有两个选择:
obj.bar = "world" //Dot Notation
或
obj["bar"] = "world" //Square Bracket Notation
对于您的情况,使用点表示法最有意义,因为您的变量名称不会被动态声明。至于在 Javascript 中计算公历时间,我推荐一个名为 moment.js 的有用库,尽管它应该足够简单,使用 JS 中的常规 Date()
对象。
话虽如此,通常最好的做法是避免在 JS 中执行您可以在后端执行的操作。让浏览器执行您的服务器可以轻松执行的计算只会浪费客户端内存,占用加载时间,并且从长远来看可能会花费您宝贵的流量。
为了从 Unix 时间戳计算 UTC 时间,如果您使用的是 .Net 4.6,这非常容易。假设您的 unix 时间戳存储在名为“myTimestamp”的变量中:
DateTimeOffset dateTimeOffset = DateTimeOffset.FromUnixTimeSeconds(myTimestamp); //Offset from Unix epoch
DateTime dateTime = dateTimeOffset.UtcDateTime; //Gregorian
【讨论】:
感谢您的推荐。看起来 Web 操作过滤器可能允许在控制器返回后进行修改。【参考方案3】:为此,您可以执行以下操作(添加一个额外的属性来表示公历日期/时间):
public class MyModel
public long UnixDateTime get; set;
public DateTime GregorianDateTime get return new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc).AddSeconds(UnixDateTime);
如@Brandon 所述,您可以将以下代码块用于 .NET 4.6 或更高版本:
public DateTime GregorianDateTime get return DateTimeOffset.FromUnixTimeSeconds(UnixDateTime).DateTime;
【讨论】:
谢谢,但是我真的在找一个框架来修改从控制器方法返回后的消息。以上是关于控制器返回后修改 JSON 对象的主要内容,如果未能解决你的问题,请参考以下文章
从 Spring Boot 控制器返回非 JSON 数据(对象列表)
向 IQueryable 添加扩展方法,该方法对列表进行分页并在从 API 控制器方法返回时返回 json 对象