项目干货挖掘3——怎么去封装一个网络层?

前言

我之前已经写过一篇有关网络层的笔记:关于网络层的设计(一)——和业务层的对接。在这篇笔记中主要解释了网络层设计时所要考虑的因素。给出的代码例子是以block回调的,以AFNetworking请求网络的,而且结构是离散型的。
那本篇我们选择处处与之相对立,即以delegate回调给业务层数据,自己封装NSURLConnection来请求网络,结构采用集约型,而且更全面细致:加入了网络超时的处理,请求队列的控制,完善的公共参数等。


** HomeViewController.m **

这是业务层的代码。业务层所有的网络请求都通过ConnectManager里的该方法完成,这就是所谓集约型结构,所有的业务请求均由一个方法完成,这样的话开发效率可能会更高,毕竟只使用这个方法嘛。
而且数据从网络层交付到业务层是通过delegate回调方法的形式,这样的话若在同一个ViewController有多个不同接口的网络请求,则需要在代理方法里判断接口requestInterface,不同接口分别做不同处理。这也是和block回调不同的地方,block方式是每个接口都对应一个block回调,而delegate则只有一个代理方法,所以需要在代理方法里带上requestInterface用以判别区分出不同的接口。

#import "HomeViewController.h"
#import "ConnectManager.h"


@interface HomeViewController ()

@end

@implementation HomeViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    NSDictionary *dict = @{@"stuId":@"666"};
    // 业务层所有网络请求,均通过该方法,是为集约型。
    [[ConnectManager shareConnectManager] sendRequestWithID:RequestInterface_StudentList
                                                 parameters:dict
                                           callbackDelegate:self
                                                   httpType:GET
                                           removeAllRequest:NO];   
}

// 通过delegate将数据从网络层交付给业务层
- (void)connectManagerCallback:(RequestInterface)requestInterface requestResult:(RequestResult *)result
{
    if(requestInterface == RequestInterface_StudentList)
    {
        if(result.status == 200){
//            NSArray *stuList = result.resultInfo;
        }
    }
}


@end

** ConnectManager.h **

可以看到我们声明了生成实例的单例方法,并声明了那个给业务层调用请求网络的方法。我们还定义了ConnectManagerDelegate协议,业务层的VC为其代理者,即上面代码看到的实现。
除此外,我们还定义了两个枚举变量,第一个是请求方式HttpType,第二个是代表接口的枚举变量,所有的接口都在此定义成枚举变量。业务层传入的接口参数便为接口的枚举变量。

#import <Foundation/Foundation.h>
#import "RequestResult.h"



// 请求方式
typedef NS_ENUM(NSInteger, HttpType)
{
    GET = 0,
    POST,
};



// 所有的接口定义成枚举变量
typedef NS_ENUM (NSInteger,RequestInterface)
{
    RequestInterface_StudentList      = 0,  // 学生列表
    RequestInterface_StudentDetail,         // 学生详情
};



// 定义ConnectManagerDelegate协议
@protocol ConnectManagerDelegate <NSObject>

- (void)connectManagerCallback:(RequestInterface)requestInterface requestResult:(RequestResult *)result;

@end





@interface ConnectManager : NSObject

@property (nonatomic, weak)id<ConnectManagerDelegate>  managerDelegate; // 回调给业务层的代理


+ (ConnectManager *)shareConnectManager;


- (void)sendRequestWithID:(RequestInterface )requestInterface
               parameters:(NSDictionary *)parama
         callbackDelegate:(id)delegate
                 httpType:(HttpType)httpType
         removeAllRequest:(BOOL)isRemove;

@end

** ConnectManager.m **

我们可以看到该类主要完成了请求队列的控制,可以控制在将当前请求加入请求队列时,是否清空请求队列中的所有请求,或者只是从请求队列中把旧的该请求清除。
通过调用HTTPConnect的方法,生成网络连接的对象,并开始连接(等下看HTTPConnect类的详情)。
而且它作为HTTPConnect的代理,实现了HTTPConnectDelegate协议的方法。并将数据转换成了RequestResult,并调用了ConnectManagerDelegate协议的方法,将其回调给业务层。

