iOS. PercentEscape是錯用的URLEncode,看看AFN和Facebook吧

来源:http://www.cnblogs.com/Mike-zh/archive/2016/01/22/5152073.html
-Advertisement-
Play Games

別再使用stringByAddingPercentEscapesUsingEncoding 當遇到發送網路請求的參數中有漢字的情況,很多人一股腦地使用 進行轉義,這樣帶有漢字的urlString就會將每個漢字轉成相應的unicode編碼對應的3個%形式,這叫urlEncode(每個能寫後端的語言都....


別再使用stringByAddingPercentEscapesUsingEncoding

當遇到發送網路請求的參數中有漢字的情況,很多人一股腦地使用stringByAddingPercentEscapesUsingEncoding:進行轉義,這樣帶有漢字的urlString就會將每個漢字轉成相應的unicode編碼對應的3個%形式,這叫urlEncode(每個能寫後端的語言都有的方法),但是蘋果的stringByAddingPercentEscapesUsingEncoding:卻不是urlEncode。實際上我們使用的參數值可能會包含一些特殊的字元,如&?這樣的字元,而Percent轉義已經不能滿足需求了,如下麵的例子:

NSString *queryWord = @"漢字&ss";
NSString *urlString = [NSString stringWithFormat:@"https://www.baidu.com/s?ie=UTF-8&wd=%@", queryWord];
NSString *escapedString = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSLog(@"%@", escapedString); // https://www.baidu.com/s?ie=UTF-8&wd=%E6%B1%89%E5%AD%97&ss

這是一個非常常見的情景,(之前公司項目的搜索中,也遇到過這種情況),這種被轉義之後的URL,服務端接收到的參數會使這樣的

["ie":"UTF-8", "wd":"漢字", "ss":nil]

即使你做如下的改進:(在請求之前將每個參數都轉義,再使用&拼接參數也無濟於事)

NSString *queryWord = @"漢字&ss";
NSString *escapedQueryWord = [queryWord stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSString *urlString = [NSString stringWithFormat:@"https://www.baidu.com/s?ie=UTF-8&wd=%@", escapedQueryWord];
NSLog(@"%@", urlString); // https://www.baidu.com/s?ie=UTF-8&wd=%E6%B1%89%E5%AD%97&ss

產生這種情況的原因是:百分號轉義不等於URLEncode
該編碼不同於URL編碼,由於不會對&字元編碼,因此不會改變URL參數的分隔。URL編碼會編碼&?與其他標點符號。如果查詢字元串包含了這些字元,那麼需要實現一種更加徹底的編碼方法。

不過還好iOS7.0推出了stringByAddingPercentEncodingWithAllowedCharacters:方法,這個方法會對字元串進行更徹底的轉義,但是需要傳遞一個參數:這個參數是一個字元集,表示:在進行轉義過程中,不會對這個字元集中包含的字元進行轉義,而保持原樣保留下來。
這樣就可以使用它改造上面的代碼了:

NSString *queryWord = @"漢字&ss";
NSString *escapedQueryWord = [queryWord stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet letterCharacterSet]];
NSLog(@"%@", escapedQueryWord); // %E6%B1%89%E5%AD%97%26ss
NSString *urlString = [NSString stringWithFormat:@"https://www.baidu.com/s?ie=UTF-8&wd=%@", escapedQueryWord];
NSLog(@"%@", urlString); // https://www.baidu.com/s?ie=UTF-8&wd=%E6%B1%89%E5%AD%97%26ss

在上面的例子中傳遞參數[NSCharacterSet letterCharacterSet]來保證字母不被轉義。所以被轉義之後的參數值是:%E6%B1%89%E5%AD%97%26ss,這樣問題就解決了,但是有時候會遇到queryString中的表單域也需要轉義的情況,比如是一個表單數組如:

https://www.baidu.com/s?person[contact]=13801001234&person[address]=北京&habit[]=游泳&habit[]=騎行

這樣可以使用將key轉義,不過key中的[]字元是不需要轉義的:可以自定義一個CharacterSet實現需求:

NSMutableCharacterSet *mutableCharSet = [[NSMutableCharacterSet alloc] init];
[mutableCharSet addCharactersInString:@"[]"]; // 允許'['和']'不被轉義
NSCharacterSet *charSet = mutableCharSet.copy;

NSMutableString *mutableString = [NSMutableString string];
for (unit in queryString) {
    NSString *escapedField = [unit.field stringByAddingPercentEncodingWithAllowedCharacters:charSet];
    NSString *escapedValue = [unit.value stringByAddingPercentEncodingWithAllowedCharacters:charSet];
    [mutableString addFormat:@"%@=%@", escapedField, escapedValue];
}

這樣問題已經圓滿解決了,美中不足的是:當queryString非常多的時候你如何保證從queryString正確地提取出來每個unit呢,這個牽扯到複雜的字元串解析的問題。先不做討論。實際上有一個好的方案是使用AFN將每個參數的URL和queryString在構建的時候分離,使用URL和parameter(字典)分別傳入的方法,也就是說在使用AFN的時候避免使用:

GET:@"https://www.baidu.com/s?ie=UTF-8&wd=%E6%B1%89%E5%AD%97%26ss"
parameters:nil
success:nil
failure:nil

而是儘量使用

GET:@"https://www.baidu.com/s"
parameters:@{@"ie":@"UTF-8",@"wd":@"漢字&ss"};
success:nil
failure:nil

為什麼要這樣,翻看AFN的源碼會發現,AFN對queryString的組裝是這樣進行的:

AFN會將parameters的傳遞的字典通過將每個表單元素的field和value進行urlcode之後拼接,然後再直接附加在傳遞的URLString後面(當然,如果是POST方式就不是附加了,而是將拼好的串放到HTTP body中)。

那麼如果要使用第一種方式,必須要確保自己在傳入的URLString是經過完美轉義的,因為AFN不會對你傳入的URLString進行檢測有沒有進行了轉義或者正確與否,但是AFN對上面方法中parameter參數的解析時非常徹底的,因此強烈建議使用第二種方式調用AFN的方法。那麼AFN是如何完美解析parameter參數的呢,這剛好是一個可以將字典轉為queryString的模塊呀!!!,下麵就來看一下:

對AFN urlEncode的研究

AFN將網路訪問分割為三個過程模塊
1.請求前:構建request的header和queryString、uploadContent和配置(如超時等),這部分的功能在AFURLRequestSerialization中
2.請求中:分別有基於NSConnection的訪問(3.0移除)和基於NSURLSession的訪問模塊
3.請求後:1錯誤處理2.成功處理:數據格式轉換和解析,主要在AFURLResponseSerialization中

requestSerialization就像過濾器一樣,每一個用於構建網路請求的URLRequest對象都會經過requestSerialization配置,再返回一個NSMutableURLRequest對象(參見2.x版本的dataTaskWithHTTPMethod: URLString: parameters: success: failure方法,3.0版本dataTaskWithHTTPMethod URLString: parameters: uploadProgress: downloadProgress: success:方法),NSURLSession對象會使用這個NSMutableURLRequest對象創建task。而我們要討論的將parameter轉為queryString的功能全部在AFURLRequestSerialization中,它實際上使用了

NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];

就完成了所有的請求前的配置功能,可以查看一下內部的實現,有一句關鍵性的代碼

mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
// 這裡的self是AFURLResponseSerialization對象

這句代碼用於對request對象設置requestHeader和轉義queryString,我們僅僅看一下對queryString進行轉義的
其內部按照這樣的思路實現:

1.如果傳遞過來的parameters不為空,就會判斷self.queryStringSerialization是否為空(self.queryStringSerialization屬性是一個 AFQueryStringSerializationBlock類型的block,它是用來實現轉義的核心代碼塊)

2.如果self.queryStringSerialization不為空,使用self.queryStringSerialization(request, parameters, &serializationError);進行轉義和組裝:

3.如果self.queryStringSerialization為空,使用一個內部函數來執行:AFQueryStringFromParameters(parameters),實際上每一個AFURLResponseSerialization對象在創建的時候queryStringSerialization屬性都是空的,因此外部不傳遞block類型的值給queryStringSerialization屬性時都會走這條路線,也就是使用AFQueryStringFromParameters(parameters)來解析參數。

AFQueryStringFromParameters的實現是這樣的:

NSString * AFQueryStringFromParameters(NSDictionary *parameters) {
    NSMutableArray *mutablePairs = [NSMutableArray array];
    for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
        [mutablePairs addObject:[pair URLEncodedStringValue]];
    }

    return [mutablePairs componentsJoinedByString:@"&"];
}

而其中使用到的AFQueryStringPairsFromDictionary函數是這樣實現的:

NSArray * AFQueryStringPairsFromDictionary(NSDictionary *dictionary) {
    return AFQueryStringPairsFromKeyAndValue(nil, dictionary);
}

NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value); // 這個方法太長,只放置了原型

思路為:

1.利用AFQueryStringPairsFromKeyAndValue函數將parameters字典中的每個key-value對取出,將每個key-value對構建為AFQueryStringPair對象,放到一個數組中。

2.在AFQueryStringFromParameters方法內部遍歷這個數組(每個元素為AFQueryStringPair對象),使用AFQueryStringPair類的轉義方法URLEncodedStringValue將AFQueryStringPair轉為字元串,將這些字元串存入新的數組中。這樣新數組中的每個元素就是轉義之後的field=value字元串,最後用&將數組元素連接即可。

函數AFQueryStringPairsFromKeyAndValue是一個非常完美的演算法,基本上考慮到了所有類型的表單域:包括表單數組的處理和對一個表單域賦值多個value的情況的處理,表單數組在html頁面經常用到的:

<form method="GET" action="http://127.0.0.1/test.php">
<input name="habit[]" value="游泳" />
<input name="habit[]" value="騎行" />
<input type="submit" value="提交">
</form>

瀏覽器自動轉義: habit%5B%5D=%E6%B8%B8%E6%B3%B3&habit%5B%5D=%E9%AA%91%E8%A1%8C
AFN傳遞parameter = @{@"habit":@[@"游泳", @"騎行"]}:
2.x版本:habit[]=%E6%B8%B8%E6%B3%B3&habit[]=%E9%AA%91%E8%A1%8C
3.0版本:habit%5B%5D=%E6%B8%B8%E6%B3%B3&habit%5B%5D=%E9%AA%91%E8%A1%8C (與瀏覽器相同)
php會將$_GET解析為:

array(1) { 
    ["habit"]=> array(2) { 
        [0]=> string(6) "游泳" 
        [1]=> string(6) "騎行" 
    } 
}

如果是這種寫法:

<form method="GET" action="http://127.0.0.1/test.php">
<input name="person[contact]" value="13801001234" />
<input name="person[address]" value="北京" />
<input type="submit" value="提交">
</form>

瀏覽器自動轉義:person%5Bcontact%5D=13801001234&person%5Baddress%5D=%E5%8C%97%E4%BA%AC
AFN傳遞parameter = @{@"person":@{@"contact":@"13801001234", @"address":@"北京"}}:
2.x版本: person[address]=%E5%8C%97%E4%BA%AC&person[contact]=13801001234 (沒有將[]轉義)
3.0版本:person%5Baddress%5D=%E5%8C%97%E4%BA%AC&person%5Bcontact%5D=13801001234 (與瀏覽器相同,但對field進行了排序)
結果為:

array(1) { 
    ["person"]=> array(2) { 
        ["contact"]=> string(11) "13801001234" 
        ["address"]=> string(6) "北京" 
    } 
}

如果是對於一個field多參數的情況

<form method="GET" action="http://127.0.0.1/test.php">
<input type="checkbox" name="habit" value="游泳">游泳
<input type="checkbox" name="habit" value="騎行">騎行
<input type="submit" value="提交">
</form>

瀏覽器自動轉義:habit=%E6%B8%B8%E6%B3%B3&habit=%E9%AA%91%E8%A1%8C
AFN傳遞parameter = @{@"habit":set} 其中set為:NSSet *set = [NSSet setWithObjects:@"游泳", @"騎行", nil];:
2.x版本: habit=%E6%B8%B8%E6%B3%B3&habit=%E9%AA%91%E8%A1%8C (與瀏覽器相同)
3.0版本:habit=%E6%B8%B8%E6%B3%B3&habit=%E9%AA%91%E8%A1%8C (與瀏覽器相同)

以上各種類型的html表單域的處理,函數AFQueryStringPairsFromKeyAndValue都已經很好地處理,不管如此,還對每個field按照NSString預設排序規則(字母表順序)進行了排序。

在接下來我會針對2.x版本的AFN剖析一下AFQueryStringPair的轉義方法- (NSString *)URLEncodedStringValueWithEncoding:(NSStringEncoding)stringEncoding的實現,中間會說到一些和3.0的差別:
我們看一下- (NSString *)URLEncodedStringValueWithEncoding:(NSStringEncoding)stringEncoding的代碼

- (NSString *)URLEncodedStringValueWithEncoding:(NSStringEncoding)stringEncoding { // AFN3.0的區別是換了個方法名,而且不用傳遞stringEncoding
    if (!self.value || [self.value isEqual:[NSNull null]]) { // 如果value為空值,只轉義field
        return AFPercentEscapedQueryStringKeyFromStringWithEncoding([self.field description], stringEncoding);
    } else { // 將field和value轉義後拼接
        return [NSString stringWithFormat:@"%@=%@", AFPercentEscapedQueryStringKeyFromStringWithEncoding([self.field description], stringEncoding), AFPercentEscapedQueryStringValueFromStringWithEncoding([self.value description], stringEncoding)];
    }
}

而對於AFPercentEscapedQueryStringKeyFromStringWithEncodingAFPercentEscapedQueryStringValueFromStringWithEncoding方法,它的實現是這樣的:

static NSString * const kAFCharactersToBeEscapedInQueryString = @":/?&=;+!@#$()',*"; // 在queryString進行URLEncode時需要進行轉義的字元

static NSString * AFPercentEscapedQueryStringKeyFromStringWithEncoding(NSString *string, NSStringEncoding encoding) {
    static NSString * const kAFCharactersToLeaveUnescapedInQueryStringPairKey = @"[]."; // 在urlencode時不需要轉義

    return (__bridge_transfer  NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (__bridge CFStringRef)string, (__bridge CFStringRef)kAFCharactersToLeaveUnescapedInQueryStringPairKey, (__bridge CFStringRef)kAFCharactersToBeEscapedInQueryString, CFStringConvertNSStringEncodingToEncoding(encoding));
}

static NSString * AFPercentEscapedQueryStringValueFromStringWithEncoding(NSString *string, NSStringEncoding encoding) {
    return (__bridge_transfer  NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (__bridge CFStringRef)string, NULL, (__bridge CFStringRef)kAFCharactersToBeEscapedInQueryString, CFStringConvertNSStringEncodingToEncoding(encoding));
}

這裡是使用了一個CoreFoundation中定義的函數:CFURLCreateStringByAddingPercentEscapes這函數的參數解釋如下:

index 參數名 解釋
1 allocator 為新的CFString對象分配記憶體的分配器,傳遞NULL或者kCFAllocatorDefault使用當前預設的分配器
2 originalString 要copy的CFString對象
3 charactersToLeaveUnescaped 在百分號轉義過程中要完好地留下的字元集,傳遞NULL指明所有非法的字元會被轉義
4 legalURLCharactersToBeEscaped 需要轉義的合法的字元集。傳遞NULL指明沒有合法的字元集要被替換(所有字元都不轉義)
5 encoding 轉化過程使用的編碼 如果你不關心正確的編碼,你應該使用UTF-8 (kCFStringEncodingUTF8), 這是一個由RFC 3986設計的在URL使用中很合適的編碼

