釘釘小程序踩坑記錄-dd.httpRequest請求頭模擬器與真機類型不一致問題.md


釘釘小程序踩坑記錄-dd.httpRequest請求頭模擬器與真機類型不一致問題

模擬器與真機測試獲取到的Http請求頭類型不一致

Http請求代碼

請求代碼
請求代碼

模擬器獲取到的Http請求頭

釘釘模擬器獲取到的cookie
釘釘模擬器獲取到的cookie

模擬器中獲取到的請求頭為一個對象,可以通過調用對象的屬性直接獲取到請求頭的值,且獲取到的Cookie值是數組類型。

真機測試獲取到的Http請求

釘釘手機獲取到的cookie
釘釘手機獲取到的cookie

可以看到獲取的請求頭為一個數組,只能通過遍歷獲取到請求頭的值,且獲取到的Cookie值是字符串類型。

影響

  1. 在獲取請求頭的值時,需要考慮到模擬器與真機中請求頭類型不一致問題
  2. 真機環境對cookie使用的問題,如果在發起請求時,直接傳入獲取的cookie字符串,導致服務端獲取的cookie字符串為Uid=admin; expires=Sat, 14 Nov 2020 08:17:47 GMT; max-age=2592000; domain=192.168.1.102; path=/; httponly, Uid=admin,從而導致服務端獲取cookie失敗。在模擬器中直接傳入獲取的cookie數組時,在服務端獲取到的cookie字符串為Uid=admin; expires=Sat, 14 Nov 2020 07:51:06 GMT; max-age=2592000; domain=192.168.1.102; path=/; httponly; Uid=admin,服務器可以正常獲取到cookie。(服務端為ASP.NET WEBAPI。)

這兩個字符串值在后面的httponly結尾的分隔符有區別,一個是逗號(,)一個是分號(;)。

HttpRequestHeaders.GetCookies源碼

public static Collection<CookieHeaderValue> GetCookies(this HttpRequestHeaders headers)
{
    if (headers == null)
    {
        throw Error.ArgumentNull("headers");
    }
    Collection<CookieHeaderValue> collection = new Collection<CookieHeaderValue>();
    IEnumerable<string> enumerable;
    // 獲取cookie請求頭
    if (headers.TryGetValues("Cookie", out enumerable))
    {
        foreach (string current in enumerable)
        {
            CookieHeaderValue item;
            // 將cookie字符串轉成cookie對象
            if (CookieHeaderValue.TryParse(current, out item))
            {
                collection.Add(item);
            }
        }
    }
    return collection;
}

CookieHeaderValue類中的TryParseParseCookieSegment靜態方法:

public static bool TryParse(string input, out CookieHeaderValue parsedValue) {
    parsedValue = null;
    if (string.IsNullOrEmpty(input))
    {
        return false;
    }
    // 根據分號拆分字符串,將cookie字符串變成{{key}}={{value}}的字符串數組
    string[] array = input.Split(CookieHeaderValue.segmentSeparator);
    CookieHeaderValue cookieHeaderValue = new CookieHeaderValue();
    string[] array2 = array;
    for (int i = 0; i < array2.Length; i++)
    {
        string segment = array2[i];
        if (!CookieHeaderValue.ParseCookieSegment(cookieHeaderValue, segment))
        {
            return false;
        }
    }
    if (cookieHeaderValue.Cookies.Count == 0)
    {
        return false;
    }
    parsedValue = cookieHeaderValue;
    return true;
}