** 注意接口返回格式的约定:**这里需要多说几句的是。项目开始前,后台会在接口文档中写明接口统一返回格式。所有的接口都遵循这个格式。比如,我们当前的项目,后台在接口文档中就给出了“接口返回格式”:返回的json数据最外层有hb两个key,分别意为headbody,在head的value又是一个字典,分别有code,msg,time三个key,分别代表错误码信息提示,服务器时间。而在body里则是真正需要的数据。
根据这个约定,我们创建了RequestResultmodel对象用以封装返回数据(后面给出了RequestResult的代码)。

** 对于Null值的处理:**若后台返回的某个字段是Null,表示该字段为空,这样直接使用既不友好,也比较危险。不友好之其一为若要显示在UI上便会显示为<null>,其二为在判断该字段是否为空的地方得要这样:if([awarder.awarderId isKindOfClass:[NSNull class]]),比较麻烦。危险之处是在OC中若字典以这样的方式@{@"key1":@"value1",@"key2":@"value2"};建立,此时若value1为空,程序执行到此直接会崩掉。
所以我们需要对后台返回的json数据都要做Null值处理。我们可以在预编译文件中定义一个宏,若值为NSNull则将其置为@""

#define DealWithJSONStringValue(_JSONVALUE)    (_JSONVALUE != [NSNull null] && _JSONVALUE!=nil) ?[NSString stringWithFormat:@"%@",_JSONVALUE]:@""
屏幕快照 2016-07-18 下午1.37.12.png
#import "ConnectManager.h"
#import "HTTPConnect.h"
#import "RequestResult.h"


#define ServerAddress @"http://www.runedu.test/api" // 服务器地址
#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{
        // 移除当前接口某连接
        [self clearConnectOfTheInterface:(requestInterface)];
    }
    
    _managerDelegate = delegate;
    
    // 2.根据枚举把接口转换为相应的接口字符串,并拼接完整URL.
    NSString *urlStr = [self urlStringWithRequestInterface:requestInterface];
    
    // 3.传入接口地址,URL,参数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];
}

// 清除当前某接口的连接
- (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.先根据枚举变量找出接口对应的字符串
    NSString *interfaceStr = @"";
    switch (interface)
    {
        case RequestInterface_StudentList:
        {
            interfaceStr = @"student/list";
            break;
        }
        case RequestInterface_StudentDetail:
        {
            interfaceStr = @"student/detail";
            break;
        }
    }
    
    // 2.再把服务器地址、版本号、接口地址拼接成完整的URL
    NSString *urlStr = @"";
    if((int)interface<1000){ // 学生接口
        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"]);
        }
    }
    
    // 代理回调。在每个VC中实现,便可取得数据。
    if(self.managerDelegate && [self.managerDelegate respondsToSelector:@selector(connectManagerCallback:requestResult:)])
    {
        [self.managerDelegate connectManagerCallback:httpConnect.requestInterface requestResult:result];
    }
    
    httpConnect = nil;
}


// 正在下载数据
- (void)httpConnectIsDownloading:(HTTPConnect *)httpConnect progress:(float)progress
{
    
}


// 请求失败
- (void)httpConnectDownloadFinish:(HTTPConnect *)httpConnect failure:(NSError *)error
{
    RequestResult *result = [[RequestResult alloc] init];
    result.status = -10;
    result.message = @"网络不给力";

    if(self.managerDelegate && [self.managerDelegate respondsToSelector:@selector(connectManagerCallback:requestResult:)])
    {
        [self.managerDelegate connectManagerCallback:httpConnect.requestInterface requestResult:result];
    }
    
    httpConnect = nil;
}


// 请求无响应
- (void)httpConnectDownloadFinish:(HTTPConnect *)httpConnect stautsCode:(NSInteger)code
{
    RequestResult *result = [[RequestResult alloc] init];
    result.status = -11;
    result.message = @"服务器无响应";
    
    if(self.managerDelegate && [self.managerDelegate respondsToSelector:@selector(connectManagerCallback:requestResult:)])
    {
        [self.managerDelegate connectManagerCallback:httpConnect.requestInterface requestResult:result];
    }
    
    httpConnect = nil;

}


@end

** HTTPConnect.h **

可以看到HTTPConnect是完成网络连接和数据请求及响应的核心。在此,我们创建网络连接的对象,并控制开始连接和断开连接。
值得注意的是HTTPConnect是继承自NSOperation的。一个网络连接的对象便是一个“操作”,每个连接对象都会加入到ConnectManager的请求队列_operationQueue中。
我们还定义了很多HTTPConnect的属性,主要为网络响应返回的数据,之所以作为其公开的属性,是因为要回调给ConnectManager,所以得暴露为公开的属性。
除此外,我们还定义了HTTPConnectDelegate协议。

#import <Foundation/Foundation.h>
#import "ConnectManager.h"

@class HTTPConnect;
@protocol HTTPConnectDelegate <NSObject> // 定义HTTPConnectDelegate协议

// 请求成功
- (void)httpConnectDownloadFinish:(HTTPConnect *)httpConnect;


// 正在下载数据
- (void)httpConnectIsDownloading:(HTTPConnect *)httpConnect progress:(float)progress;


// 请求失败
- (void)httpConnectFailure:(HTTPConnect *)httpConnect failure:(NSError *)error;


// 请求无响应
- (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 **

首先我们在初始化方法里,初始化了一些变量,并拼接了各种参数,生成了网络连接对象_urlConnection。然后实现了startConnectcancelConnect方法。值得注意的是我们加入了网络请求超时的控制,若开始连接后,若计时器撑到了30s还没有被销毁,说明网络请求超时了。
该类是网络连接的核心,我们实现了NSURLConnectionDataDelegate协议的方法,在该协议的方法里,网络数据主要在这些协议方法里进行响应,这是重点。我们对响应的数据进行了一些处理,并赋值给HTTPConnect的属性,然后调用HTTPConnectDelegate协议的方法,回调给ConnectManager,注意,这些协议的方法均有参数httpConnect对象,这样便把其属性,即网络响应返回的数据回调给了ConnectManager;而在ConnectManager中上面我们已经说了,会把数据转换为RequestResultConnectManager再调用其对应的协议方法,便将RequestResult形式的数据回调给业务层。
这样就完成了网络响应的数据从NSURLConnectionDataDelegate的实现方法到HTTPConnectDelegate的实现方法,再到ConnectManagerDelegate的实现方法。即网络数据从HTTPConnectConnectManager,再到HomeViewController
嗯,整个流程就是这样。

#import "HTTPConnect.h"


@interface HTTPConnect ()
{
    NSMutableURLRequest         *_urlRequest;
    NSURLConnection             *_urlConnection;
    NSHTTPURLResponse           *_httpResponse;
    
    
    BOOL                         _isConnect;   // 是否已连接
    RequestInterface             _requestInterface; // 请求接口
    
    NSTimer                     *_connectTimer; // 定时器,用来判断请求超时
    BOOL                         _isTimerFired; // 请求是否超时
    
    
    NSInteger                    _dataSize;//总数据的大小
    NSInteger                    _received;//每节点数据的大小
    
}
@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]; // 生成参数字符串(url?stuId=12&stuName=wang)
        
        // 1.根据GET和POST的不同,把参数装入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]; // 把参数字符串编码为二进制流
            if(paramData.length>0){
                _urlRequest.HTTPBody = paramData;
            }
        }
        
        // 2.设置HTTP请求的一些请求头信息
        // 省略...
        
        // 3. 生成URL连接
        _urlConnection = [[NSURLConnection alloc] initWithRequest:_urlRequest delegate:self];
    }
    
    
    return self;
}




#pragma mark ---- 提供给外部的功能方法