看了參數的說明應該很容易理解為什麼要那樣傳遞參數,不過需要註意的是傳遞的字元串都是CFStringRef類型:因此要和NSString做一下橋接:

// NSString 轉為CFStringRef
(__bridge CFStringRef)string

// CFStringRef 轉為NSString
(__bridge_transfer  NSString *)string

而對於3.0的AFN無論是對field的轉義還是對value的轉義都使用了相同的函數NSString * AFPercentEscapedStringFromString(NSString *string)其在內部的實現就是使用了在本文第一部分提到提到的系統方法- (nullable NSString *)stringByAddingPercentEncodingWithAllowedCharacters:(NSCharacterSet *)allowedCharacters,而apple的文檔中指出這個方法內部會按照UTF-8編碼進行轉義,因此這裡剛好解釋了為什麼之前2.x版本需要傳遞編碼參數,而3.0就不用了。
這裡是NSString * AFPercentEscapedStringFromString(NSString *string)函數的一點核心代碼:

NSString * AFPercentEscapedStringFromString(NSString *string) {
    static NSString * const kAFCharactersGeneralDelimitersToEncode = @":#[]@"; // does not include "?" or "/" due to RFC 3986 - Section 3.4
    static NSString * const kAFCharactersSubDelimitersToEncode = @"!$&'()*+,;=";

    NSMutableCharacterSet * allowedCharacterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy];
    [allowedCharacterSet removeCharactersInString:[kAFCharactersGeneralDelimitersToEncode stringByAppendingString:kAFCharactersSubDelimitersToEncode]]; 

    // ......
    // .....
    return eacapedString;
}

可以看到允許轉義的字元集一開始是URLQueryAllowedCharacterSet,然後去掉了kAFCharactersGeneralDelimitersToEncode(@":#[]@")和kAFCharactersSubDelimitersToEncode(@"!$&'()*+,;=")包含的字元,也就是說這些字元最終都需要轉義,相比較2.x版本確實是多轉義了[,].。這也是剛纔看到說的使用AFN2.x版本參數值和3.0版本轉義後參數值不同而3.0與瀏覽器中相同的原因。

費了那麼大的勁,終於把這部分梳理清晰了,然後來做一件有意義的事:(將字典轉為queryString的功能抽取)

將AFN字典轉queryString模塊抽取

這裡我只是寫了一個NSDictionary的分類

@interface NSDictionary (ConvertToQueryString)

- (NSString *)convertToQueryString;

@end
#import "NSDictionary+ConvertToQueryString.h"
#import "AFNetworking.h"

@implementation NSDictionary (ConvertToQueryString)

- (NSString *)convertToQueryString {
    if (!self || [self isEqual:[NSNull null]]) {
        return @"";
    }
#if AFN Version < 3.0
    return AFQueryStringFromParametersWithEncoding(self, NSUTF8StringEncoding);
#else
    return AFQueryStringFromParameters(self);
#endif
}

@end

一切就是那麼簡單,但是很有用處。接下來就是一點點擴展了,我們知道AFN已經封裝了字典轉為queryString的功能,那麼有時候會有將queryString轉為字典的需求,雖然這種需求並不常見,但偶爾也會碰到。那麼具體怎麼做呢。我推薦一個比較優秀的框架:Facebook的facebook-ios-sdk這是一個用於構建iOS應用的基礎框架:包含了facebook登錄和分享、處理應用間跳轉的功能、一些繪圖API,應用的數據統計模塊等功能。這個框架並不是多麼龐大,源碼文件也比較少,但是其中一個網路工具還是挺好用的:FBSDKUtility。
在這個類的頭文件中只是聲明瞭這樣4個方法:

