• C#里如何简单的校验时间格式


    前言:

    晚上打算睡觉的时候,群里反馈订单接收失败,开工排查问题,日志显示验签失败,发现一个蛮有意思的BUG,总算有了一个写作的素材

    场景描述

    本次的场景属于比较常见的收单API,对第三方的订单进行签名验证,然后持久化到数据库,签名规则大致是将参数key按照升序排序,然后根据key=value&进行字符串拼接,最后加上秘钥,按照指定的加密方式生成签名

    前戏一

    设计之初,肯定是怎么简单怎么来,粗略代码如下

    [HttpPost]
    public async Task TestSendOrder([FromBody] ReceiveOrderRequest request)
    {
        var secret_key = _options.Value.SecretKey;
        var url = _options.Value.Host;
        //1.将模型转成json格式字符串
        var param = JsonConvert.SerializeObject(request);
        //2.将json格式字符串,序列化成有序字典
        SortedDictionary<string, string> dict = JsonConvert.DeserializeObjectstring, string>>(param);
        //3.循环字典,按规则拼接成待加密的明文字符串
        var data = "";
        foreach (var item in dict)
        {
            if (item.Key == "sign") continue;
            data += $"{item.Key}={item.Value}&";
        }
        data += $"secret_key={secret_key}";
        //4.生成签名
        var sign = EncryptHelper.SHA1Encryption(data);
        request.sign = sign;
        //5.模拟订单推送
        var res = await _httpClientHelper.PostData(url, JsonConvert.SerializeObject(request));
        return Ok(res);
    }
    

    不出意外,肯定是要出意外的,联调的时候,发现与第三方待加密的明文字符串不一致,问题出在JsonConvert序列化上,这里有两个问题

        1. DateTime格式不一致
        如: DateTime dt = "2022-07-30 12:26:56"
        序列化后 dt=2022-07-30T12:26:56
        2. decimal小数点后自动补0
        如: decimal price = 10
        序列化后 price=10.0
    

    针对第一个问题,很好解决,我们在序列化的时候,指定DateTime的格式即可

    var iso = new IsoDateTimeConverter();
    iso.DateTimeFormat = "yyyy-MM-dd HH:mm:ss";
    var param = JsonConvert.SerializeObject(request, iso);
    

    针对第二个问题,处理起来就比较麻烦了,要重写底层的一些东西(主要是我不会),这不符合"简单"的定义,得换个方案

    前戏二

    通过反射遍历对象,然后将属性名称与值,丢到有序字典里面,这里我写了个方法来判断值是否为时间,如果是时间类型,则格式化,代码如下

    public string GetFmortDateTime(string strDate) 
    {
        DateTime dt;
        if (DateTime.TryParse(strDate, out dt))
        {
            return dt.ToString("yyyy-MM-dd HH:mm:ss");
        }
        else
        {
            return strDate;
        }
    }   
    

    不出意外,肯定是要出意外的,不然也不会有这个素材去水一篇博客了

    正戏

    有个字段的值是9.9,结果被序列化成了 2022-09-09 00:00:00,吃了一惊,看来是把这个数字格式化成月份日份了,真有意思,又GET到一个新姿势,发现问题解决问题就简单多了,因为定义了数据模型,我们直接在反射的时候,获取该值的类型做判断即可

    public static async Task<bool> CheckSign(dynamic request, string secret)
    {
        SortedDictionary<string, string> dict = new SortedDictionary<string, string>();
        foreach (PropertyInfo p in request.GetType().GetProperties())
        {
            var value = p.GetValue(request);
            if (value == null)
            {
                dict[p.Name] = "";
            }
            else
            {
                var valueType = value.GetType();
                if (valueType.Name == "DateTime") 
                {
                    dict[p.Name] = Convert.ToDateTime(value).ToString("yyyy-MM-dd HH:mm:ss");
                }
                else 
                {
                    dict[p.Name] = value.ToString();
                }
            }
        }
        var sign = dict["sign"];
        dict.Remove("sign");
        var data = "";
        foreach (var item in dict)
        {
            data += $"{item.Key}={item.Value}&";
        }
        data += $"secret_key={secret}";
        var new_sign = EncryptHelper.SHA1Encryption(data);
        return new_sign.ToLower() == sign.ToLower();
    }
    
    折叠

    尾戏

    看到这里,可能就有小伙伴有话要说了,你这定义了一个模型,还要通过循环两次,才能生成待加密的明文字符串,不符合"简单",干脆直接用个有序字典去接收参数好了,这样只用循环一次

    秒啊,秒啊,秒啊,妙蛙种子都没有你秒,这种做法不是不行,但是后面维护的人估计要抓狂了,按照规约,我们是不推荐这么干的,这次就破例这么干一次,抛出另一个问题,一个字符串,如何判断它是一个我们约定的时间格式,很显然9.9并不是约定的时间格式

    这里推荐 DateTime.ParseExact方法,可以根据我们自定义的方式,来格式化时间,舒坦了...

    public static string GetFmortDateTime(string strDate)
    {
        string[] format = { "yyyy-MM-ddTHH:mm:ss" };
        DateTime dt;
        if (DateTime.TryParseExact(strDate,format,CultureInfo.InvariantCulture,DateTimeStyles.None,out dt))
        {
            return dt.ToString("yyyy-MM-dd HH:mm:ss");
        }
        else
        {
            return strDate;
        }
    }
    
  • 相关阅读:
    项目会议如何开
    CSS的元素显示模式和CSS的背景
    【信息安全原理】——传输层安全(学习笔记)
    Windows C++程序运行过程中生成dump文件
    计算机组成原理 | 第一章
    什么是供应链?
    [ C++ ] string类常见接口及其模拟实现
    GJB 5000B简介
    09架构管理之工作量评估评审
    雪花算法生成主键ID
  • 原文地址:https://www.cnblogs.com/tibos/p/16543880.html