private static bool ParseCookieSegment(CookieHeaderValue instance, string segment) {
    if (string.IsNullOrWhiteSpace(segment))
    {
        return true;
    }
    // 根據等號拆分{{key}}={{value}}字符串
    string[] array = segment.Split(CookieHeaderValue.nameValueSeparator, 2);
    if (array.Length < 1 || string.IsNullOrWhiteSpace(array[0]))
    {
        return false;
    }
    string text = array[0].Trim();
    if (string.Equals(text, "expires", StringComparison.OrdinalIgnoreCase))
    {
        string segmentValue = CookieHeaderValue.GetSegmentValue(array, null);
        DateTimeOffset value;
        if (FormattingUtilities.TryParseDate(segmentValue, out value))
        {
            instance.Expires = new DateTimeOffset?(value);
            return true;
        }
        return false;
    }
    else
    {
        if (string.Equals(text, "max-age", StringComparison.OrdinalIgnoreCase))
        {
            string segmentValue2 = CookieHeaderValue.GetSegmentValue(array, null);
            int seconds;
            if (FormattingUtilities.TryParseInt32(segmentValue2, out seconds))
            {
                instance.MaxAge = new TimeSpan?(new TimeSpan(0, 0, seconds));
                return true;
            }
            return false;
        }
        else
        {
            if (string.Equals(text, "domain", StringComparison.OrdinalIgnoreCase))
            {
                instance.Domain = CookieHeaderValue.GetSegmentValue(array, null);
                return true;
            }
            if (string.Equals(text, "path", StringComparison.OrdinalIgnoreCase))
            {
                instance.Path = CookieHeaderValue.GetSegmentValue(array, "/");
                return true;
            }
            if (string.Equals(text, "secure", StringComparison.OrdinalIgnoreCase))
            {
                string segmentValue3 = CookieHeaderValue.GetSegmentValue(array, null);
                if (!string.IsNullOrWhiteSpace(segmentValue3))
                {
                    return false;
                }
                instance.Secure = true;
                return true;
            }
            else
            {
                if (!string.Equals(text, "httponly", StringComparison.OrdinalIgnoreCase))
                {
                    string segmentValue4 = CookieHeaderValue.GetSegmentValue(array, null);
                    bool result;
                    try
                    {
                        FormDataCollection formDataCollection = new FormDataCollection(segmentValue4);
                        NameValueCollection values = formDataCollection.ReadAsNameValueCollection();
                        CookieState item = new CookieState(text, values);
                        instance.Cookies.Add(item);
                        result = true;
                    }
                    catch
                    {
                        result = false;
                    }
                    return result;
                }
                string segmentValue5 = CookieHeaderValue.GetSegmentValue(array, null);
                if (!string.IsNullOrWhiteSpace(segmentValue5))
                {
                    return false;
                }
                instance.HttpOnly = true;
                return true;
            }
        }
    }
}

由於通過分號拆分,再通過等號拆分,真機傳入的Cookie字符串最后會出現httponly, Uid的Key,該Key只滿足if (!string.Equals(text, "httponly", StringComparison.OrdinalIgnoreCase))這個條件,最后會執行CookieState item = new CookieState(text, values);構造函數,該構造函數會對Key做驗證,最終調用的的是FormattingUtilities類的ValidateHeaderToken驗證方法,源碼為:

// System.Net.Http.FormattingUtilities
public static bool ValidateHeaderToken(string token) {
    if (token == null)
    {
        return false;
    }
    for (int i = 0; i < token.Length; i++)
    {
        char c = token[i];
        if (c < '!' || c > '~' || "()<>@,;:\\\"/[]?={}".IndexOf(c) != -1)
        {
            return false;
        }
    }
    return true;
}

解決方案

  function getCookie(headers){
    if(!headers) return undefined;
    // 模擬器獲取cookie的代碼
    var cookie = headers["set-cookie"]||headers["Set-Cookie"];

    // 真機獲取cookie的代碼
    if(headers.length){
      for(let i = 0;i<headers.length;i++){
        var element=headers[i];

        if(element["set-cookie"]){
          cookie = element["set-cookie"];
          break;
        }
        if(element["Set-Cookie"]){
          cookie = element["Set-Cookie"];
          break;
        }
      }
    }

    // 如果獲取到的cookie是字符串,則將其轉成數組
    if(typeof(cookie)==="string"){
      cookie = [cookie];
    }

    return cookie;
  }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM