给AFN加点Cookie

作者: Zhang_yD | 来源:发表于2016-02-02 22:37 被阅读4102次
    2016.5.18 Update

    推翻之前繁琐的保存读取palapala一系列操作。
    事实证明,那些都没用....

    首先,Session是会过期的,所以存储再调用是没有任何意义的,更合适的做法是在获取到过期信息及时刷新Cookie。
    其次,Cookie的管理和加载是系统本身已经做好的,如果自行保存和加载,反而累赘,就是你需要在很多地方去考虑要不要保存,要不要清除。

    所以其实根本不需要把Cookie保存在本地。
    让系统自动在内存中管理就可以了。

    (一般性正常请求)

    如果需要向Cookie传参,也只需要在发送请求前拿到系统当前Cookie,set进去对应的参数就可以。

    以上...

    2016.5.16 Update

    做了一个Demo..
    说是Demo,其实内容都是一样的,不过是把下面的那些话放在了代码里面...
    好处是拿来即用。
    AFNCookieDemo(已失效)

    原文:

    为什么网络请求要用AFN?因为好用呗! ------沃·兹基硕德


    第一次写在简书上写文章,也是第一次使用markdown,所以写的很是粗糙。本来说写一下自己能看就行,不想过几天来发现被点了两个喜欢...顿时觉得不行不行,得重构一下文章...所以一边学习markdown语法一边又重新写了一下...


    我的AFN需要Cookie

    因为提供的接口是用Cookie来识别用户信息的,所以需要在每次请求的时候附带上指定的Cookie,以及在登陆后保存Cookie。

    刚开始做的时候也是百度了一番,从没有头绪到有一点头绪再到头绪不对重新找头绪经历了很多波折,不过最后还是顺利解决问题。这里添加记录以备不时之需。

    首先AFN自己是不管理Cookie的,它使用的是Foundation底层的Cookie管理工具。
    NSHTTPCookieStorage

    所有有关Cookie的设置和获取都可以通过这个类来实现。

    当我们在使用AFN发送请求之前,手动设置了这个Cookie,在发送请求的时候就会把这段自动加到请求头信息中。(我猜应该是每次发送请求都会自动带上Cookie)

    这里给出百度找到的两段方法,分别是存储获取到的Cookie和设置本地的Cookie(kCookie是一个字符串常量,作为保存/读取的Key值):

    NSData * cookiesData = [NSKeyedArchiver archivedDataWithRootObject: [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]];
    NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
    [defaults setObject: cookiesData forKey:kCookie];
    [defaults synchronize];
    

    上面这段代码的作用是保存当前Cookie到本地,我通常会在用户登录请求成功返回后调用这段代码将这次的Cookie进行保存,这样下次再发起其他的访问请求时候就可以取到这段Cookie。

    取Cookie的代码如下:

    NSArray * cookies = [NSKeyedUnarchiver unarchiveObjectWithData: [[NSUserDefaults standardUserDefaults] objectForKey:kCookie]];
    NSHTTPCookieStorage * cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    for (NSHTTPCookie * cookie in cookies){
        [cookieStorage setCookie: cookie];
    }
    

    上段的代码实现的就是获取到本地的Cookie,然后设置CookieStorage,这样在下载请求时候就会自动带上这段Cookie。我是在每次请求发送之前执行这段代码。

    事实上保存Cookie并不是仅仅在用户登录结束后执行的,比如在有图片验证码的时候,每次刷新图片验证码,也都是需要更新Cookie的(因为需要更新的Cookie里面的SessionID)。所以可以对外提供一个方法,这样在需要保存Cookie的时候调用方法就可以。

    至此Cookie问题解决,实测可以正常获取和传递SessionID等。(在我的项目中Cookie只是包含了SessionId,大家也可以根据需要往里面自由添加其他字段,毕竟Cookie已经取到了!)

    给WebView也扔一块Cookie

    产品设计跑过来扔跟我说小Z啊,你看我们这里有一个酷炫的动画!是不是hin帅气?我说是是是,他说是不是要加进去啊?我说加加加,他说好这是链接,干巴爹!拿到链接我想了想这不就是一个WebView的事嘛?so easy。一句代码搞定!

    NSString * urlStr = @"酷炫网页的链接";
    NSURL * url = [NSURL URLWithString:urlStr];
    NSURLRequest * request = [NSURLRequest requestWithURL:url];
    [self.webView loadRequest:request];
    

    好吧其实我把一句话代码拆成了四句,不过它依然简洁高效。点击,跳转,显示一切正常。于是我把它交给后台说搞定收工我去喝咖啡了。咖啡刚泡两分钟测试过来找我说,你这个链接要带着Cookie的,要不然我们怎么知道点进来的是哪个用户啊?我一脸懵逼,啊?

    最后我还是找到了给WebView加载链接加Cookie的方法,代码以及思路如下:

    首先取到要加载的Cookie。我这里就是之前写的那堆保存在本地的,一个NSArray类型的东西,kCookie是常量(好像是第二次说了),就是保存和读取的Key值。

    NSArray * cookies = [NSKeyedUnarchiver unarchiveObjectWithData: [[NSUserDefaults standardUserDefaults] objectForKey:kCookie]];
    

    然后启封NSHTTPCookieStorage,它有一个方法可以给指定URL加Cookie,大概是长下面这样:

    NSHTTPCookieStorage * cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    [cookieStorage setCookies:cookies forURL:url mainDocumentURL:nil];
    

    这里面有两个参数可以设置,cookies当然就是我们刚才取到的Cookie数组,url就是要加上Cookie的指定url,对我来说就是上面的@"酷炫网页的链接"。参数设置完毕,其他的就没问题啦!(什么?mainDocumentURL?这啥?不知道!填nil就可以了!)

    所以四行简洁的代码就变成了下面这样:

    NSString * urlStr = @"酷炫网页的链接";
    NSURL * url = [NSURL URLWithString:urlStr];
    NSArray * cookies = [NSKeyedUnarchiver unarchiveObjectWithData: [[NSUserDefaults standardUserDefaults] objectForKey:kCookie]];
    NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
    [cookieStorage setCookies:cookies forURL:url mainDocumentURL:nil];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    [self.webView loadRequest:request];
    

    看上去又多了不少行,不过总算是实现了给WebView传Cookie的任务。

    再次提交,希望我的咖啡还没有凉。

    相关文章

      网友评论

      • 大大盆子:有帮助 很棒!
      • LeiovU:楼楼你好!如何把SESSIONID塞到cookie里?
        LeiovU:@Zhang_yD: 谢谢,已经解决了
        Zhang_yD:@LeiovU 获取和设置都在一个类下管理的 怎么取到的 就怎么设置被.,...
      • JoyceZhao:请问 在有图片验证码的时候 cookie 是怎样获取和设置呢?
        JoyceZhao:@Zhang_yD 直接修改作者源码?
        Zhang_yD:@JoyceZhao 图片验证码一般是用SDWebImage请求,如果你也是用这个库,那很方便的,直接设置请求策略就可以。
      • 加嘞个油seek:大哥我的项目里有一些问题想请教你 能不能加我一下467601991
      • MakeThatChange:cookie只需要存起来就好了吧,不需要取,是不是请求需要会自己去找~
        Zhang_yD:@MakeThatChange 如果你的请求只是用到cookie的有效期这一个参数,那其实不需要保存本地的,系统会自行保存在内存中并在每次提交请求的时候加上。所以存取其实都不需要自己操作。
      • 智能居家: :+1: 期待demo
      • FredYJH:大哥,你讲了这么多,我还是看不懂,还不如来个DEMO实际点。网上讲的都差不多,关键是我不知道运行的顺序,和运行要走哪些步骤,怎么保存,怎么取值。如果有个DEMO,我可以慢慢调,求DEMO :smile:
        Zhang_yD:@FredYJH 我没有用到需要setCookie的场景,只是用cookie存储一个用户状态。而这个状态会自动存储,所以...实际上我的项目里面没有任何对cookie进行操作的代码。所以真让我写个demo,反而不知道该怎么写....
      • 萧城x:有demo吗
        萧城x:好的 谢谢
        Zhang_yD:@低调做事 其实不需要写demo的,如果我真的写了一个demo发出来,你会发现在代码里面没有任何和cookie相关的语句,因为所有cookie相关的操作都由系统来处理了。
        如果真的要对cookie做处理,比如在发送请求的时候需要额外向cookie里面传值,或者需要对一个webView加载的url单独制定Cookie,或者对sd应用cookie(sd应用方案见上一个评论回复),基本都是在请求发送之前,取到cookie(取cookie见上一个回复)再配置就可以。
      • 萧城x:如何让 sd 和afn的cooki 一致
        Zhang_yD:@低调做事 不需要修改库文件....他是有对外方法的...[self.imageView sd_setImageWithURL:url placeholderImage:nil options:SDWebImageHandleCookies completed:nil]; 用这个方法就可以。
        萧城x:我不太明白
        1. 我在afn success block 之后 调用 你的取cookie 的代码 取到是空;
        2.如何让 afn 请求 的时候 带着 cookie 写在header 里面吗 对应的key值是什么
        3. sd里面的 .m 文件是这样
        我注释了一句 这样做事对的吗
        // request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies);
        request.HTTPShouldUsePipelining = YES;
        Zhang_yD:@低调做事 调用sd的时候设置参数 handleCookie就可以
      • 炘瀚Alex:大神,cookie本地存储是没问题,每次请求时也获取了本地cookie,也有值,但是感觉afn发送请求的时候没有自动获取啊?是不是还需要什么代码来设置啊,比如说将它设置为请求头?
        Zhang_yD:@炘瀚Alex 5.18的更新我把Demo去掉了...
        之前的想法是把登录后获取到的Cookie持久化保存在本地,但是后来实验证明这样虽然可以实现却显得繁琐。
        如果你没有对AFN的模式(比如某个参数有type或者某个参数有handlecookie等字样)修改,那他默认是会带着的。
        我现在的请求里面操作的只有manager.responseSerializer.acceptableContentTypes来控制请求头类型,其他的都使用的AFN的默认配置。
        正常调用Cookie自动会加上。
        如果你需要额外给Cookie里面传值,那可能需要在发送请求之前先获取到类似[NSHTTPCookieStorage sharedHTTPCookieStorage]这个,然后进行set传值。
        炘瀚Alex:我在网上查资料,也都说一般在请求前获取下本地设置的cookie就可以了,难道是获取本地cookie的步骤放置的位置不对?一般我是在发送请求前先获取一遍;也有人说需要添加[manager.requestSerializer setValue:[cookie value] forHTTPHeaderField:@"Cookie"],但好像也没什么效果。。。大神,我看上面,5月的时候你不是说要写个demo吗?话说进行到哪步了?求学习! :smile: :blush:
        Zhang_yD:@炘瀚Alex 理论上什么都不需要设置才对.... 用AFHTTPSessionManager调用请求最后调用的还是系统的NSURLSession相关的方法。所以系统会在调用后自动加上内存中保存的cookie。我们需要做的就是在请求前对内存中的cookie做修改或者添加参数操作就行。
      • 1a392df793c3:+ (instancetype)shareHttpMangager {
        static CPAFHTTPSessionManager *_sharedHttpManager = nil;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
        NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
        _sharedHttpManager = [[CPAFHTTPSessionManager alloc] initWithSessionConfiguration:configuration];
        _sharedHttpManager.requestSerializer = [AFHTTPRequestSerializer serializer];
        _sharedHttpManager.responseSerializer = [AFHTTPResponseSerializer serializer];
        _sharedHttpManager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript",@"text/html", @"text/plain", @"image/jpeg", nil];
        _sharedHttpManager.requestSerializer.HTTPShouldHandleCookies = true;


        });
        return _sharedHttpManager;
        }
        + (void)postWithUrlString:(NSString *)url parameter:(NSDictionary *)param progressBlock:(void (^)(NSProgress *))progress successBlock:(void (^)(id))success failureBlock:(void (^)(NSError *))failure{

        [[CPAFHTTPSessionManager shareHttpMangager] POST:url parameters:param progress:^(NSProgress * _Nonnull uploadProgress) {
        if (progress) {
        progress(uploadProgress);
        }
        } success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
        if (success) {
        if ([url isEqualToString:URL_LOGIN]) {
        NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
        NSLog(@"%@", cookies);
        NSHTTPCookie *cookie;
        for (cookie in cookies) {
        [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
        }
        }
        NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingMutableContainers error:nil];
        success(dic);
        每次调用登录接口时我就保存cookie,但是过一分
        Zhang_yD:@cmcupid 你可以看一下是cookie里面的sessionid失效了还是你的cookie被清空了。
        1a392df793c3:@cmcupid 过一分钟再操作就失效了
      • 漂泊的煙烣:请问一下,AFN3.x同步请求怎么用?
        Zhang_yD:@漂泊的煙烣 一说同步第一个就想到的是GCD...
      • 丷有归: :blush: 大神,项目中获取cookie,是只需要在登录的时候获取一次保存本地?然后每次调取接口之前吧cookie取出?这样cookie已经传到数据的header吗?
        Zhang_yD:@丷有归 默认系统会帮你处理这个事情的... 比如你的某次请求中cookie里面的session变化了,系统会自动保存这个cookie到内存,然后在下次请求发送出去。所以理论上不需要任何自行保存。在session过期的时候,服务器一般会传回一个规定好的异常code,捕获到这个code,我们重新刷新cookie就行。
      • Fatm:可以写一个demo吗,这样就更好懂了,例如一个登录时获取cookie,以后调接口如何带上这个cookie,过期了如何更新💪😊💪
        诗雯:@Zhang_yD 求问什么时候写 :sob: :confounded: 一直用不明白呀~`
        Zhang_yD:@Fatm 好, 那我抽空写一个Demo更新上来...
      • 挂着铃铛的兔:我正在做验证码的获取,后台说要用到cookie, cookie有个参数api_captcha, 我看了你上面的两个方法, 但不知道具体该怎么做
        Zhang_yD:@挂着铃铛的兔 空? :flushed: 我这边是可以收到的 - - 里面是name 和value组成,不过我的里面是只有JESSIONID....
        挂着铃铛的兔:@Zhang_yD 这个数组为空....
        Zhang_yD:@挂着铃铛的兔 当请求数据并成功回调后,[[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies] 这个方法是可以取到请求的Cookie的,打印这个数组,应该是可以看到你想要的参数.....
      • Janice小白:我怎么设置了,cookie里面还是只有SessionId :sob:
        Zhang_yD:@d464388232f9 ...如果只是需要请求权限,那sessionId就足够了。如果是需要主动用Cookie传递参数,可能需要再去查查NSHTTPCookieStorage的设值方法。
      • 来宝:大神,我cookie设置是成功了,但是会有过期的问题,如何解决?我尝试了[cookieProperties setValue:[NSDate dateWithTimeIntervalSinceNow:60*60*24*30] forKey:NSHTTPCookieExpires];设置过期时间也没用
        Zhang_yD:@来宝 Cookie过期一般是里面的SessionID过期...我的解决方法就是重新提交登录请求来刷新Cookie。可以和服务端协调,发送请求如果遇到过期返回指定错误Code,对Code解析,如果是过期就用本地存储的用户名密码重新发送登录请求。
      • b5f05d9357e2: let cookie = NSHTTPCookie(properties: [
        NSHTTPCookieName:"username",
        NSHTTPCookieValue:"test",
        NSHTTPCookiePath:"/",
        NSHTTPCookieDomain:"http://ycms.test.com",
        ]);

        print("set cookie \(cookie)");
        NSHTTPCookieStorage.sharedHTTPCookieStorage().setCookies([cookie!], forURL: NSURL(string: "http://ycms.test.com")!, mainDocumentURL: nil)


        Http.shareInstance.request(.GET, URLString: "http://ycms.test.com/test", parameters: nil) { (result, error) in
        print("ddd :\(result)");
        }

        我设置了cookie,用afn请求,但服务端返回的cookie中没有我这个。。
        Zhang_yD:@袁克强 我的工程里面Cookie主要还是作为请求的有效性来使用的。并没有遇到需要传参的情况...不过我觉得设置参数应该是同理的。
        你可以尝试本地打印你设置好的Cookie和Url,然后找到服务端的同事那里看看发送过去后的Cookie是否一致。
      • 8ae158dda3f2:写得很好,跟我们的需求一样,原来只要设置cookie,AFN会自动带上,跟我猜的不一样呢,谢谢啦
        加嘞个油seek:@codernevinz cookie是怎么设置的?
        Zhang_yD:@_NevinZ 能帮到你我也很高兴~

      本文标题:给AFN加点Cookie

      本文链接:https://www.haomeiwen.com/subject/qcqekttx.html