@interface FBSDKUtility : NSObject

+ (NSDictionary *)dictionaryWithQueryString:(NSString *)queryString;

+ (NSString *)queryStringWithDictionary:(NSDictionary *)dictionary error:(NSError *__autoreleasing *)errorRef;

+ (NSString *)URLDecode:(NSString *)value;

+ (NSString *)URLEncode:(NSString *)value;

@end

在m文件中的實現並不複雜,單就URLEncode來說,它雖然對了一些對參數值的驗空操作,但是沒有想AFN那樣將各種情況都充分考慮,因此若要完成queryStringWithDictionary:的功能還是建議使用AFN的功能,將AFN的方法加入到FBSDKUtility中,這種代碼的普適性降低了但是增加幾分可靠性。
至於dictionaryWithQueryString:方法,我相信我們都能寫出這樣的方法,但是說到底數據被轉換後是要拿給後臺使用的,排除各種後臺語言和框架的差異,我們應該做到儘量使得傳遞的數據與後臺經過解析之後獲取的數據一致。

+ (NSDictionary *)dictionaryWithQueryString:(NSString *)queryString
{
  NSMutableDictionary *result = [[NSMutableDictionary alloc] init];
  NSArray *parts = [queryString componentsSeparatedByString:@"&"];

  for (NSString *part in parts) {
    if ([part length] == 0) {
      continue;
    }

    NSRange index = [part rangeOfString:@"="];
    NSString *key;
    NSString *value;

    if (index.location == NSNotFound) {
      key = part;
      value = @"";
    } else {
      key = [part substringToIndex:index.location];
      value = [part substringFromIndex:index.location + index.length];
    }

    key = [self URLDecode:key];
    value = [self URLDecode:value];
    if (key && value) {
      result[key] = value;
    }
  }
  return result;
}

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • css3動畫使用技巧之—JQ配合css3實現輪播之animation-delay應用 1 2 3 4 5 ...
  • 利用轉場動畫實現(這裡不說轉場動畫),主要就是幾個坐標的轉換:將cell上的imageView快照生成一個snapView(直接創建一個ImageVIew也一樣), 在將cell上image的frame 坐標轉換到containerView上,在將snapView放大到目標尺寸 (首先你要知道轉場動...
  • 一、日誌工具類 Log.java 1 public class L 2 { 3 private L() 4 { 5 /* 不可被實例化 */ 6 throw new UnsupportedOperationException("Ca...
  • 在這個大冬天里默默敲著鍵盤,勿噴.今天學習swift過程中,學習到閉包,發現閉包和oc的block中有很多的相同之處,又重新學習了一下並且學習了一些高級點的用法,內容如下:1.block格式說明:(返回類型)(^塊名稱)(參數類型) = ^(參數列表) {代碼實現};//如果沒有參數,等號後面參數列...
  • 通過自定義輪播圖控制項,學習自定義控制項,本文介紹如何創建輪播圖控制項,方便以後使用,本控制項帶計時器,預設時間間隔是5.0秒,同時支持設置左右方向輪播,也支持手勢滑動切換,另外還可以指定從下標為currentPage的圖片開始輪播
  • 一、Shape的用法 :shape用於設定形狀,可以在selector,layout等裡面使用,有6個子標簽,各屬性如下: 填充:設置填充的顏色 間隔:設置四個方向上的間隔 大小:設置大小 圓角:同時設置五個屬性,則Radius屬性無效 android:Radius="20dp" 設置...
  • 在文章《Android程式員從小白到大神必讀資料彙總(一)》裡面介紹了幾篇Android入門和提升效率的技術資料,今天小編收集了5篇進階的資料,趕緊來看看吧!另外,歡迎大家加入工程師博主交流群:391519124,交流博客經驗和技術一、Android開發相見恨晚的方法和介面 Android開發中,有...
  • 1.Stream 與IRandomAccessStream轉換 2.為Group創建Person的示例代碼 3.FaceIdentify 介面調用出現問題
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...