HTTP请求规则及OC封装

http请求原理,主要是post,首先post向服务器发送key,value格式的数据,一般都是一下字符串,例如:

NSDictionary *dic = @{@"key1":@"value1",@"key2":@"value2",@"key3":@"value3"};
//传递形式则是:
--AaB03x
Content-Disposition: form-data; name="key1"
 
value1
--AaB03x
Content-Disposition: form-data; name="key3"
 
value3
--AaB03x
Content-Disposition: form-data; name="key2"
 
value2

–AaB03x是分割符,如果单纯的传递字符串形式的,分隔符是每个系统确定的,例如ios,或者Android等SDK确定的,也可以自己去命名而且使用分隔符:

例如ios,

    NSString *TWITTERFON_FORM_BOUNDARY = @"AaB03x";
    //分界线 --AaB03x
    NSString *MPboundary=[[NSString alloc]initWithFormat:@"--%@",TWITTERFON_FORM_BOUNDARY];
    //结束符 AaB03x--
    NSString *endMPboundary=[[NSString alloc]initWithFormat:@"%@--",MPboundary];
    NSArray *keys= [params allKeys];
    for(int i=0;i<[keys count];i++){
        //得到当前key
        NSString *key=[keys objectAtIndex:i];
        //添加分界线,换行
        [body appendFormat:@"%@\r\n",MPboundary];
        //添加字段名称,换2行
        [body appendFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n",key];
        //添加字段的值
        [body appendFormat:@"%@\r\n",[params objectForKey:key]];
    }

在循环的最后一行添加完字段的值之后,必须加入\r\n去换行,这是http协议的规则,否则后台接受不到数据。

完了就是图片等文件的上传,如果要字符串和文件上传一起的话,那么首先将文字,也就是上边代码中的string类型的body转换成2进制的,添加到httpbody中,然后将图片添加到httpbody中。

然后是图片上传:

图片上传和html中form表单上传是同一个原理,都需要指定图片的key值,图片上传的模式:form-data。不同的是,图片的名字在浏览器中上传的时候是可以获取的,但是在app中获取不到名字,所以还需要传递名字进去。如果app不想传递图片名的话,那么可以自己随机几个名字,因为后台接受的是data数据,也就是2进制流。

格式:

1,分隔符,

2,声明图片的处理机制,也就是告诉http,用什么机制去处理我这次请求,如果我要上传附件的话,那么就得确定data,所以,声明处理机制:Content-Disposition: form-data; name=\”%@\”; filename=\”%@\”\r\n”

里边包含了图片的key值,key值就是后台接受图片的键值,filename是图片名,content-Disposition是处理机制,每个分号;之后必须得有一个空格,

3,当前附件的类型:Content-Type: %@\r\n\r\n”,@”image/png

然后将所有声明的格式打包成二进制,添加到httpbody中,最后将图片data,也就是图片的二进制流添加到httpbody中。最后,再添加一个回车,也就是\r\n,\r\n也得打包成二进制文件。最后再添加 “\r\n结束符”。

这只是一张图片,也可以说是一个附件,因为没一个附件都得有分隔符,机制,类型,还得有最后的一个回车。

例如:

NSMutableData *RequestData=[[NSMutableData alloc]init];
    //将params添加进去
    [RequestData appendData:[body dataUsingEncoding:NSUTF8StringEncoding]];
    for (int i = 0; i < [images count]; i++) {
        //
        NSMutableString *subBody = [[NSMutableString alloc]init];
        //fileName
        NSString *fileName = [NSString stringWithFormat:@"%@.png",[self sha1WithSource:[@(i) stringValue]]];
        ////添加分界线,换行
        [subBody appendFormat:@"%@\r\n",MPboundary];
        //声明pic字段,文件名
        [subBody appendFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n",formNames[i],fileName];
        if(_mimeType != nil){
            [subBody appendFormat:@"Content-Type: %@\r\n\r\n",_mimeType];
        }else{
            //声明上传文件的格式
            [subBody appendFormat:@"Content-Type: %@\r\n\r\n",@"image/png"];
        }
        //将body字符串转化为UTF8格式的二进制
        [RequestData appendData:[subBody dataUsingEncoding:NSUTF8StringEncoding]];
        //将image的data加入
        [RequestData appendData:images[i]];
        //必加的分隔符,每一个data后边,http的规则
        [RequestData appendData:[ @"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
    }

/*===现在httpbody中运行的结果是这样的:====*/
--AaB03x
Content-Disposition: form-data; name="key1"
 
value1
--AaB03x
Content-Disposition: form-data; name="key3"
 
value3
--AaB03x
Content-Disposition: form-data; name="key2"
 
value2
--AaB03x
Content-Disposition: form-data; name="uploadImage[]"; filename="b6589fc6ab0dc82cf12099d1c2d40ab994e8410c.png"
Content-Type: image/png
 
--AaB03x
Content-Disposition: form-data; name="uploadImage[]"; filename="356a192b7913b04c54574d18c28d46e6395428ab.png"
Content-Type: image/png
 
--AaB03x
Content-Disposition: form-data; name="uploadImage[]"; filename="da4b9237bacccdf19c0760cab7aec4a8359010b0.png"
Content-Type: image/png
 
--AaB03x
Content-Disposition: form-data; name="uploadImage[]"; filename="77de68daecd823babbb58edb1c8e14d7106e83bb.png"
Content-Type: image/png
 
 
--AaB03x--
//结束了,看见这边三个回车,然后又添加了一个结束符,说明上边两个回车,最后结束符还得带一个回车

服务器可以检查当前的各种规则,例如:

2016-03-12 13:55:20.718 HttpDemo[3019:168928] {
    "CONTENT_LENGTH" = 1678187;
    "CONTENT_TYPE" = "multipart/form-data; boundary=AaB03x";
    "CONTEXT_DOCUMENT_ROOT" = "/Applications/XAMPP/xamppfiles/htdocs";
    "CONTEXT_PREFIX" = "";
    "DOCUMENT_ROOT" = "/Applications/XAMPP/xamppfiles/htdocs";
    "DYLD_LIBRARY_PATH" = "/Applications/XAMPP/xamppfiles/lib:/Applications/XAMPP/xamppfiles/lib";
    "GATEWAY_INTERFACE" = "CGI/1.1";
    "HTTP_ACCEPT" = "*/*";
    "HTTP_ACCEPT_ENCODING" = "gzip, deflate";
    "HTTP_ACCEPT_LANGUAGE" = "zh-cn";
    "HTTP_CONNECTION" = "keep-alive";
    "HTTP_HOST" = localhost;
    "HTTP_KEY1" = value1;
    "HTTP_KEY2" = value2;
    "HTTP_KEY3" = value3;
    "HTTP_USER_AGENT" = "HttpDemo/1 CFNetwork/758.2.8 Darwin/14.5.0";
    PATH = "/usr/bin:/bin:/usr/sbin:/sbin";
    "PHP_SELF" = "/php/posttest.php";
    "QUERY_STRING" = "";
    "REMOTE_ADDR" = "::1";
    "REMOTE_PORT" = 58838;
    "REQUEST_METHOD" = POST;
    "REQUEST_SCHEME" = http;
    "REQUEST_TIME" = 1457762120;
    "REQUEST_TIME_FLOAT" = "1457762120.683";
    "REQUEST_URI" = "/php/posttest.php";
    "SCRIPT_FILENAME" = "/Applications/XAMPP/xamppfiles/htdocs/php/posttest.php";
    "SCRIPT_NAME" = "/php/posttest.php";
    "SERVER_ADDR" = "::1";
    "SERVER_ADMIN" = "you@example.com";
    "SERVER_NAME" = localhost;
    "SERVER_PORT" = 80;
    "SERVER_PROTOCOL" = "HTTP/1.1";
    "SERVER_SIGNATURE" = "";
    "SERVER_SOFTWARE" = "Apache/2.4.10 (Unix) OpenSSL/1.0.1j PHP/5.6.3 mod_perl/2.0.8-dev Perl/v5.16.3";
    "UNIQUE_ID" = "VuOvSH8AAAEAAAufp-oAAAAB";
}

最后,在请求的时候,设置http此次请求的body的大小,得再头信息里边设置请求的类型,这样才能和请求一一对应,http协议才能解析这次请求,最后分隔符http是怎么解析的,也是从头信息里边申明了,然后进行body的解析,例如:

NSString *content=[[NSString alloc]initWithFormat:@"multipart/form-data; boundary=%@",TWITTERFON_FORM_BOUNDARY];
    //设置HTTPHeader
    [_request setValue:content forHTTPHeaderField:@"Content-Type"];
    //设置Content-Length
    [_request setValue:[NSString stringWithFormat:@"%ld", [RequestData length]] forHTTPHeaderField:@"Content-Length"];

最后设置一下requestHttpBody就行。 这就是一个完整的post请求。 PS,写了一个类似AFNet的库,比较小巧,但是功能指定没人那个强大,但是日常请求,上传是没问题了,性能就不知道了,但是封装苹果的sdk,相信苹果写的性能应该不会那么差吧。 下载:http://pan.baidu.com/s/1o6RCwOi 用法: 

#import “AshesHttpRequest.h”

//get,post,post图片上传请求:
-(void)getRequestWithURL:(NSString *)url atParames:(NSDictionary *)parames atWillRequest:(willSendRequestHandle)willRequest atSucceedRequest:(succeedRequestHandle)succeedRequest atErrorRequest:(errorRequestHandle)errorRquest;
 
-(void)postRequestWithURL:(NSString *)url atParames:(NSDictionary *)parames atWillRequest:(willSendRequestHandle)willRequest atSucceedRequest:(succeedRequestHandle)succeedRequest atErrorRequest:(errorRequestHandle)errorRquest;
 
-(void)postRequestWithURL:(NSString *)url atImages:(NSArray *)images atFormNames:(NSArray *)formNames atParames:(NSDictionary *)parames atWillRequest:(willSendRequestHandle)willRequest atSucceedRequest:(succeedRequestHandle)succeedRequest atErrorRequest:(errorRequestHandle)errorRquest;
//图片上传注意的是,images里边传递是图片的data数组,names传递每个图片对应的键。一一对应,两个数组count必须相等
 
GET:
    AshesHttpRequest *r = [AshesHttpRequest manager];
    [r getRequestWithURL:@"http://skyzizhu2.sinaapp.com/api.php" atParames:nil atWillRequest:^{
        NSLog(@"will begin reques!");
    } atSucceedRequest:^(AshesHttpRequest *ashesRequest, id reponseObject) {
        NSArray *dataArray = (NSArray *)[reponseObject objectForKey:@"data"];
        for (int i = 0; i < [dataArray count]; i++) {
            AshesHttpModule *model = [[AshesHttpModule alloc]init];
            model.title = [NSString stringWithFormat:@"%@",[dataArray[i] objectForKey:@"infoname"]];
            model.subTitle = [[NSString stringWithFormat:@"%@",[dataArray[i] objectForKey:@"info_intro"]] stringByReplacingOccurrencesOfString:@" " withString:@""];
            NSLog(@"title - - -%@,subtitle- - %@",model.title,model.subTitle);
            [_dataSource addObject:model];
        }
        [_tableView reloadData];
    } atErrorRequest:^(NSError *error) {
        NSLog(@"error - -- %@",error);
    }];
//POST多图片上传,如果一张图片则数组里边一个元素即可
    NSData *imgData1 = UIImagePNGRepresentation([UIImage imageNamed:@"1.png"]);
    NSData *imgData2 = UIImageJPEGRepresentation([UIImage imageNamed:@"User2.jpg"],1);
    NSData *imgData3 = UIImageJPEGRepresentation([UIImage imageNamed:@"User1.jpg"],1);
    NSData *imgData4 = UIImageJPEGRepresentation([UIImage imageNamed:@"USA.jpg"],1);
    NSArray *images = @[imgData1,imgData2,imgData3,imgData4];
    NSArray *names = @[@"uploadImage[]",@"uploadImage[]",@"uploadImage[]",@"uploadImage[]"];
    NSDictionary *dic = @{@"key1":@"value1",@"key2":@"value2",@"key3":@"value3"};
    NSString *cURL = @"http://localhost/php/posttest.php";
    //@"http://skyzizhu2.sinaapp.com/api.php"
    AshesHttpRequest *manager = [AshesHttpRequest manager];
    [manager addHeaderFieldWithFieldValue:dic];
    [manager setMimeType:@"image/png"];
    [manager postRequestWithURL:cURL atImages:images atFormNames:names atParames:dic atWillRequest:^{
        NSLog(@"will request");
    } atSucceedRequest:^(AshesHttpRequest *request, id reponseObject) {
        NSLog(@"%@",request.request.allHTTPHeaderFields);
        NSLog(@"%@ , responseData%@",reponseObject,request.responseData);
    } atErrorRequest:^(NSError *error) {
        NSLog(@"%@",error);
    }];
//过几天提供一个多图上传的后台测试接口

测试接口:

http://skyzizhu2.sinaapp.com/App/posttest.php

返回例子

{
    clientIP = "xxx.xxx.99.221";
    "client_AGENT" = "HttpDemo/1 CFNetwork/758.2.8 Darwin/14.5.0";
    data =     {
        image1 = "http://skyzizhu2-testappimage.stor.sinaapp.com/b6589fc6ab0dc82cf12099d1c2d40ab994e8410c.png";
        image2 = "http://skyzizhu2-testappimage.stor.sinaapp.com/356a192b7913b04c54574d18c28d46e6395428ab.png";
        image3 = "http://skyzizhu2-testappimage.stor.sinaapp.com/da4b9237bacccdf19c0760cab7aec4a8359010b0.png";
        image4 = "http://skyzizhu2-testappimage.stor.sinaapp.com/77de68daecd823babbb58edb1c8e14d7106e83bb.png";
    };
    gets =     (
    );
    isUploadSucceed = 1;
    posts =     {
        key1 = value1;
        key2 = value2;
        key3 = value3;
    };
    requestTime = 1457766301;
}

鸣谢:http://m.open-open.com/m/code/view/1421912619062

<?php
    $finderName = "testSaveFinders/";
    if(!is_dir($finderName)){
        mkdir($finderName);
    }
    $keys = array();
    $serverPath =  dirname('http://'.$_SERVER['SERVER_NAME'].$_SERVER["REQUEST_URI"]);
    foreach ($_FILES as $key => $value) {
        # code...
         move_uploaded_file($_FILES[$key]["tmp_name"],$finderName . $_FILES[$key]["name"]);
 
         $keys[$key] =$serverPath.'/'.$finderName . $_FILES[$key]["name"];
    }
    $arrayData = array();
    if(count($keys) > 0){
        $arrayData['isSucceed'] = TRUE;
    }else{
        $arrayData['isSucceed'] = FALSE;
    }
    $arrayData['requestTime'] = $_SERVER['REQUEST_TIME'];
    $arrayData['clientIP'] = $_SERVER["REMOTE_ADDR"];
    $arrayData['client_AGENT'] = $_SERVER['HTTP_USER_AGENT'];
    $arrayData['data'] = $keys;
    echo json_encode($arrayData);
?>

Leave a Reply

Required fields are marked *