以上为个人愚见,
如有不妥,望大家斧正!!!本文的GitHub源码下载地址:

阐述:

  • 實現Post請求

一、遇到的问题:

网络请求的时候抛出 —–> NSLocalizedDescription = “Request failed:
unacceptable content-type: text/html”;


使用AFNetworking遇到错误
错误日志:

如需转载,请注明转载自DXSmile的GitHub项目

在获取网络数据的时候,我们一般会使用到一个非常著名的框架:AFNetworking框架,可以说,这是作为iOS工程师必备的框架之一:

这是一个非常强大的框架,对于发送异步请求来说,简直没有比这个更好用了,不过,在使用的过程中,我们可能遇到这样一个bug:如下

连接出错 Error Domain=com.alamofire.error.serialization.response Code=-1016 
"Request failed: unacceptable content-type: text/html" UserInfo=
{com.alamofire.serialization.response.error.response=<NSHTTPURLResponse: 0x7f93fad1c4b0> 
{ URL: http://c.m.163.com/nc/article/headline/T1348647853363/0-140.html } 
{ status code: 200, headers { .....}
...... 
 22222c22 626f6172 64696422 3a226e65 77735f73 68656875 69375f62 6273222c 22707469 6d65223a 22323031 362d3033 2d303320 31313a30 323a3435 227d5d7d>,
 NSLocalizedDescription=Request failed: unacceptable content-type: text/html}

二、原因

AFNetworking  默认不支持 text/html

AFURLResponseSerialization.m 源码  -> self.acceptableContentTypes=
[NSSetsetWithObjects:@”application/json”,@”text/json”,@”text/javascript”,nil];

木有 @”text/html”


图片 1

在获取网络数据的时候, 我们一般会使用到一个非常著名的框架:
AFNetworking框架,
可以说,这是作为iOS攻城狮必备的框架之一;这个框架是非常强大的一个框架,
对于发送异步请求来说,简直没有比这个更好用的了,
不过,在使用的过程中,我们可能会遇到这样一个bug: 如下

说明:

由于数据很多,所以返回的请求体,和响应体部分我用省略号(……)代替了,但是,通过上面的返回的信息,我们不难看出,状态码200,而且也有一堆数据,但是在tableViewCell中就是没有显示,在最后的时候还出现”NSLocalizedDescription=Request
failed: unacceptable content-type: text/html}” 这样一句话:

 NSString *strURL = @"http://xxx/WebService.php";
    NSURL *url = [NSURL URLWithString:strURL];

    //设置参数
    NSString *post = [NSString stringWithFormat:@"email=%@&type=%@&action=%@", @"xxx@xxx.com", @"JSON", @"query"];
    NSData *postData = [post dataUsingEncoding:NSUTF8StringEncoding];

    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    [request setHTTPMethod:@"POST"];
    [request setHTTPBody:postData];

    NSURLSessionConfiguration *defaultConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
//前面都和URLSESSION一樣,
    AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:defaultConfig];

    NSURLSessionDataTask *task = [manager dataTaskWithRequest:request completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
        NSLog(@"请求完成...");
        if (!error) {
            [self reloadView:responseObject];
        } else {
            NSLog(@"error : %@", error.localizedDescription);
        }
    }];

    [task resume];

三、解决办法

1)在作者源代码处添加 @”text/html” 。

2)在自己的代码处加上这句代码:

manager.responseSerializer.acceptableContentTypes = \[NSSet
setWithObject:@”text/html”\];

2FCEE60E-9AE8-420B-B71D-A7B4941B60C2.png

连接出错 Error Domain=com.alamofire.error.serialization.response Code=-1016 "Request failed: unacceptable content-type: text/html" UserInfo={com.alamofire.serialization.response.error.response=<NSHTTPURLResponse: 0x7f93fad1c4b0> { URL: http://c.m.163.com/nc/article/headline/T1348647853363/0-140.html } { status code: 200, headers { .....}...... 22222c22 626f6172 64696422 3a226e65 77735f73 68656875 69375f62 6273222c 22707469 6d65223a 22323031 362d3033 2d303320 31313a30 323a3435 227d5d7d>, NSLocalizedDescription=Request failed: unacceptable content-type: text/html}
分析:那个这个错误是什么原因造成的呢?

因为manager有一个responseSerializer属性,它只设置了一些固定的解析格式.其中不包括text/html这种数据的格式.所以解析报错.

我们来看一下AFNetworking解析格式的底层:

self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil];

通过底层,我们也可以看见,确实是没有text/html这种数据的格式,

  • 請求下載

还好,不是什么大坑,出现这个错误的原因大概就是不接受返回类型
为“text/html”的数据,既然是这样,那就修改一下AFNetworking的内容就好。

由于数据很多,所以返回的请求体,和响应体部分我用省略号代替了,
但是,通过上面的返回的信息,我们不难看出,状态码是200, 而且也有一堆数据,
但是在tableviewCell中就是没有显示,
在最后的时候还出现”NSLocalizedDescription=Request failed: unacceptable
content-type: text/html} ” 这样一句话;