// 开始连接,并开始连接计时,若超过30秒还没被关掉说明请求超时了。
- (void)startConnect
{
    
    if(_isConnect){
        return;
    }
    _isConnect = YES;
    
    if(_urlConnection){

        [_urlConnection start];  // 真真切切地进行网络连接
        _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;
    }
    
    // 回调给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]; // 总数据
        _tokenString = httpResponseHeaderFields[@"x-auth-token"]; // 从响应头里获得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];
}



// 把参数字典转换为字符串
- (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; //计算出进度
    // 回调给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; // 状态码
@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


补充

上面我们在生成网络连接对象时将公共参数部分的代码省略了,它们应该装入到网络请求的HTTP请求头中。一般公共参数有token(登录凭证)、UA(设备基本信息)、IMEI(设备的唯一标示)等。
代码补上,如下:

        //用户登录token
        _tokenString=[REDUserModel shareInstance].token;
        if(_tokenString.length>0)
        {
            [_request setValue:_tokenString forHTTPHeaderField:@"x-auth-token"];
        }
        
        if ([PubicClassMethod iosAPPUA].length>0) {
            //ua 设备的基本信息
            [_request setValue:[PubicClassMethod iosAPPUA] forHTTPHeaderField:@"UA"];
        }
        
        if ([[UIDevice currentDevice] uniqueDeviceIdentifier].length>0) {
            // 设备的唯一标示
            [_request setValue:[[UIDevice currentDevice] uniqueDeviceIdentifier] forHTTPHeaderField:@"IMEI"];
        }
        
        [_request setValue:@"ios" forHTTPHeaderField:@"_c"];

更新(2016.8.17)——APP的token用户登录状态验证机制

因为HTTP协议是“无状态”协议,也就是它是无记忆,每次请求和响应后是不保留信息的。就像在火车站售票窗口买票一样,卖票的工作人员不可能记住你,等你下次再买时一眼便能认出你是老王,直接把票交给你。买票这个过程也是不保存状态信息、无状态的。下次你买票时,还是要和以往一样进行质询对话,出示身份证表明身份等。但我们在实际应用中,服务器对于请求得验证权限,验证是否是权限内的用户。所以说保存用户状态却又是实实在在存在的需求。所以我们引进了Cookie技术,用以保持状态,保存用户信息。
但是在APP端,因为token使用更简单,所以大多使用它来完成用户状态验证。其道理是和Cookie一样的。

其原理是:####

用户首次登录成功后,服务器会生成一个token值,保存在数据库中,并返回给客户端;
客户端拿到这个token值后会保存在本地,作为登录令牌,后续作为公共参数请求服务器;
请求到达服务器后,服务器拿公共参数里的token和数据库里的作比较。若两者相同,说明用户以前登录过且现在处于已登录状态(未过期)。若两者不同,说明用户的登录状态已失效,让用户重新登录;
更多请阅读: IOS 中使用token机制来验证用户的安全性

** 更新(2016-10-23)**
我已经就这个话题写了一篇总结笔记:token机制完成登录状态保持/身份认证

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,718评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,683评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,207评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,755评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,862评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,050评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,136评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,882评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,330评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,651评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,789评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,477评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,135评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,864评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,099评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,598评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,697评论 2 351

推荐阅读更多精彩内容

  • iOS网络架构讨论梳理整理中。。。 其实如果没有APIManager这一层是没法使用delegate的,毕竟多个单...
    yhtang阅读 5,174评论 1 23
  • AFHTTPRequestOperationManager 网络传输协议UDP、TCP、Http、Socket、X...
    Carden阅读 4,333评论 0 12
  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 10,934评论 6 13
  • 13.服务器图片改了,url没有变,需求,服务器的图片变了,就从服务器加载图片,如果服务器的没变,就从本地加载 1...
    AlanGe阅读 1,142评论 0 1
  • 在“你好阅读”,我得到了一本书《眼》,这是一本绘本书,但它又不同于其他的绘本,翻开第一页,你就会看到一双大眼睛望着...
    RuT阅读 490评论 3 7