前言
我之前已經(jīng)寫過一篇有關(guān)網(wǎng)絡(luò)層的筆記:關(guān)于網(wǎng)絡(luò)層的設(shè)計(一)——和業(yè)務(wù)層的對接。在這篇筆記中主要解釋了網(wǎng)絡(luò)層設(shè)計時所要考慮的因素。給出的代碼例子是以block回調(diào)的,以AFNetworking請求網(wǎng)絡(luò)的,而且結(jié)構(gòu)是離散型的。
那本篇我們選擇處處與之相對立,即以delegate回調(diào)給業(yè)務(wù)層數(shù)據(jù),自己封裝NSURLConnection來請求網(wǎng)絡(luò),結(jié)構(gòu)采用集約型,而且更全面細致:加入了網(wǎng)絡(luò)超時的處理,請求隊列的控制,完善的公共參數(shù)等。
** HomeViewController.m **
這是業(yè)務(wù)層的代碼。業(yè)務(wù)層所有的網(wǎng)絡(luò)請求都通過
ConnectManager里的該方法完成,這就是所謂集約型結(jié)構(gòu),所有的業(yè)務(wù)請求均由一個方法完成,這樣的話開發(fā)效率可能會更高,畢竟只使用這個方法嘛。
而且數(shù)據(jù)從網(wǎng)絡(luò)層交付到業(yè)務(wù)層是通過delegate回調(diào)方法的形式,這樣的話若在同一個ViewController有多個不同接口的網(wǎng)絡(luò)請求,則需要在代理方法里判斷接口requestInterface,不同接口分別做不同處理。這也是和block回調(diào)不同的地方,block方式是每個接口都對應(yīng)一個block回調(diào),而delegate則只有一個代理方法,所以需要在代理方法里帶上requestInterface用以判別區(qū)分出不同的接口。
#import "HomeViewController.h"
#import "ConnectManager.h"
@interface HomeViewController ()
@end
@implementation HomeViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSDictionary *dict = @{@"stuId":@"666"};
// 業(yè)務(wù)層所有網(wǎng)絡(luò)請求,均通過該方法,是為集約型。
[[ConnectManager shareConnectManager] sendRequestWithID:RequestInterface_StudentList
parameters:dict
callbackDelegate:self
httpType:GET
removeAllRequest:NO];
}
// 通過delegate將數(shù)據(jù)從網(wǎng)絡(luò)層交付給業(yè)務(wù)層
- (void)connectManagerCallback:(RequestInterface)requestInterface requestResult:(RequestResult *)result
{
if(requestInterface == RequestInterface_StudentList)
{
if(result.status == 200){
// NSArray *stuList = result.resultInfo;
}
}
}
@end
** ConnectManager.h **
可以看到我們聲明了生成實例的單例方法,并聲明了那個給業(yè)務(wù)層調(diào)用請求網(wǎng)絡(luò)的方法。我們還定義了
ConnectManagerDelegate協(xié)議,業(yè)務(wù)層的VC為其代理者,即上面代碼看到的實現(xiàn)。
除此外,我們還定義了兩個枚舉變量,第一個是請求方式HttpType,第二個是代表接口的枚舉變量,所有的接口都在此定義成枚舉變量。業(yè)務(wù)層傳入的接口參數(shù)便為接口的枚舉變量。
#import <Foundation/Foundation.h>
#import "RequestResult.h"
// 請求方式
typedef NS_ENUM(NSInteger, HttpType)
{
GET = 0,
POST,
};
// 所有的接口定義成枚舉變量
typedef NS_ENUM (NSInteger,RequestInterface)
{
RequestInterface_StudentList = 0, // 學(xué)生列表
RequestInterface_StudentDetail, // 學(xué)生詳情
};
// 定義ConnectManagerDelegate協(xié)議
@protocol ConnectManagerDelegate <NSObject>
- (void)connectManagerCallback:(RequestInterface)requestInterface requestResult:(RequestResult *)result;
@end
@interface ConnectManager : NSObject
@property (nonatomic, weak)id<ConnectManagerDelegate> managerDelegate; // 回調(diào)給業(yè)務(wù)層的代理
+ (ConnectManager *)shareConnectManager;
- (void)sendRequestWithID:(RequestInterface )requestInterface
parameters:(NSDictionary *)parama
callbackDelegate:(id)delegate
httpType:(HttpType)httpType
removeAllRequest:(BOOL)isRemove;
@end
** ConnectManager.m **
我們可以看到該類主要完成了請求隊列的控制,可以控制在將當(dāng)前請求加入請求隊列時,是否清空請求隊列中的所有請求,或者只是從請求隊列中把舊的該請求清除。
通過調(diào)用HTTPConnect的方法,生成網(wǎng)絡(luò)連接的對象,并開始連接(等下看HTTPConnect類的詳情)。
而且它作為HTTPConnect的代理,實現(xiàn)了HTTPConnectDelegate協(xié)議的方法。并將數(shù)據(jù)轉(zhuǎn)換成了RequestResult,并調(diào)用了ConnectManagerDelegate協(xié)議的方法,將其回調(diào)給業(yè)務(wù)層。
** 注意接口返回格式的約定:**這里需要多說幾句的是。項目開始前,后臺會在接口文檔中寫明接口統(tǒng)一返回格式。所有的接口都遵循這個格式。比如,我們當(dāng)前的項目,后臺在接口文檔中就給出了“接口返回格式”:返回的json數(shù)據(jù)最外層有
h和b兩個key,分別意為head和body,在head的value又是一個字典,分別有code,msg,time三個key,分別代表錯誤碼,信息提示,服務(wù)器時間。而在body里則是真正需要的數(shù)據(jù)。
根據(jù)這個約定,我們創(chuàng)建了RequestResultmodel對象用以封裝返回數(shù)據(jù)(后面給出了RequestResult的代碼)。
** 對于Null值的處理:**若后臺返回的某個字段是Null,表示該字段為空,這樣直接使用既不友好,也比較危險。不友好之其一為若要顯示在UI上便會顯示為
<null>,其二為在判斷該字段是否為空的地方得要這樣:if([awarder.awarderId isKindOfClass:[NSNull class]]),比較麻煩。危險之處是在OC中若字典以這樣的方式@{@"key1":@"value1",@"key2":@"value2"};建立,此時若value1為空,程序執(zhí)行到此直接會崩掉。
所以我們需要對后臺返回的json數(shù)據(jù)都要做Null值處理。我們可以在預(yù)編譯文件中定義一個宏,若值為NSNull則將其置為@""。
#define DealWithJSONStringValue(_JSONVALUE) (_JSONVALUE != [NSNull null] && _JSONVALUE!=nil) ?[NSString stringWithFormat:@"%@",_JSONVALUE]:@""