那如何解决这个问题呢?

错误的解决方法如下:

解决方案:
AFURLResponseSerialization.m中,228行左右
把 这句: self.acceptableContentTypes = [NSSet
setWithObjects:@”application/json”, @”text/json”, @”text/javascript”,
nil];
修改为:
self.acceptableContentTypes = [NSSet
setWithObjects:@”application/json”, @”text/json”,
@”text/javascript”,@”text/html”, nil];
在进行网络请求时出现-1016
是因为只支持text/json,application/json,text/javascript

通过这句话:unacceptable content-type:
text/html,我们可以看出报错原因:是不接收的内容类型,也就是说AFNetworking框架不支持解析text/html这种格式.
那么怎样解决呢?

解决方法1: 直接给acceptableContentTypes属性添加类型

着手解决:

self.acceptableContentTypes = [NSSet setWithObjects:@"text/html",@"application/json", @"text/json" ,@"text/javascript", nil];

图片 2

ae6Njir.png!web.png

解决之后:
通过执行结果可以很明显的看得出,我们已经非常成功的获取到数据:

当然有很多方法.

NSURLSessionDownloadTask *downloadTask = [manager downloadTaskWithRequest:request progress:^(NSProgress *downloadProgress) {
//下載進度
        NSLog(@"%@", [downloadProgress localizedDescription]);
        dispatch_async(dispatch_get_main_queue(), ^{
            [self.progressView setProgress:downloadProgress.fractionCompleted animated:TRUE];
        });

    } destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
//存放文件
        NSString *downloadsDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, TRUE) objectAtIndex:0];
        NSString *downloadStrPath = [downloadsDir stringByAppendingPathComponent:[response suggestedFilename]];
        NSURL *downloadURLPath = [NSURL fileURLWithPath:downloadStrPath];

        return downloadURLPath;

    } completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
//下載完成更新UI組件
        NSLog(@"File downloaded to: %@", filePath);
        NSData *imgData = [[NSData alloc] initWithContentsOfURL:filePath];
        UIImage *img = [UIImage imageWithData:imgData];
        self.imageView1.image = img;

    }];

    [downloadTask resume];

这样就好了,完美解决!

首先我们需要明白: AFNetworking为什么能够解析服务器返回的东西呢?

因为manager有一个responseSerializer属性.它只设置了一些固定的解析格式.其中不包含text/html这种数据的格式.因为解析报错了.我们来看一下AFNetworking解析格式的底层:

 self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil];

通过底层,我们也可以看见,确实是没有text/html这种数据的格式的,

  • 請求上傳

AFNetworking报3840错误
Error Domain=NSCocoaErrorDomain Code=3840 “Invalid value around
character 64.” UserInfo={NSDebugDescription=Invalid value around
character 64.}
服务器没有返回json数据,服务器的锅

那如何解决这个问题呢?

错误的解决方法下面我尝试了三种方法:

解决方法1: 直接给acceptableContentTypes属性添加类型

解决之前:

 self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil];

图片 3解决之前

着手解决:

 AFHTTPRequestOperationManager *mgr = [AFHTTPRequestOperationManagermanager]; // 添加 text/html 类型到可接收内容类型中 mgr.responseSerializer.acceptableContentTypes= [NSSetsetWithObjects:@"text/html", nil];

运行结果:

图片 4解决之后:

