iOS網絡請求之ASI與AFN的二次封裝及意義

引言

iOS9.0之后,NSURLConnection被蘋果拋棄,該來的即便是遲些,但最終肯定會來。對于迷戀NSURLConnection還要適配iOS低版本的公司,就不得不三思了,當然,使用NSURLSession也是不錯的,但面對成熟的ASI和AFN,我們更有理由選擇。

正題

開始看到這個題目,很多人會有疑問:ASI和AFN都是很成熟的三方庫,為什么要再次封裝呢?
對此,我的理解是,對于咨訊、新聞類APP,或許不需要,它們主要以刷流量為主,直接使用(很適合我們抓包拿來練手)。但對于安全性要求較高的APP而言,它們需要的是有效流量,這時一般要對自己的URL進行加密,比如:“支付”類、會員視頻點播等。如果URL加密的話,對ASI或AFN二次封裝就要提上日程啦,當然這也是對其進行二次封裝的作用及意義所在。

URL加密

我們采用方式比較簡易,在URL的head中,添加動態token及User-Agent添加des加密字符的方式等,起到加密效果。

引入ASI和AFN SDK(最終項目選其一)

本人使用CocoaPods三方庫管理工具

platform :ios,’7.0’
target ‘MMBao_master’ do

pod 'ASIHTTPRequest'
pod 'AFNetworking', '~> 3.0'
......
end

一、ASIHTTPRequest再封裝

ASIHTTPRequest GitHub鏈接

1、ASIHTTPRequest異步請求及ASIHTTPRequestDelegate簡析

ASI是iOS知名度最高的網絡請求三方庫,由于它的實現是基于底層的CFNetwork框架,運行效率較其他網絡請求庫都要高出一籌。
ASIHTTPRequest異步請求

(1)創建網絡請求

NSURL *url = [NSURL URLWithString:@"http://192.168.1.111:8080/ProjectName/getToken];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url]; 
request.timeOutSeconds = 30; // 超時設置

(2)統一為請求添加head,如:Date 提供日期和時間標志、Content-Length請求內容的長度、Content-Type請求與實體對應的MIME信息等;或者添加自定義header的類型,比如特定用戶標記信息,加密信息token等。

[request addRequestHeader:@"phone_type" value:@"iOS"];
[manager.requestSerializer setValue:tokenDes forHTTPHeaderField:@"token"];

......
(3)設置ASIHTTPRequestDelegate代理

request.delegate = self;  

(4)發送異步請求

[request startAsynchronous]; 

ASIHTTPRequestDelegate簡析

(1)網絡請求start
- (void)requestStarted:(ASIHTTPRequest *)request
(2)開始接收到服務器的數據
- (void)request:(ASIHTTPRequest *)request didReceiveData:(NSData *)data
(3)請求成功完畢
- (void)requestFinished:(ASIHTTPRequest *)request
(4)請求失敗
- (void)requestFailed:(ASIHTTPRequest *)request
(5)取消請求,切記

[request clearDelegatesAndCancel];
2、EVNASIConnectionUtil工具類實現

EVNASIConnectionUtil.h

//
//  EVNASIConnectionUtil.h
//  MMBao_master
//
//  Created by developer on 16/8/24.
//  Copyright ? 2016年 仁伯安. All rights reserved.
//

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

#pragma mark - 請求方法
typedef NS_ENUM(NSUInteger, ASIURLMethod)
{
    ASIPOST = 1,
    ASIGET = 2,
};

#pragma mark - TAG定義,命名規則: URL開頭+模塊名字+Tag
typedef NS_ENUM(NSUInteger, ASIURLTag)
{
#pragma mark: 賬戶管理
    ASIURLGetTokenTag,
#pragma mark - 登錄及注冊
    
#pragma mark:  三方登錄

#pragma mark - 分類及熱詞搜索

#pragma mark - 買家模塊
};

#pragma mark - 返回的Result
typedef NS_ENUM(NSUInteger, ASIResultCode)
{
    ASIRS_Success = 1,
    ASIRS_Error,
};

@protocol ASIHTTPConnectionDelegate <NSObject>
@optional

@required
/**
 *  網絡請求完成(包括數據返回成功、失敗、接口超時)后的回調方法
 *
 *  @param dicRespon     網絡請求后返回的JSON數據
 *  @param URLTag        網絡請求的標識tag
 *  @param theResultCode 狀態碼,成功 && 失敗
 *  @param res           網絡請求返回的result,一般1表示請求成功,0表示請求失敗
 *  @param message       提示信息
 *  @param failStatus    是否顯示網絡請求異常圖片
 */
- (void)resultWithDic:(NSDictionary *)dicRespon urlTag:(ASIURLTag)URLTag isSuccess:(ASIResultCode)theResultCode Result:(int) res Message:(NSString *) message failImage:(int) failStatus;

@optional
- (void) resultWithString:(NSString *) str;

@end

#pragma mark - EVNASIConnectionUtil工具類
@interface EVNASIConnectionUtil : NSObject<ASIHTTPRequestDelegate>
{
    NSMutableData *_dtReviceData;
    int FailStatus;
}

@property (nonatomic, assign) id<ASIHTTPConnectionDelegate> delegate;
@property (nonatomic, assign) ASIURLTag urlTag;
@property (nonatomic, strong) ASIHTTPRequest *conn;


/**
 *  網絡請求初始化方法
 *
 *  @param theUrlTag   網絡請求的標識tag
 *  @param theDelegate 網絡請求代理
 *
 *  @return ASIHTTP實例
 */
- (instancetype)initWithURLTag:(ASIURLTag)theUrlTag delegate:(id<ASIHTTPConnectionDelegate>)theDelegate;


/**
 *  common 網絡請求
 *
 *  @param strUrl      URL服務器地址
 *  @param strPostBody body
 *  @param theMethod   網絡請求方式,比如GET、POST
 */
- (void)getResultFromUrlString:(NSString *)strUrl postBody:(NSString *)strPostBody method:(ASIURLMethod)theMethod;


/**
 *  上傳圖片音頻 網絡請求
 *
 *  @param strUrl           URL服務器地址
 *  @param dicText          需要的參數,dicImage表示,strImageFileName表示
 *  @param dicImage         圖片名字
 *  @param strImageFileName 服務器端文件的目錄名
 *  @param avFile           音頻類網絡請求文件名
 */
- (void)getResultFromUrlString:(NSString *)strUrl dicText:(NSDictionary *)dicText dicImage:(NSDictionary *)dicImage imageFilename:(NSMutableArray *)strImageFileName WithAVFile:(NSString *) avFile;

/**
 *  網絡請求結束,終止
 */
- (void)stopConnection;

#pragma mark -
- (void)getResultFromUrlString:(NSString *)strUrl postBody:(NSMutableArray *)strPostBodyArray postArg:(NSMutableArray *) argArray method:(ASIURLMethod)theMethod failImage:(int) faileSatatus;

@end

EVNASIConnectionUtil.m

//
//  EVNASIConnectionUtil.m
//  MMBao_master
//
//  Created by developer on 16/8/24.
//  Copyright ? 2016年 仁伯安. All rights reserved.
//

#import "EVNASIConnectionUtil.h"
#import "AppDelegate.h"
#import "MCdes.h"

#import <AVFoundation/AVFoundation.h>
#define KEYPassWordMCdes @"加密字符"

@implementation EVNASIConnectionUtil
{
    AppDelegate *appDel;
    NSString *tokenDes;
}

- (instancetype)initWithURLTag:(ASIURLTag)theUrlTag delegate:(id<ASIHTTPConnectionDelegate>)theDelegate
{
    self = [super init];
    if (self)
    {
        self.delegate = theDelegate;
        self.urlTag = theUrlTag;

        appDel = (AppDelegate *)[UIApplication sharedApplication].delegate;

        tokenDes = appDel.appToken;

        if([self validateString:tokenDes] == NO)
        {
            tokenDes = @"";
        }
    }

    return self;
}
// URL編碼
- (NSString *)encodeToPercentEscapeString: (NSString *) input
{
    NSString *outputStr = (NSString *)     CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,  (CFStringRef)input, NULL,  (CFStringRef)@"!*'();:@=+$,/?%#[]", kCFStringEncodingUTF8));
    return outputStr;
}

- (void)getResultFromUrlString:(NSString *)strUrl postBody:(NSMutableArray *)strPostBodyArray postArg:(NSMutableArray *)argArray method:(ASIURLMethod)theMethod failImage:(int)faileSatatus
{
    FailStatus = faileSatatus;
    strUrl = [strUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    NSMutableString *strPostBody = [[NSMutableString alloc] initWithString:@""];
    for(NSInteger i=0;i<argArray.count;i++)
    {
        NSString *bodyStr = [[strPostBodyArray objectAtIndex:i] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

        NSString *argStr = [self encodeToPercentEscapeString:[NSString stringWithFormat:@"%@",[argArray objectAtIndex:i]]];

        NSString *s = [NSString stringWithFormat:@"%@%@",bodyStr,argStr];

        [strPostBody appendString:s];
    }

    NSString *urlStr;
    if([self validateString:strPostBody] == NO)
    {
        urlStr = [NSString stringWithFormat:@"%@",strUrl];
    }
    else
    {
        urlStr = [NSString stringWithFormat:@"%@%@",strUrl,strPostBody];

    }
    NSLog(@"請求接口:%@",urlStr);
    ASIHTTPRequest *request = [[ASIHTTPRequest alloc]initWithURL:[NSURL URLWithString:urlStr]];
    [request setValidatesSecureCertificate:NO];//請求https的時候,需要設置該屬性
    request.timeOutSeconds = 30;
    //設置傳送類型
    NSString *contentType = [NSString stringWithFormat:@"application/x-www-form-urlencoded"];
    //給傳輸類型添加內容
    [request addRequestHeader:@"Content-Type" value:contentType];
#pragma mark - 修改header頭
    NSString *dataDes = [MCdes encryptUseDES:@"需要加密的字符" key:KEYPassWordMCdes];
    [request addRequestHeader:@"User-Agent" value:dataDes];
    [request addRequestHeader:@"token" value:tokenDes];

......
    [request addRequestHeader:@"phone_type" value:@"應用縮寫_iOS"];

    if (theMethod == ASIGET)
    {
        [request setRequestMethod:@"GET"];
    }
    else
    {
        [request setRequestMethod:@"POST"];
    }

    [[UIApplication sharedApplication]setNetworkActivityIndicatorVisible:YES];
    request.delegate = self;
    self.conn = request;

    [self.conn startAsynchronous];
}

#pragma mark - 異步請求數據,strUrl表示域名,strPostBody表示上傳的參數,theMethod表示請求方式
- (void)getResultFromUrlString:(NSString *)strUrl postBody:(NSString *)strPostBody method:(ASIURLMethod)theMethod
{

    strUrl = [strUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

    strPostBody = [strPostBody stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

    NSString *urlStr;
    if([self validateString:strPostBody] == NO)
    {
        urlStr = [NSString stringWithFormat:@"%@",strUrl];

    }
    else
    {
        urlStr = [NSString stringWithFormat:@"%@%@",strUrl,strPostBody];

    }
    NSLog(@"請求接口:%@",urlStr);
    ASIHTTPRequest *request = [[ASIHTTPRequest alloc]initWithURL:[NSURL URLWithString:urlStr]];
    [request setValidatesSecureCertificate:NO];//請求https的時候,需要設置該屬性
    request.timeOutSeconds = 30;
    //設置傳送類型
    NSString *contentType = [NSString stringWithFormat:@"application/x-www-form-urlencoded"];
    //給傳輸類型添加內容
    [request addRequestHeader:@"Content-Type" value:contentType];

#pragma mark - 修改header頭
    NSString *dataDes = [MCdes encryptUseDES:@"需要加密的字符" key:KEYPassWordMCdes];
    [request addRequestHeader:@"User-Agent" value:dataDes];
    [request addRequestHeader:@"token" value:tokenDes];
......
    [request addRequestHeader:@"phone_type" value:@"應用縮寫_iOS"];

    if (theMethod == ASIGET)
    {
        [request setRequestMethod:@"GET"];
    }
    else
    {
        [request setRequestMethod:@"POST"];
    }
    [[UIApplication sharedApplication]setNetworkActivityIndicatorVisible:YES];

    request.delegate = self;
    self.conn = request;
    [self.conn startAsynchronous];
}

#pragma mark - POST上傳圖片,strUrl表示服務器地址,dicText表示需要的參數,dicImage表示圖片名字,strImageFileName表示上傳到服務器的目錄
- (void)getResultFromUrlString:(NSString *)strUrl dicText:(NSDictionary *)dicText dicImage:(NSDictionary *)dicImage imageFilename:(NSMutableArray *)strImageFileName WithAVFile:(NSString *) avFile
{
    NSLog(@"strUrl = %@",strUrl);
    NSString *url = strUrl;
    //分界線的標識符
    NSString *TWITTERFON_FORM_BOUNDARY = @"AaB03x";
    //根據url初始化request
    ASIHTTPRequest *request = [[ASIHTTPRequest alloc]initWithURL:[NSURL URLWithString:url]];
    [request setValidatesSecureCertificate:NO];//請求https的時候,需要設置該屬性
    request.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;

    request.timeOutSeconds = 30;

    //分界線 --AaB03x
    NSString *MPboundary=[[NSString alloc]initWithFormat:@"--%@",TWITTERFON_FORM_BOUNDARY];
    //結束符 AaB03x--
    NSString *endMPboundary=[[NSString alloc]initWithFormat:@"%@--",MPboundary];
    //http body的字符串
    NSMutableString *body=[[NSMutableString alloc]init];

    for (NSString *strKey in [dicText allKeys])
    {
        NSString *strValue = [dicText objectForKey:strKey];
        [body appendFormat:@"\r\n--%@\r\ncontent-disposition: form-data; name=\"%@\"\r\n\r\n%@",TWITTERFON_FORM_BOUNDARY,strKey,strValue];
    }

    //添加分界線,換行
    [body appendString:@"\r\n"];
    NSMutableArray *aryBody = [[NSMutableArray alloc]init];

    for(int i=0;i<[[dicImage allKeys] count];i++)
    {
        NSString *strImageKey = [NSString stringWithFormat:@"%@",[[dicImage allKeys] objectAtIndex:i]];
        NSMutableString *strBody = [[NSMutableString alloc]init];
        [strBody appendFormat:@"\r\n%@\r\n",MPboundary];

        NSString *S = [NSString stringWithFormat:@"%@",[strImageFileName objectAtIndex:i]];

        //聲明pic字段,文件名
        [strBody appendFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n",S,strImageKey];

        [strBody appendString:@"Content-Type: multipart/form-data\r\n\r\n"];


        [aryBody addObject:strBody];
    }

    //聲明結束符:--AaB03x--
    NSString *end=[[NSString alloc]initWithFormat:@"\r\n%@",endMPboundary];
    //聲明myRequestData,用來放入http body
    NSMutableData *myRequestData=[NSMutableData data];

    //將body字符串轉化為UTF8格式的二進制
    [myRequestData appendData:[body dataUsingEncoding:NSUTF8StringEncoding]];


    for (int i = 0 ; i<[[dicImage allValues] count]; i++)
    {
        NSString *strBody = [aryBody objectAtIndex:i];

        id test = [[dicImage allValues] objectAtIndex:i];
        if([test isKindOfClass:[UIImage class]])
        {
            UIImage *imgUpload = [[dicImage allValues] objectAtIndex:i];
            //上傳圖片格式為png,可以更改為jpeg,同時需要更改文件名
            NSData *dtImg = UIImagePNGRepresentation(imgUpload);
            [myRequestData appendData:[strBody dataUsingEncoding:NSUTF8StringEncoding]];
            [myRequestData appendData:dtImg];
        }
        else if([test isKindOfClass:[AVURLAsset class]])
        {
            NSData *data = [NSData dataWithContentsOfFile:avFile];
            [myRequestData appendData:[strBody dataUsingEncoding:NSUTF8StringEncoding]];
            [myRequestData appendData:data];
        }

    }

    //加入結束符--AaB03x--
    [myRequestData appendData:[end dataUsingEncoding:NSUTF8StringEncoding]];

    //設置HTTPHeader中Content-Type的值
    NSString *content = [[NSString alloc]initWithFormat:@"multipart/form-data; boundary=%@",TWITTERFON_FORM_BOUNDARY];
    //設置HTTPHeader
    [request addRequestHeader:@"Content-Type" value:content];

#pragma mark - 修改header頭
    NSString *dataDes = [MCdes encryptUseDES:@"需要加密的字符" key:KEYPassWordMCdes];
    [request addRequestHeader:@"User-Agent" value:dataDes];
    [request addRequestHeader:@"token" value:tokenDes];

......
    [request addRequestHeader:@"phone_type" value:@"應用縮寫_iOS"];

    //設置Content-Length

    [request addRequestHeader:@"Content-Length" value:[NSString stringWithFormat:@"%lu", (unsigned long)[myRequestData length]]];
    [request setRequestMethod:@"POST"];
    [request setPostBody:myRequestData];

    [[UIApplication sharedApplication]setNetworkActivityIndicatorVisible:YES];
    request.delegate = self;
    self.conn = request;
    [self.conn startAsynchronous];
}

- (void)request:(ASIHTTPRequest *)request didReceiveData:(NSData *)data
{
    [_dtReviceData appendData:data];
}

- (void)request:(ASIHTTPRequest *)request didReceiveResponseHeaders:(NSDictionary *)responseHeaders
{
    _dtReviceData = [[NSMutableData alloc]init];
}

- (void)requestFinished:(ASIHTTPRequest *)request // 根據request.responseData來獲取數據
{
    NSDictionary *dicRespon = nil;
    NSError *error;
    _dtReviceData = [NSMutableData dataWithData:request.responseData];
    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
    dicRespon = [NSJSONSerialization JSONObjectWithData:_dtReviceData options:NSJSONReadingMutableLeaves error:&error];

    NSString *str = [dicRespon descriptionWithLocale:dicRespon];
    NSLog(@"接口返回:%@",str);

    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
    ASIResultCode theResultCode = ASIRS_Error;

    if (dicRespon && [[dicRespon allKeys] containsObject:@"result"]) {

        theResultCode = ASIRS_Success;
    }

    int result = [[dicRespon objectForKey:@"result"] intValue];
    NSString *msg = [dicRespon objectForKey:@"msg"];
    if([self validateString:msg] == NO)
    {
        msg = @"請求失敗";
    }

    if (result == 500) // MARK: 賬號異地二次登錄
    {
    }
    else
    {
        if ([self.delegate respondsToSelector:@selector(resultWithDic:urlTag:isSuccess:Result:Message:failImage:)])
        {
            [self.delegate resultWithDic:dicRespon urlTag:self.urlTag isSuccess:theResultCode Result:result Message:msg failImage:FailStatus];
        }
    }
}

- (void)requestFailed:(ASIHTTPRequest *)request
{
    [[UIApplication sharedApplication]setNetworkActivityIndicatorVisible:NO];
    ASIResultCode theResultCode = ASIRS_Error;
    if ([self.delegate respondsToSelector:@selector(resultWithDic:urlTag:isSuccess:Result:Message:failImage:)])
    {
        [self.delegate resultWithDic:nil urlTag:self.urlTag isSuccess:theResultCode Result:0 Message:@"請求失敗" failImage:FailStatus];
    }
}

- (void)stopConnection
{
    [[UIApplication sharedApplication]setNetworkActivityIndicatorVisible:NO];
    [self.conn clearDelegatesAndCancel];
}
- (void)dealloc
{
    if (self.delegate)
    {
        self.delegate = nil;
    }
}
#pragma mark - 判斷字符串是否為空
- (BOOL) validateString:(NSString *) str
{
    if(str.length ==  0 || [str isKindOfClass:[NSNull class]] || str == nil || str == NULL || [str isEqualToString:@"(null)"] || [[str stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] length]==0 || [str isEqualToString:@"null"])
    {
        return NO;
    }
    return YES;
}
@end

3、EVNASIConnectionUtil使用
#import "EVNConnectionUtil.h"

@interface GetTokenFailViewController : BaseViewController<ASIHTTPConnectionDelegate>
{
    EVNASIConnectionUtil *conn;
}
......
// 使用
- (void) getTokenMethod
{
    conn = [[EVNASIConnectionUtil alloc] initWithURLTag:ASIURLGetTokenTag delegate:self];
    NSString *urlString = [NSString stringWithFormat:@"%@%@",URL_HOST,@"getToken.html"];
    [conn getResultFromUrlString:urlString postBody:nil postArg:nil method:EVNPOST failImage:0]; // 發起請求
}

- (void) resultWithDic:(NSDictionary *)dicRespon evnUrlTag:(ASIURLTag)URLTag isSuccess:(ASIResultCode)theResultCode Result:(int)res Message:(NSString *)message failImage:(int)failStatus
{
    if(URLTag == ASIURLGetTokenTag)
    {
        if(res == 1)
        {
            AppDelegate *appDel = (AppDelegate *)[UIApplication sharedApplication].delegate;
           appDel.appToken = [NSString stringWithFormat:@"%@",[dicRespon objectForKey:@"token"]];
        }
        else
        {
            [NSThread sleepForTimeInterval:0.5];
            [self getTokenMethod];
        }
    }
}

二、AFNetworking再封裝

AFNetworking GitHub鏈接

1、AFNetworking異步請求及AFNetworking簡析

為了迎合蘋果API,AFNetworking3.0中刪除了對NSURLConnection的支持,轉而對NSURLSession封裝使用。

(1)創建網絡請求,設置接受數據類型

AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.requestSerializer.timeoutInterval = 10;
manager.requestSerializer = [AFHTTPRequestSerializer serializer];
manager.responseSerializer = [AFJSONResponseSerializer serializer];

(2)統一為請求添加head,如:Date 提供日期和時間標志、Content-Length請求內容的長度、Content-Type請求與實體對應的MIME信息等;或者添加自定義header的類型,比如特定用戶標記信息,加密信息token等。

[manager.requestSerializer setValue:tokenDes forHTTPHeaderField:@"token"];
......

(3)發起網絡請求,GET、POST
GET方式

[self.manager GET:strUrl parameters:strPostBody progress:^(NSProgress * _Nonnull downloadProgress) {
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        NSLog(@"JSON: %@", responseObject);
        [[UIApplication sharedApplication]setNetworkActivityIndicatorVisible:NO];
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        NSLog(@"Error: %@", error);
    }];

POST方式

[self.manager POST:strUrl parameters:strPostBody progress:^(NSProgress * _Nonnull uploadProgress) {
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        NSLog(@"JSON: %@", responseObject);
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        NSLog(@"Error: %@", error);
    }];
2、EVNAFNConnectionUtil工具類實現

EVNAFNConnectionUtil.h

//
//  EVNAFNConnectionUtil.h
//  MMBao_master
//
//  Created by developer on 16/8/24.
//  Copyright ? 2016年 仁伯安. All rights reserved.
//

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

#pragma mark - 請求方法
typedef NS_ENUM(NSUInteger, URLMethod)
{
    POST = 1,
    GET = 2,
};

#pragma mark - TAG定義,命名規則: URL開頭+模塊名字+Tag
typedef NS_ENUM(NSUInteger, URLTag)
{
#pragma mark: 賬戶管理
    URLGetTokenTag,
#pragma mark - 登錄及注冊

#pragma mark:  三方登錄

};

#pragma mark - 返回的Result
typedef NS_ENUM(NSUInteger, ResultCode)
{
    RS_Success = 1,
    RS_Error,
};

@protocol AFNConnectionDelegate <NSObject>
@optional

@required
/**
 *  網絡請求完成(包括數據返回成功、失敗、接口超時)后的回調方法
 *
 *  @param dicRespon     網絡請求后返回的JSON數據
 *  @param URLTag        網絡請求的標識tag
 *  @param theResultCode 狀態碼,成功 && 失敗
 *  @param res           網絡請求返回的result,一般1表示請求成功,0表示請求失敗
 *  @param message       提示信息
 *  @param failStatus    是否顯示網絡請求異常圖片
 */
- (void)resultWithDic:(NSDictionary *)dicRespon urlTag:(URLTag)URLTag isSuccess:(ResultCode)theResultCode Result:(int) res Message:(NSString *) message failImage:(int) failStatus;

@optional
- (void) resultWithString:(NSString *) str;

@end

#pragma mark - EVNAFNConnectionUtil工具類
@interface EVNAFNConnectionUtil : NSObject<AFNConnectionDelegate>
{
    NSMutableData *_dtReviceData;
    int FailStatus;
}

@property (nonatomic, assign) id<AFNConnectionDelegate> delegate;
@property (nonatomic, assign) URLTag urlTag;
@property (nonatomic, strong) AFHTTPSessionManager *manager;

/**
 *  網絡請求初始化方法
 *
 *  @param theUrlTag   網絡請求的標識tag
 *  @param theDelegate 網絡請求代理
 *
 *  @return ASIHTTP實例
 */
- (instancetype)initWithURLTag:(URLTag)theUrlTag delegate:(id<AFNConnectionDelegate>)theDelegate;


/**
 *  common 網絡請求
 *
 *  @param strUrl      URL服務器地址
 *  @param strPostBody body
 *  @param theMethod   網絡請求方式,比如GET、POST
 */
- (void)getResultFromUrlString:(NSString *)strUrl postBody:(NSString *)strPostBody method:(URLMethod)theMethod;

/**
 *  上傳圖片音頻 網絡請求
 *
 *  @param strUrl           URL服務器地址
 *  @param dicText          需要的參數,dicImage表示,strImageFileName表示
 *  @param dicImage         圖片名字
 *  @param strImageFileName 服務器端文件的目錄名
 *  @param avFile           音頻類網絡請求文件名
 */
- (void)getResultFromUrlString:(NSString *)strUrl dicText:(NSDictionary *)dicText dicImage:(NSDictionary *)dicImage imageFilename:(NSMutableArray *)strImageFileName WithAVFile:(NSString *) avFile;

/**
 *  網絡請求結束,終止
 */
- (void)stopConnection;
#pragma mark - 
- (void)getResultFromUrlString:(NSString *)strUrl postBody:(NSMutableArray *)strPostBodyArray postArg:(NSMutableArray *) argArray method:(URLMethod)theMethod failImage:(int) faileSatatus;

@end

EVNAFNConnectionUtil.m

//
//  EVNAFNConnectionUtil.m
//  MMBao_master
//
//  Created by developer on 16/8/24.
//  Copyright ? 2016年 仁伯安. All rights reserved.
//

#import "EVNAFNConnectionUtil.h"
#import "AppDelegate.h"
#import "MCdes.h"
#import <AVFoundation/AVFoundation.h>
#define KEYPassWordMCdes @"加密字符"
@implementation EVNAFNConnectionUtil
{
    AppDelegate *appDel;
    NSString *tokenDes;
}

- (instancetype)initWithURLTag:(URLTag)theUrlTag delegate:(id<AFNConnectionDelegate>) theDelegate
{
    self = [super init];
    if (self)
    {
        self.delegate = theDelegate;
        self.urlTag = theUrlTag;
        appDel = (AppDelegate *)[UIApplication sharedApplication].delegate;
        tokenDes = appDel.appToken;
        if([self validateString:tokenDes] == NO)
        {
            tokenDes = @"";
        }
    }
    return self;
}
// URL編碼
- (NSString *)encodeToPercentEscapeString: (NSString *) input
{
    NSString *outputStr = (NSString *) CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)input, NULL, (CFStringRef)@"!*'();:@=+$,/?%#[]", kCFStringEncodingUTF8));

    return outputStr;
}

- (void)getResultFromUrlString:(NSString *)strUrl postBody:(NSMutableArray *)strPostBodyArray postArg:(NSMutableArray *)argArray method:(URLMethod)theMethod failImage:(int)faileSatatus
{
    FailStatus = faileSatatus;
    strUrl = [strUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

    NSMutableString *strPostBody = [[NSMutableString alloc] initWithString:@""];
    for(NSInteger i=0;i<argArray.count;i++)
    {
        NSString *bodyStr = [[strPostBodyArray objectAtIndex:i] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
        NSString *argStr = [self encodeToPercentEscapeString:[NSString stringWithFormat:@"%@",[argArray objectAtIndex:i]]];
        NSString *s = [NSString stringWithFormat:@"%@%@",bodyStr,argStr];
        [strPostBody appendString:s];
    }

    NSString *urlStr;
    if([self validateString:strPostBody] == NO)
    {
        urlStr = [NSString stringWithFormat:@"%@",strUrl];
    }
    else
    {
        urlStr = [NSString stringWithFormat:@"%@%@",strUrl,strPostBody];
    }
    NSLog(@"請求接口:%@",urlStr);
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];

    manager.requestSerializer.timeoutInterval = 10;
    manager.requestSerializer = [AFHTTPRequestSerializer serializer];

    manager.responseSerializer = [AFJSONResponseSerializer serializer];
    //設置傳送類型
    NSString *contentType = [NSString stringWithFormat:@"application/x-www-form-urlencoded"];
    //給傳輸類型添加內容
    [manager.requestSerializer setValue:contentType forHTTPHeaderField:@"Content-Type"];
#pragma mark - 修改header頭
    NSString *dataDes = [MCdes encryptUseDES:@"需要加密的字符" key:KEYPassWordMCdes];
    [manager.requestSerializer setValue: dataDes forHTTPHeaderField:@"User-Agent"];
    [manager.requestSerializer setValue:tokenDes forHTTPHeaderField:@"token"];
    [manager.requestSerializer setValue:@"應用縮寫_iOS" forHTTPHeaderField:@"phone_type"];
    manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/html",@"text/json",@"text/javascript", @"text/plain", nil];
    self.manager = manager;
    [[UIApplication sharedApplication]setNetworkActivityIndicatorVisible:YES];
    if (theMethod == GET)
    {
        [self.manager GET:strUrl parameters:strPostBody progress:^(NSProgress * _Nonnull downloadProgress) {

        } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
            NSLog(@"JSON: %@", responseObject);
            [[UIApplication sharedApplication]setNetworkActivityIndicatorVisible:NO];
            NSError *error;
            NSDictionary *dicRespon = responseObject;
            NSString *str = [dicRespon descriptionWithLocale:dicRespon];
            NSLog(@"接口返回:%@",str);
            ResultCode theResultCode = RS_Error;
            if (dicRespon && [[dicRespon allKeys] containsObject:@"result"]) 
            {
                theResultCode = RS_Success;
            }
            int result = [[dicRespon objectForKey:@"result"] intValue];
            NSString *msg = [dicRespon objectForKey:@"msg"];
            if([self validateString:msg] == NO)
            {
                msg = @"請求失敗";
            }
            if (result == 500) // MARK: 賬號異地二次登錄
            {
            }
            else
            {
                if ([self.delegate respondsToSelector:@selector(resultWithDic:urlTag:isSuccess:Result:Message:failImage:)])
                {
                    [self.delegate resultWithDic:dicRespon urlTag:self.urlTag isSuccess:theResultCode Result:result Message:msg failImage:FailStatus];
                }
            }
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            NSLog(@"Error: %@", error);

            [[UIApplication sharedApplication]setNetworkActivityIndicatorVisible:NO];
            ResultCode theResultCode = RS_Error;
            if ([self.delegate respondsToSelector:@selector(resultWithDic:urlTag:isSuccess:Result:Message:failImage:)])
            {
                [self.delegate resultWithDic:nil urlTag:self.urlTag isSuccess:theResultCode Result:0 Message:@"請求失敗" failImage:FailStatus];
            }
        }];
    }
    else
    {
        [self.manager POST:strUrl parameters:strPostBody progress:^(NSProgress * _Nonnull uploadProgress) {

        } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {

            NSLog(@"JSON: %@", responseObject);

            [[UIApplication sharedApplication]setNetworkActivityIndicatorVisible:NO];
            NSError *error;
            NSDictionary *dicRespon = responseObject;

            NSString *str = [dicRespon descriptionWithLocale:dicRespon];
            NSLog(@"接口返回:%@",str);
            ResultCode theResultCode = RS_Error;

            if (dicRespon && [[dicRespon allKeys] containsObject:@"result"])
           {
                theResultCode = RS_Success;
            }
            int result = [[dicRespon objectForKey:@"result"] intValue];
            NSString *msg = [dicRespon objectForKey:@"msg"];
            if([self validateString:msg] == NO)
            {
                msg = @"請求失敗";
            }
            if (result == 500) // MARK: 賬號異地二次登錄
            {
            }
            else
            {
                if ([self.delegate respondsToSelector:@selector(resultWithDic:urlTag:isSuccess:Result:Message:failImage:)])
                {
                    [self.delegate resultWithDic:dicRespon urlTag:self.urlTag isSuccess:theResultCode Result:result Message:msg failImage:FailStatus];
                }
            }
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            NSLog(@"Error: %@", error);
            [[UIApplication sharedApplication]setNetworkActivityIndicatorVisible:NO];
            ResultCode theResultCode = RS_Error;
            if ([self.delegate respondsToSelector:@selector(resultWithDic:urlTag:isSuccess:Result:Message:failImage:)])
            {
                [self.delegate resultWithDic:nil urlTag:self.urlTag isSuccess:theResultCode Result:0 Message:@"請求失敗" failImage:FailStatus];
            }
        }];
    }
}
#pragma mark - 異步請求數據,strUrl表示域名,strPostBody表示上傳的參數,theMethod表示請求方式
- (void)getResultFromUrlString:(NSString *)strUrl postBody:(NSString *)strPostBody method:(URLMethod)theMethod
{
    strUrl = [strUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    strPostBody = [strPostBody stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    NSString *urlStr;
    if([self validateString:strPostBody] == NO)
    {
        urlStr = [NSString stringWithFormat:@"%@",strUrl];
    }
    else
    {
        urlStr = [NSString stringWithFormat:@"%@%@",strUrl,strPostBody];
    }
    NSLog(@"請求接口:%@",urlStr);
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    manager.requestSerializer.timeoutInterval = 10;
    manager.requestSerializer = [AFHTTPRequestSerializer serializer];
    manager.responseSerializer = [AFJSONResponseSerializer serializer];
    //設置傳送類型
    NSString *contentType = [NSString stringWithFormat:@"application/x-www-form-urlencoded"];
    //給傳輸類型添加內容
    [manager.requestSerializer setValue:contentType forHTTPHeaderField:@"Content-Type"];
#pragma mark - 修改header頭
    NSString * dataDes = [MCdes encryptUseDES:@"需要加密的字符" key:KEYPassWordMCdes];
    [manager.requestSerializer setValue: dataDes forHTTPHeaderField:@"User-Agent"];
    [manager.requestSerializer setValue:tokenDes forHTTPHeaderField:@"token"];
    [manager.requestSerializer setValue:@"應用縮寫_iOS" forHTTPHeaderField:@"phone_type"];
    manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/html",@"text/json",@"text/javascript", @"text/plain", nil];
    self.manager = manager;
    [[UIApplication sharedApplication]setNetworkActivityIndicatorVisible:YES];
    if (theMethod == GET)
    {
        [self.manager GET:strUrl parameters:strPostBody progress:^(NSProgress * _Nonnull downloadProgress) {

        } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
            NSLog(@"JSON: %@", responseObject);
            [[UIApplication sharedApplication]setNetworkActivityIndicatorVisible:NO];
            NSError *error;
            NSDictionary *dicRespon = responseObject;
            NSString *str = [dicRespon descriptionWithLocale:dicRespon];
            NSLog(@"接口返回:%@",str);
            ResultCode theResultCode = RS_Error;
            if (dicRespon && [[dicRespon allKeys] containsObject:@"result"]) 
            {
                theResultCode = RS_Success;
            }
            int result = [[dicRespon objectForKey:@"result"] intValue];
            NSString *msg = [dicRespon objectForKey:@"msg"];
            if([self validateString:msg] == NO)
            {
                msg = @"請求失敗";
            }
            if (result == 500) // MARK: 賬號異地二次登錄
            {
            }
            else
            {
                 __weak typeof(self) weakSelf = self;
                if ([weakSelf.delegate respondsToSelector:@selector(resultWithDic:urlTag:isSuccess:Result:Message:failImage:)])
                {
                    [weakSelf.delegate resultWithDic:dicRespon urlTag:self.urlTag isSuccess:theResultCode Result:result Message:msg failImage:FailStatus];
                }
            }
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            NSLog(@"Error: %@", error);
            [[UIApplication sharedApplication]setNetworkActivityIndicatorVisible:NO];

            ResultCode theResultCode = RS_Error;
             __weak typeof(self) weakSelf = self;
            if ([weakSelf.delegate respondsToSelector:@selector(resultWithDic:urlTag:isSuccess:Result:Message:failImage:)])
            {
                [weakSelf.delegate resultWithDic:nil urlTag:self.urlTag isSuccess:theResultCode Result:0 Message:@"請求失敗" failImage:FailStatus];
            }
        }];
    }
    else
    {
        [self.manager POST:strUrl parameters:strPostBody progress:^(NSProgress * _Nonnull uploadProgress) {

        } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
            NSLog(@"JSON: %@", responseObject);
            [[UIApplication sharedApplication]setNetworkActivityIndicatorVisible:NO];
            NSError *error;
            NSDictionary *dicRespon = responseObject;
            NSString *str = [dicRespon descriptionWithLocale:dicRespon];
            NSLog(@"接口返回:%@",str);
            ResultCode theResultCode = RS_Error;
            if (dicRespon && [[dicRespon allKeys] containsObject:@"result"]) {
                theResultCode = RS_Success;
            }
            int result = [[dicRespon objectForKey:@"result"] intValue];
            NSString *msg = [dicRespon objectForKey:@"msg"];
            if([self validateString:msg] == NO)
            {
                msg = @"請求失敗";
            }
            if (result == 500) // MARK: 賬號異地二次登錄
            {
            }
            else
            {
                __weak typeof(self) weakSelf = self;
                if ([weakSelf.delegate respondsToSelector:@selector(resultWithDic:urlTag:isSuccess:Result:Message:failImage:)])
                {
                    [weakSelf.delegate resultWithDic:dicRespon urlTag:self.urlTag isSuccess:theResultCode Result:result Message:msg failImage:FailStatus];
                }
            }
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            NSLog(@"Error: %@", error);
            [[UIApplication sharedApplication]setNetworkActivityIndicatorVisible:NO];
            ResultCode theResultCode = RS_Error;
            __weak typeof(self) weakSelf = self;
            if ([weakSelf.delegate respondsToSelector:@selector(resultWithDic:urlTag:isSuccess:Result:Message:failImage:)])
            {
                [weakSelf.delegate resultWithDic:nil urlTag:self.urlTag isSuccess:theResultCode Result:0 Message:@"請求失敗" failImage:FailStatus];
            }
        }];
    }
}
......
- (void)stopConnection
{
    [[UIApplication sharedApplication]setNetworkActivityIndicatorVisible:NO];
    [self.manager invalidateSessionCancelingTasks:YES];
}

- (void)dealloc
{
    if (self.delegate)
    {
        self.delegate = nil;
    }
}

#pragma mark - 判斷字符串是否為空
- (BOOL) validateString:(NSString *) str
{
    if(str.length ==  0 || [str isKindOfClass:[NSNull class]] || str == nil || str == NULL || [str isEqualToString:@"(null)"] || [[str stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] length]==0 || [str isEqualToString:@"null"])
    {
        return NO;
    }
    return YES;
}

@end

3、EVNAFNConnectionUtil使用
@interface GetTokenFailViewController : BaseViewController<AFNConnectionDelegate>
{
    EVNAFNConnectionUtil *conn;
}
......
- (void) getTokenMethod
{
    conn = [[EVNAFNConnectionUtil alloc] initWithURLTag:URLGetTokenTag delegate:self];
    NSString *urlString = [NSString stringWithFormat:@"%@%@",URL_HOST_CHEN,@"token/getToken.html"];
    [conn getResultFromUrlString:urlString postBody:nil postArg:nil method:EVNPOST failImage:0];
}

- (void) resultWithDic:(NSDictionary *)dicRespon evnUrlTag:(EVNURLTag)URLTag isSuccess:(EVNResultCode)theResultCode Result:(int)res Message:(NSString *)message failImage:(int)failStatus
{
    if(URLTag == URLGetTokenTag)
    {
        if(res == 1)
        {
                AppDelegate *appDel = (AppDelegate *)[UIApplication sharedApplication].delegate;
                appDel.appToken = [NSString stringWithFormat:@"%@",[dicRespon objectForKey:@"token"]];
        }
        else
        {
              [NSThread sleepForTimeInterval:0.5];
              [self getTokenMethod];
        }
    }
}

三、補充

時間倉促,不足之處見諒。。。。。。

本文已在版權印備案,如需轉載請在版權印獲取授權。
獲取版權

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • AFN什么是AFN全稱是AFNetworking,是對NSURLConnection、NSURLSession的一...
    醉葉惜秋閱讀 1,250評論 0 0
  • 13.服務器圖片改了,url沒有變,需求,服務器的圖片變了,就從服務器加載圖片,如果服務器的沒變,就從本地加載 1...
    AlanGe閱讀 1,221評論 0 1
  • AFHTTPRequestOperationManager 網絡傳輸協議UDP、TCP、Http、Socket、X...
    Carden閱讀 4,384評論 0 12
  • //聯系人:石虎QQ: 1224614774昵稱:嗡嘛呢叭咪哄 ASI和AFN有什么區別 1.性能(重點) * A...
    石虎132閱讀 970評論 3 15