#import "ConnectManager.h"
#import "HTTPConnect.h"
#import "RequestResult.h"
#define ServerAddress @"http://www.runedu.test/api" // 服務(wù)器地址
#define Interface_Version @"v5" // 接口版本
#define DealWithJSONValue(_JSONVALUE) ((_JSONVALUE != [NSNull null] && _JSONVALUE!=nil) ? _JSONVALUE:nil)
#define DealWithJSONStringValue(_JSONVALUE) (_JSONVALUE != [NSNull null] && _JSONVALUE!=nil) ?[NSString stringWithFormat:@"%@",_JSONVALUE]:@""
@interface ConnectManager ()<HTTPConnectDelegate>
{
NSOperationQueue *_operationQueue;
HTTPConnect *_httpConnect;
}
@end
@implementation ConnectManager
+ (ConnectManager *)shareConnectManager
{
static ConnectManager *shareManager = nil;
if(!shareManager){
shareManager = [[ConnectManager alloc] init];
}
return shareManager;
}
- (id)init
{
self = [super init];
if(self){
_operationQueue = [[NSOperationQueue alloc] init];
_operationQueue.suspended = YES;
}
return self;
}
- (void)sendRequestWithID:(RequestInterface)requestInterface
parameters:(NSDictionary *)parama
callbackDelegate:(id)delegate
httpType:(HttpType)httpType
removeAllRequest:(BOOL)isRemove
{
if(isRemove){
// 1.移除所有連接
[self clearAllRequestsConnect];
}else{
// 移除當(dāng)前接口某連接
[self clearConnectOfTheInterface:(requestInterface)];
}
_managerDelegate = delegate;
// 2.根據(jù)枚舉把接口轉(zhuǎn)換為相應(yīng)的接口字符串,并拼接完整URL.
NSString *urlStr = [self urlStringWithRequestInterface:requestInterface];
// 3.傳入接口地址,URL,參數(shù)dict,請求方式等生成請求連接對象。
_httpConnect = [[HTTPConnect alloc] initWithInterface:requestInterface
urlString:urlStr
parameters:parama
callbackDelegate:self
httpType:GET];
// _httpConnect.httpConnectDelegate = self;
// 4. 開始連接,并加入請求隊列
[_httpConnect startConnect];
[_operationQueue addOperation:_httpConnect];
}
// 清除所有連接
- (void)clearAllRequestsConnect
{
[_operationQueue cancelAllOperations];
}
// 清除當(dāng)前某接口的連接
- (void)clearConnectOfTheInterface:(RequestInterface)interface
{
for(HTTPConnect *httpConnect in _operationQueue.operations)
{
if(httpConnect.requestInterface == interface){
[httpConnect cancelConnect];
}
}
}
//- (void)clearTheRequestConnectOfInterface:(RequestInterface)requestInterface
//{
// for(nsop)
//}
// 返回完整的URL
- (NSString *)urlStringWithRequestInterface:(RequestInterface)interface
{
// 1.先根據(jù)枚舉變量找出接口對應(yīng)的字符串
NSString *interfaceStr = @"";
switch (interface)
{
case RequestInterface_StudentList:
{
interfaceStr = @"student/list";
break;
}
case RequestInterface_StudentDetail:
{
interfaceStr = @"student/detail";
break;
}
}
// 2.再把服務(wù)器地址、版本號、接口地址拼接成完整的URL
NSString *urlStr = @"";
if((int)interface<1000){ // 學(xué)生接口
urlStr = [NSString stringWithFormat:@"%@/student/%@%@",ServerAddress, Interface_Version, interfaceStr];
}else{ // 公共接口
urlStr = [NSString stringWithFormat:@"%@/public/%@%@",ServerAddress, Interface_Version, interfaceStr];
}
return urlStr;
}
#pragma mark ---- HTTPConnectDelegate
// 請求成功
- (void)httpConnectDownloadFinish:(HTTPConnect *)httpConnect
{
RequestResult *result = [[RequestResult alloc] init];
result.token = [httpConnect.tokenString copy];
//解析頭
NSDictionary *head = DealWithJSONValue([httpConnect.jsonDic objectForKey:@"h"]);
result.status = [DealWithJSONStringValue([head objectForKey:@"code"])integerValue];
result.message = DealWithJSONStringValue( [head objectForKey:@"msg"]);
result.serverTime = DealWithJSONStringValue( [head objectForKey:@"time"]);
result.jsonData = [[NSData alloc]initWithData:httpConnect.jsonData];
if(result.status == 200){
result.resultInfo = DealWithJSONValue([httpConnect.jsonDic objectForKey:@"b"]);
if([result.resultInfo isKindOfClass:[NSDictionary class]]){
NSDictionary *dataDic = result.resultInfo;
result.dataTotal = DealWithJSONStringValue([dataDic objectForKey:@"dataTotal"]);
}
}
// 代理回調(diào)。在每個VC中實現(xiàn),便可取得數(shù)據(jù)。
if(self.managerDelegate && [self.managerDelegate respondsToSelector:@selector(connectManagerCallback:requestResult:)])
{
[self.managerDelegate connectManagerCallback:httpConnect.requestInterface requestResult:result];
}
httpConnect = nil;
}
// 正在下載數(shù)據(jù)
- (void)httpConnectIsDownloading:(HTTPConnect *)httpConnect progress:(float)progress
{
}
// 請求失敗
- (void)httpConnectDownloadFinish:(HTTPConnect *)httpConnect failure:(NSError *)error
{
RequestResult *result = [[RequestResult alloc] init];
result.status = -10;
result.message = @"網(wǎng)絡(luò)不給力";
if(self.managerDelegate && [self.managerDelegate respondsToSelector:@selector(connectManagerCallback:requestResult:)])
{
[self.managerDelegate connectManagerCallback:httpConnect.requestInterface requestResult:result];
}
httpConnect = nil;
}
// 請求無響應(yīng)
- (void)httpConnectDownloadFinish:(HTTPConnect *)httpConnect stautsCode:(NSInteger)code
{
RequestResult *result = [[RequestResult alloc] init];
result.status = -11;
result.message = @"服務(wù)器無響應(yīng)";
if(self.managerDelegate && [self.managerDelegate respondsToSelector:@selector(connectManagerCallback:requestResult:)])
{
[self.managerDelegate connectManagerCallback:httpConnect.requestInterface requestResult:result];
}
httpConnect = nil;
}
@end
** HTTPConnect.h **
可以看到HTTPConnect是完成網(wǎng)絡(luò)連接和數(shù)據(jù)請求及響應(yīng)的核心。在此,我們創(chuàng)建網(wǎng)絡(luò)連接的對象,并控制開始連接和斷開連接。
值得注意的是HTTPConnect是繼承自NSOperation的。一個網(wǎng)絡(luò)連接的對象便是一個“操作”,每個連接對象都會加入到ConnectManager的請求隊列_operationQueue中。
我們還定義了很多HTTPConnect的屬性,主要為網(wǎng)絡(luò)響應(yīng)返回的數(shù)據(jù),之所以作為其公開的屬性,是因為要回調(diào)給ConnectManager,所以得暴露為公開的屬性。
除此外,我們還定義了HTTPConnectDelegate協(xié)議。
#import <Foundation/Foundation.h>
#import "ConnectManager.h"
@class HTTPConnect;
@protocol HTTPConnectDelegate <NSObject> // 定義HTTPConnectDelegate協(xié)議
// 請求成功
- (void)httpConnectDownloadFinish:(HTTPConnect *)httpConnect;
// 正在下載數(shù)據(jù)
- (void)httpConnectIsDownloading:(HTTPConnect *)httpConnect progress:(float)progress;
// 請求失敗
- (void)httpConnectFailure:(HTTPConnect *)httpConnect failure:(NSError *)error;
// 請求無響應(yīng)
- (void)httpConnectNoResponse:(HTTPConnect *)httpConnect stautsCode:(NSInteger)code;
@end
@interface HTTPConnect : NSOperation
@property (nonatomic,strong) NSString *jsonStr;
@property (nonatomic,strong) NSString *tokenString; //token,
@property (nonatomic,strong) NSMutableDictionary *jsonDic;
@property (nonatomic,strong) NSMutableData *jsonData;
@property (nonatomic,assign) RequestInterface requestInterface;
@property (nonatomic, weak)id<HTTPConnectDelegate> httpConnectDelegate;
- (id)initWithInterface:(RequestInterface)interface
urlString:(NSString *)urlStr
parameters:(NSDictionary *)parama
callbackDelegate:(id)delegate
httpType:(HttpType)httpType;
// 開始連接
- (void)startConnect;
// 斷開連接
- (void)cancelConnect;
@end
** HTTPConnect.m **
首先我們在初始化方法里,初始化了一些變量,并拼接了各種參數(shù),生成了網(wǎng)絡(luò)連接對象
_urlConnection。然后實現(xiàn)了startConnect和cancelConnect方法。值得注意的是我們加入了網(wǎng)絡(luò)請求超時的控制,若開始連接后,若計時器撐到了30s還沒有被銷毀,說明網(wǎng)絡(luò)請求超時了。
該類是網(wǎng)絡(luò)連接的核心,我們實現(xiàn)了NSURLConnectionDataDelegate協(xié)議的方法,在該協(xié)議的方法里,網(wǎng)絡(luò)數(shù)據(jù)主要在這些協(xié)議方法里進行響應(yīng),這是重點。我們對響應(yīng)的數(shù)據(jù)進行了一些處理,并賦值給HTTPConnect的屬性,然后調(diào)用HTTPConnectDelegate協(xié)議的方法,回調(diào)給ConnectManager,注意,這些協(xié)議的方法均有參數(shù)httpConnect對象,這樣便把其屬性,即網(wǎng)絡(luò)響應(yīng)返回的數(shù)據(jù)回調(diào)給了ConnectManager;而在ConnectManager中上面我們已經(jīng)說了,會把數(shù)據(jù)轉(zhuǎn)換為RequestResult;ConnectManager再調(diào)用其對應(yīng)的協(xié)議方法,便將RequestResult形式的數(shù)據(jù)回調(diào)給業(yè)務(wù)層。
這樣就完成了網(wǎng)絡(luò)響應(yīng)的數(shù)據(jù)從NSURLConnectionDataDelegate的實現(xiàn)方法到HTTPConnectDelegate的實現(xiàn)方法,再到ConnectManagerDelegate的實現(xiàn)方法。即網(wǎng)絡(luò)數(shù)據(jù)從HTTPConnect到ConnectManager,再到HomeViewController。
嗯,整個流程就是這樣。
#import "HTTPConnect.h"
@interface HTTPConnect ()
{
NSMutableURLRequest *_urlRequest;
NSURLConnection *_urlConnection;
NSHTTPURLResponse *_httpResponse;
BOOL _isConnect; // 是否已連接
RequestInterface _requestInterface; // 請求接口
NSTimer *_connectTimer; // 定時器,用來判斷請求超時
BOOL _isTimerFired; // 請求是否超時
NSInteger _dataSize;//總數(shù)據(jù)的大小
NSInteger _received;//每節(jié)點數(shù)據(jù)的大小
}
@end
@implementation HTTPConnect
- (id)initWithInterface:(RequestInterface)interface
urlString:(NSString *)urlStr
parameters:(NSDictionary *)parama
callbackDelegate:(id)delegate
httpType:(HttpType)httpType
{
self = [super init];
if(self)
{
_isConnect = NO;
_requestInterface = interface;
_jsonData = [[NSMutableData alloc] init];
_httpConnectDelegate = delegate;
NSString *paramStr = [self paramStrWithDict:parama]; // 生成參數(shù)字符串(url?stuId=12&stuName=wang)
// 1.根據(jù)GET和POST的不同,把參數(shù)裝入URL中。
if(httpType == GET)
{
NSString *URLStr = [NSString stringWithFormat:@"%@?%@",urlStr,paramStr]; // 拼接出GET請求下完整的URL
_urlRequest = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:URLStr]];
_urlRequest.HTTPMethod = @"GET";
}
else if(httpType == POST)
{
_urlRequest = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:urlStr]];
_urlRequest.HTTPMethod = @"POST";
NSData *paramData = [paramStr dataUsingEncoding:NSUTF8StringEncoding]; // 把參數(shù)字符串編碼為二進制流
if(paramData.length>0){
_urlRequest.HTTPBody = paramData;
}
}
// 2.設(shè)置HTTP請求的一些請求頭信息
// 省略...
// 3. 生成URL連接
_urlConnection = [[NSURLConnection alloc] initWithRequest:_urlRequest delegate:self];
}
return self;
}
#pragma mark ---- 提供給外部的功能方法
// 開始連接,并開始連接計時,若超過30秒還沒被關(guān)掉說明請求超時了。
- (void)startConnect
{
if(_isConnect){
return;
}
_isConnect = YES;
if(_urlConnection){
[_urlConnection start]; // 真真切切地進行網(wǎng)絡(luò)連接
_connectTimer = [NSTimer scheduledTimerWithTimeInterval:30
target:self
selector:@selector(connectTimeFired)
userInfo:nil
repeats:NO];
_isTimerFired = NO; // 賦初值為NO
}
}
// 斷開連接
- (void)cancelConnect
{
if(!_isTimerFired){
_isTimerFired = YES;
[_connectTimer invalidate];
_connectTimer = nil;
}
if(_urlConnection){
[_urlConnection cancel];
}
}
#pragma mark ---- NSURLConnection delegate
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
// 請求超時不做處理
if(_isTimerFired){
return;
}
// 回調(diào)給ConnectManager
if(_httpConnectDelegate && [_httpConnectDelegate respondsToSelector:@selector(httpConnectFailure:failure:)])
{
[_httpConnectDelegate httpConnectFailure:self failure:error];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
_httpResponse = (NSHTTPURLResponse *)response;
if(_httpResponse && [_httpResponse respondsToSelector:@selector(allHeaderFields)])
{
NSDictionary *httpResponseHeaderFields = [_httpResponse allHeaderFields];
NSNumber *totle=[NSNumber numberWithLongLong:[[httpResponseHeaderFields objectForKey:@"Content-Length"] longLongValue]];
_dataSize = [totle integerValue]; // 總數(shù)據(jù)
_tokenString = httpResponseHeaderFields[@"x-auth-token"]; // 從響應(yīng)頭里獲得token
}
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
_received += [data length];
[self performSelectorOnMainThread:@selector(updateProgress) withObject:nil waitUntilDone:NO];
[_jsonData appendData:data];
}
//
//- (BOOL)connectionShouldUseCredentialStorage:(NSURLConnection *)connection
//{
//
//}
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
}
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
}
- (void)connection:(NSURLConnection *)connection didCancelAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]){
[[challenge sender] useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust] forAuthenticationChallenge:challenge];
[[challenge sender] continueWithoutCredentialForAuthenticationChallenge: challenge];
}
}
#pragma mark ---- fuction method
- (void)connectTimeFired
{
_isTimerFired = YES; // 請求超時
if([_httpConnectDelegate respondsToSelector:@selector(httpConnectFailure:failure:)]){
[_httpConnectDelegate httpConnectFailure:self failure:nil];
}
[self cancelConnect];
}
// 把參數(shù)字典轉(zhuǎn)換為字符串
- (NSString *)paramStrWithDict:(NSDictionary *)dict
{
if(!dict){
return nil;
}
NSMutableString *paramStr = [NSMutableString stringWithString:@""];
NSInteger index = 0;
for(NSString *keyStr in dict.allKeys)
{
if(index==0){
[paramStr appendString:[NSString stringWithFormat:@"%@=%@",keyStr, [dict valueForKey:keyStr]]];
}else{
[paramStr appendString:[NSString stringWithFormat:@"&%@=%@",keyStr, [dict valueForKey:keyStr]]];
}
index++;
}
return [paramStr copy];
}
-(void)updateProgress
{
if (_dataSize==0) {
return;
}
float progress = _received/_dataSize; //計算出進度
// 回調(diào)給ConnectManager
if([_httpConnectDelegate respondsToSelector:@selector(httpConnectIsDownloading:progress:)])
{
[_httpConnectDelegate httpConnectIsDownloading:self progress:progress];
}
}
@end
** RequestResult.h 和 RequestResult.m **
#import <Foundation/Foundation.h>
@interface RequestResult : NSObject
@property(nonatomic,assign)NSInteger status; // 狀態(tài)碼
@property(nonatomic,strong)NSString *message;
@property(nonatomic,strong)NSString *serverTime;
@property(nonatomic,strong)NSString *token;
@property(nonatomic,strong)id resultInfo;
@property(nonatomic,strong)NSData *jsonData;
@property(nonatomic,strong)NSMutableArray *dataArray;
@property(nonatomic,strong)NSString *dataTotal;
@end
#import "RequestResult.h"
@implementation RequestResult
@end
補充
上面我們在生成網(wǎng)絡(luò)連接對象時將公共參數(shù)部分的代碼省略了,它們應(yīng)該裝入到網(wǎng)絡(luò)請求的HTTP請求頭中。一般公共參數(shù)有token(登錄憑證)、UA(設(shè)備基本信息)、IMEI(設(shè)備的唯一標(biāo)示)等。
代碼補上,如下:
//用戶登錄token
_tokenString=[REDUserModel shareInstance].token;
if(_tokenString.length>0)
{
[_request setValue:_tokenString forHTTPHeaderField:@"x-auth-token"];
}
if ([PubicClassMethod iosAPPUA].length>0) {
//ua 設(shè)備的基本信息
[_request setValue:[PubicClassMethod iosAPPUA] forHTTPHeaderField:@"UA"];
}
if ([[UIDevice currentDevice] uniqueDeviceIdentifier].length>0) {
// 設(shè)備的唯一標(biāo)示
[_request setValue:[[UIDevice currentDevice] uniqueDeviceIdentifier] forHTTPHeaderField:@"IMEI"];
}
[_request setValue:@"ios" forHTTPHeaderField:@"_c"];
更新(2016.8.17)——APP的token用戶登錄狀態(tài)驗證機制
因為HTTP協(xié)議是“無狀態(tài)”協(xié)議,也就是它是無記憶,每次請求和響應(yīng)后是不保留信息的。就像在火車站售票窗口買票一樣,賣票的工作人員不可能記住你,等你下次再買時一眼便能認出你是老王,直接把票交給你。買票這個過程也是不保存狀態(tài)信息、無狀態(tài)的。下次你買票時,還是要和以往一樣進行質(zhì)詢對話,出示身份證表明身份等。但我們在實際應(yīng)用中,服務(wù)器對于請求得驗證權(quán)限,驗證是否是權(quán)限內(nèi)的用戶。所以說保存用戶狀態(tài)卻又是實實在在存在的需求。所以我們引進了Cookie技術(shù),用以保持狀態(tài),保存用戶信息。
但是在APP端,因為token使用更簡單,所以大多使用它來完成用戶狀態(tài)驗證。其道理是和Cookie一樣的。
其原理是:####
用戶首次登錄成功后,服務(wù)器會生成一個token值,保存在數(shù)據(jù)庫中,并返回給客戶端;
客戶端拿到這個token值后會保存在本地,作為登錄令牌,后續(xù)作為公共參數(shù)請求服務(wù)器;
請求到達服務(wù)器后,服務(wù)器拿公共參數(shù)里的token和數(shù)據(jù)庫里的作比較。若兩者相同,說明用戶以前登錄過且現(xiàn)在處于已登錄狀態(tài)(未過期)。若兩者不同,說明用戶的登錄狀態(tài)已失效,讓用戶重新登錄;
更多請閱讀: IOS 中使用token機制來驗證用戶的安全性
** 更新(2016-10-23)**
我已經(jīng)就這個話題寫了一篇總結(jié)筆記:token機制完成登錄狀態(tài)保持/身份認證