在控制器 Returns 之后修改 JSON 对象

Modify JSON Object After Controller Returns

我将 C# 与 MVC 一起使用,并尝试在控制器 returns 之后但在到达客户端之前将数据添加到 JSON 消息中。

有没有我可以用的 C# framework/tool?

例子

控制器returns 特定订单的订单数据,其中包括 Unix 时间日期(int 表示秒)。

消息在返回给调用者之前被拦截。

JSON 消息中附加了另一个从 Unix 时间转换为公历 date/time 的字段。

这可能吗?

从控制器返回数据后,您不能对 C# 中的数据执行任何操作。现在要交给客户了。您必须在公历时间的 class 中添加一个字段并在返回之前对其进行初始化,或者在 client-side 上处理该字段。向 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

为此,您可以执行如下操作(添加额外的 属性 来表示公历 date/time):

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; } }

有两种解决方案,但推荐none!!!两者都需要使用反射和递归来解析并在结果上注入值。

主要问题,你怎么知道一个数字是unix时间跨度而不是一个数字!在我的例子中,我比较结果日期是否在 2000 年和当年之间。 一个简单的匹配是使用 Unix 时间垃圾邮件的命名约定,例如:

public int UnixSaleDate {get; set;}

如果存在命名约定,请更改以下代码片段:

if (value.Type == JTokenType.Integer && IsValidDateTime(value)) // <= Change it
if (value.Type == JTokenType.Integer && name.Contains("Unix"))  // <= With these

Option 1: WebApi Action filter

可以在动作上指定使用,方便。

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"
    }
  ]
}

Option 2: DelegatingHandler

是最糟糕的解决方案!!!更多详细信息以及如何 DelegatingHandler for response in WebApi

shebang 的其余部分,reuse-it 来自用于日期时间注入(注入方法)的选项 1 代码片段。

祝你好运,选择明智。