通过执行结果可以很明显的看得出,我们已经非常成功的获取到了数据;

    NSString *uploadStrURL = @"http://xxx/upload.php";
    NSDictionary *params = @{@"email" : @"xxx@xxx.com"};

    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"test2" ofType:@"jpg"];


    NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST"
                  URLString:uploadStrURL parameters:params
                  constructingBodyWithBlock:^(id <AFMultipartFormData> formData) {
                      [formData appendPartWithFileURL:[NSURL fileURLWithPath:filePath] name:@"file" fileName:@"1.jpg" mimeType:@"image/jpeg" error:nil];
                  } error:nil];

    AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
    NSURLSessionUploadTask *uploadTask;
    uploadTask = [manager uploadTaskWithStreamedRequest:request
                   progress:^(NSProgress *uploadProgress) {
                       NSLog(@"上传: %@", [uploadProgress localizedDescription]);
                       dispatch_async(dispatch_get_main_queue(), ^{
                           [self.progressView setProgress:uploadProgress.fractionCompleted];
                       });

                   }
                  completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
                      if (!error) {
                          NSLog(@"上传成功");
                          [self download];
                      } else {
                          NSLog(@"上传失败: %@", error.localizedDescription);
                      }
                  }];

    [uploadTask resume];
对方法1的思考:

**首先,我们可以明显的看出,方法1确实是可以解决问题的,但是这样解决真的好吗?
不一定!为什么呢? 很简单,
如果我们只是发送一条网络请求,无疑方法1是最恰当的解决方案了,
但是实际开发中,我们不可能只发一次请求,
那么就需要我们每次发请求的时候都来写一次这些代码;
当然,如果您愿意写,那我也没办法多说什么了;很显然,这个方法是存在不足的!
**

于是我们有了第二种方法:

解决之前:

 self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil];

解决之后:

 self.acceptableContentTypes = [NSSet setWithObjects:@"text/html",@"application/json", @"text/json" ,@"text/javascript", nil];
分析方法2:

不得不说,这也是一种办法, 而且釜底抽薪,效果方面呢,当然也是显而易见了,
但是, 注意了,这个方法2 又真的恰当吗? 真的好吗?

我们来假设一种情况, 而且实际开发中必然会发生的情况:
这个框架更新了!!!对,就是更新了!!! 更新了显然又会回到之前的状态傻眼了吧?
**实际开发中,我们都会用cocoaPods来管理我们的第三方框架,
当某个框架更新之后, cocoaPods会下载最新的框架源码镶嵌到我们的项目中,
我们并不能保证AFNetworking这个框架一定会把我们需要的类型添加上去,
所以
每一次更新,我们都需要针对源码再做一次修改**很显然,这也是费力不讨好的;

那么有没有一劳永逸的方法呢? 别急,马上就来!!!

>>这里需要考虑到两种情况: 如下
>1. 如果你的APP只需要适配iOS7.0之前的版本,为了能够适配旧系统,需要使用 AFHTTPRequestOperationManager
>2. 如果你的APP只需要适配iOS7.0之后的版本,那么你需要 自定义的类是继承AFHTTPSessionManager的

这里我只简单的介绍iOS7.0之后的版本,

1> 自定义manager,继承自AFHTTPSessionManager

@interface DXHTTPManager : AFHTTPSessionManager

2> 在.m文件中,重写父类的manager方法 目的 : 添加类型

+ (instancetype)manager { DXHTTPManager *mgr = [super manager]; // 创建NSMutableSet对象 NSMutableSet *newSet = [NSMutableSet set]; // 添加我们需要的类型 newSet.set = mgr.responseSerializer.acceptableContentTypes; [newSet addObject:@"text/html"]; // 重写给 acceptableContentTypes赋值 mgr.responseSerializer.acceptableContentTypes = newSet; return mgr;}

3> 在发送请求的时候,使用我们自定义的类来发送请求

[[DXHTTPManager manager] GET:@"http://...." parameters:nil success:^(AFHTTPRequestOperation *operation, NSDictionary * responseObject) { NSLog(@"请求成功 -- %@",responseObject);} failure:^(AFHTTPRequestOperation *operation, NSError *error) { NSLog(@"请求失败 -- %@",error);}];

执行结果:

图片 5方法3执行结果:

结论:

**1.
使用方法3来解决这个bug,虽然看似比第一种,第二种要繁琐一些,实则可拓展性,和维护方面,要好得多,以后我们开发项目的时候,
只需要将我们自定义的这个类拖进去就可以了, 假如有需要新的类型的时候,
也只是简单的多配置一下类型即可,
**2.此外方法3还有一个好处,我们假设一个情景:
如果有一天我们使用的这个AFNetworking框架不再更新了,甚至作废了呢?
这种可能性还是有的,如果我们用方法1和方法2的话,
那个时候你会在项目中看见一堆一堆的bug,而如果我们使用的方法3的话,那么只需要简单的将继承的类更换成我们最新使用的框架里面的类,然后做一些简单的配置即可,这样维护起来的成本自然就轻松了,3.而这也正是我们代码重构,
和优化项目架构的思路之一!!!

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图