Objective-C的网络请求-请求与响应相关学习笔记

�Http协议 - 超文本传输协议

Http协议是应用层协议,底层要求的传输协议必须是可靠的传输协议,通常是TCP协议。

Http协议规定客户端与服务端之间的通讯方式为一次请求一次响应,即客户端发起链接,并向服务端发送一个标准的Http请求,而后服务端处理该请求后给予一个标准的Http响应,所以对于Http协议而言,服务端永远不会主动响应客户端。

Http协议现在常用的是Http1.1版本协议在Http1.0版本时,要求一次会话只能完成一次请求与响应。即客户端与服务端建立TCP链接后,发送一次请求,当服务端给予响应后立即断开链接。在Http1.1版本中,一个较大的改动就是在一次会话中可以完成多次请求与响应

Http协议中规定的请求与响应的内容大部分是文本数据,但是这些字符只能是ISO88590-1编码中出现的字符,这意味着在Http协议中是不能直接出现中文等字符的。

Http中的请求定义Request,一个请求通常包含三部分:请求行,消息头,消息正文

请求行的格式为: Method(请求方式) URL(请求的资源路径) Protocol(协议版本)

消息头是客户端发起请求时传递过来的一些附加信息,比如描述客户端是谁,以及希望与客户端如何通讯,描述消息正文的长度以及内容类型等信息。消息头由若干行组成,每一行为一个消息头,格式为Name :Value(CRLF)格式,当所有消息头全部发送完毕后会单独发送一个CRLF(回车符(CR)和换行符(LF))。一些消息头的名称的含义:

  • Content - Type :用于说明消息正文的内容是什么类型的数据。
  • Content - Length : 用于说明消息正文的数据长度(字节量)。
  • If-Modified-Since :与响应头的Last-Modified配合使用,当客户端第二次请求已响应过的URL时,根据HTTP协议的规定,浏览器会向服务器传送If-Modified-Since报头,询问该时间之后文件是否有被修改过。如果服务器端的资源没有变化,则自动返回 HTTP 304(Not Changed)状态码,内容为空;当服务器端代码发生改变或者重启服务器时,则重新发出资源,返回和第一次请求时类似。

消息正文是二进制数据,内容是用户实际向服务端传递的数据。它可能是用户传递的注册用户的信息,上传的附件内容等。一个请求中可以不含有消息正文部分。请求中是否含有消息正文的一个重要标志是请求的消息头中是否含有以下两个头:

  • Content - Type :用于说明消息正文的内容是什么类型的数据。
  • Content - Length : 用于说明消息正文的数据长度(字节量)。

Http响应是服务器端发送给客户端的内容。一个响应应当包含三部分:状态行,响应头,响应正文

状态行格式分为三部分:Protocol(协议版本) Status_code(状态代码) Status_reason(状态描述) (CRLF)

状态代码是一个3位数字,分为5类:

  • 1XX :消息,在Http1.0协议时为保留部分,未使用。
  • 2XX :成功,表示客户端请求成功
  • 3XX:重定向,表示客户端需要进一步操作后才能完成请求
  • 4XX:客户端错误,表示客户端的请求无效
  • 5XX:服务端错误,表示请求被接受,但是服务器处理时发生了错误。

常见状态代码:

  • 200 : 请求已接受,并正常响应客户端。
  • 302 : 要求客户端进一步请求服务端指定的路径
  • 404 : 客户端请求的资源未找到
  • 500 : 服务端发生错误

响应头的格式与意义同请求中的信息头一样。一些响应头的名称的含义:

  • Content - Type : 说明响应正文的数据类型。
  • Content- Length :说明响应正文的长度(字节量)。
  • Last-Modified :标记此文件在服务器端最后被修改的时间。格式类似:Fri , 12 May 2006 18:53:33 GMT。
  • Etag :HTTP协议ETag为“被请求变量的实体值”(另一种说法是资源的特定版本的标识符)。

响应正文也是二进制数据,是服务端响应给客户端的实体数据,通常就是客户端所请求的资源。一个响应中是否含有响应正文可以通过响应头中的两个信息得知:

  • Content - Type : 说明响应正文的数据类型。
  • Content- Length :说明响应正文的长度(字节量)。

客户端就是通过这两个头来读取并理解响应正文内容的。

HTTP Streaming - 推送形式的数据传输技术

HTTP Streaming是一种推送形式的数据传输技术,它通过无限期开放的HTTP连接让web服务器(tomcat)能持续向客户端(浏览器)传送数据。在HTTP Streaming下,web服务器会握住客户端的请求并让返回保持开放,这样服务器可以通过返回一直发送数据。这个请求-返回的通道会一直开启,除非我们显示的要求关闭。

NSURLRequest - 封装了要加载的URL的请求对象

独立于协议或URL方案的URL加载请求。NSURLRequest封装了加载请求的两个基本属性:要加载的URL和用于加载它的策略。此外,对于HTTP和HTTPS请求,URLRequest包括HTTP方法(GET、POST等)和HTTP头。最后,如自定义协议属性中所述,自定义协议可以支持自定义属性。NSURLRequest的可变子类是NSMutableURLRequest。

NSURLRequest仅表示有关请求的信息。使用其他类(如NSURLSession)将请求发送到服务器。

NSURLRequest常用属性
@property (class, readonly) BOOL supportsSecureCoding;

属性描述:类属性,一个布尔值,指示NSURLRequest是否实现NSSecureCoding协议,以支持安全编码。YES表示NSURLRequest实现了NSSecureCoding协议。

@property (nullable, readonly, copy) NSURL *URL;

属性描述:请求的URL。

@property (readonly) NSURLRequestCachePolicy cachePolicy;

属性描述:请求的缓存策略。

  • NSURLRequestCachePolicy枚举的缓存策略:
typedef NS_ENUM(NSUInteger, NSURLRequestCachePolicy)
{
    //使用协议实现中定义的缓存逻辑(如果有的话)
    //这是URL加载请求的默认策略。
    NSURLRequestUseProtocolCachePolicy = 0,
    //URL加载只能从原始源加载
    //策略指定不应使用现有缓存数据来满足URL加载请求
    //如果要进行HTTP或HTTPS字节范围请求,请始终使用此策略
    NSURLRequestReloadIgnoringLocalCacheData = 1,
    //忽略本地缓存数据,并指示代理和其他中间设备在协议允许的范围内忽略它们的缓存。
    //早于macOS 10.15、iOS 13、watchOS 6和tvOS 13的版本不实现此常量
    NSURLRequestReloadIgnoringLocalAndRemoteCacheData = 4,
    //替换为NSURLRequestReloadIgnoringLocalCacheData
    NSURLRequestReloadIgnoringCacheData = NSURLRequestReloadIgnoringLocalCacheData,
    //使用现有的缓存数据,无论是否在缓存有效期内,只有在没有缓存数据时才从原始源加载。
    NSURLRequestReturnCacheDataElseLoad = 2,
    //使用现有的缓存数据,无论是否在缓存有效期内,如果没有可用的缓存数据则认为加载失败
    //此常量指定类似于“脱机”模式的行为
    NSURLRequestReturnCacheDataDontLoad = 3,
    //如果原始源可以验证缓存数据,则使用缓存数据,否则在原始源加载
    //macOS 10.15、iOS 13、watchOS 6和tvOS 13之前的版本没有实现这个常量
    NSURLRequestReloadRevalidatingCacheData = 5,
};
@property (readonly) NSTimeInterval timeoutInterval;

属性描述请求超时的时间间隔,单位为秒。如果在连接尝试期间,请求闲置的时间超过了超时间隔,则认为请求已经超时。

@property (nullable, readonly, copy) NSURL *mainDocumentURL;

属性描述主HTML文档的用于顶级框的URL(如果已知),可以是nil。如果Cookie接受策略是NSHTTPCookieAcceptPolicyOnlyFromMainDocumentDomain,则该URL用于确定Cookie是否应该被接受。

@property (readonly) NSURLRequestNetworkServiceType networkServiceType API_AVAILABLE(macos(10.7), ios(4.0), watchos(2.0), tvos(9.0));

属性描述请求的网络服务类型。网络服务类型向操作系统提供关于底层流量用于什么的提示。此提示增强了系统对流量进行优先级排序、确定唤醒蜂窝或Wi-Fi无线所需的速度等的能力。

  • NSURLRequestNetworkServiceType枚举的请求的网络服务类型:
typedef NS_ENUM(NSUInteger, NSURLRequestNetworkServiceType)
{ 
    //标准网络流量的服务类型
    NSURLNetworkServiceTypeDefault = 0,
    //VoIP流量的服务类型
    NSURLNetworkServiceTypeVoIP API_DEPRECATED("Use PushKit for VoIP control purposes", macos(10.7,10.15), ios(4.0,13.0), watchos(2.0,6.0), tvos(9.0,13.0)) = 1,    
    //视频流量的服务类型
    NSURLNetworkServiceTypeVideo = 2,
    //后台流量的服务类型
    NSURLNetworkServiceTypeBackground = 3, 
    //语音业务的服务类型
    NSURLNetworkServiceTypeVoice = 4,     
    //用户正在等待的数据的服务类型
    NSURLNetworkServiceTypeResponsiveData = 6, 
    //用于流式传输音频/视频数据的服务类型
    NSURLNetworkServiceTypeAVStreaming API_AVAILABLE(macosx(10.9), ios(7.0), watchos(2.0), tvos(9.0)) = 8 , 
    //响应式(时间敏感)音频/视频数据的服务类型
    NSURLNetworkServiceTypeResponsiveAV API_AVAILABLE(macosx(10.9), ios(7.0), watchos(2.0), tvos(9.0)) = 9, 
    //呼叫信令的服务类型
    NSURLNetworkServiceTypeCallSignaling API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0)) = 11, // Call Signaling
};
@property (readonly) BOOL allowsCellularAccess  API_AVAILABLE(macos(10.8), ios(6.0), watchos(2.0), tvos(9.0));

属性描述:一个布尔值,指示是否允许请求使用蜂窝无线网络(如果存在)。如果为YES可以使用蜂窝无线网络,否则为NO。

@property (readonly) BOOL allowsExpensiveNetworkAccess API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0));

属性描述:一个布尔值,指示连接是否可以使用系统认为昂贵的网络接口。系统根据网络接口的性质和其他因素确定什么构成“昂贵”?iOS 13认为大多数蜂窝网络和个人热点都很昂贵。如果没有可用的非扩展网络接口,并且请求的该属性为NO,则从请求创建的任何任务都将失败。在这种情况下,任务失败时提供的错误具有networkUnavailableReason属性,其值为NSURLErrorNetworkUnavailableReasonExpensive。

@property BOOL allowsConstrainedNetworkAccess API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0));

属性描述:一个布尔值,指示当用户指定了低数据模式时,连接是否可以使用网络。在iOS 13及更高版本中,用户可以在“设置”应用程序中将设备设置为使用“低数据模式”作为“蜂窝数据选项”之一。用户可以打开低数据模式以减少应用程序的网络数据使用。当用户打开“低数据模式”时,此属性控制请求的行为。如果没有可用的非约束网络接口,并且请求的该属性为NO,则从请求创建的任何连接都将失败。在这种情况下,连接失败时提供的错误具有networkUnavailableReason属性,其值为NSURLErrorNetworkUnavailableReasonConstrained。

@property BOOL assumesHTTP3Capable API_AVAILABLE(macos(11.3), ios(14.5), watchos(7.4), tvos(14.5));

属性描述:如果已知服务器端点支持HTTP/3,则为YES,默认为NO。

@property BOOL requiresDNSSECValidation API_AVAILABLE(macos(13.0), ios(16.0), watchos(9.0), tvos(16.0));

属性描述:如果该请求的DNS查找需要DNSSEC验证则为YES,否则为NO。

@property NSURLRequestAttribution attribution API_AVAILABLE(macos(12.0), ios(15.0), watchos(8.0), tvos(15.0));

属性描述发起网络请求的实体。如果您不设置这个值,系统将假设NSURLRequestAttributionDeveloper的值。对于应用程序发出的任何非用户明确发出的网络请求,请使用此默认值。这包括您向自己的服务器发出的请求,甚至在加载用户数据时也是如此。它还包括用户选择的链接,但您可以在加载内容之前以任何方式修改链接(包括添加HTTP头)。只有当用户明确地发出请求时,比如当用户输入一个URL或点击或点击一个可以读取的URL时,并且只有当你的应用程序加载和显示数据而不改变请求时,才将这个值设置为NSURLRequestAttributionUser。

  • NSURLRequestAttribution枚举的网络请求的实体:
typedef NS_ENUM(NSUInteger, NSURLRequestAttribution)
{
    //开发人员发起的网络请求,将此值用于应用程序出于任何目的(用户明确访问链接时除外)发出的URL请求的归因参数
    //这是默认值
    NSURLRequestAttributionDeveloper = 0,
    //用户明确指示应用程序发出网络请求,将此值用于满足用户访问显式且未经修改URL所发起的URL请求的归因参数
    NSURLRequestAttributionUser = 1,
} API_AVAILABLE(macos(12.0), ios(15.0), watchos(8.0), tvos(15.0));
NSURLRequest常用函数
+ (instancetype)requestWithURL:(NSURL *)URL;

函数描述初始化请求的类函数,使用给定的URL和默认缓存策略(NSURLRequestUseProtocolCachePolicy)和默认超时间隔(60秒)初始化请求。

参数 :

URL :用于初始化请求的URL。

返回值 :完成初始化的NSURLRequest的URL请求对象。

+ (instancetype)requestWithURL:(NSURL *)URL cachePolicy:(NSURLRequestCachePolicy)cachePolicy timeoutInterval:(NSTimeInterval)timeoutInterval;

函数描述初始化请求的类函数,使用给定的URL、缓存策略和超时间隔初始化请求。

参数 :

URL :用于初始化请求的URL。

cachePolicy :用于初始化请求的缓存策略。

timeoutInterval :用于初始化请求的超时间隔。

返回值 :完成初始化的NSURLRequest的URL请求对象。

- (instancetype)initWithURL:(NSURL *)URL;

函数描述初始化请求的实例函数,使用给定的URL和默认缓存策略(NSURLRequestUseProtocolCachePolicy)和默认超时间隔(60秒)初始化请求。

参数 :

URL :用于初始化请求的URL。

返回值 :完成初始化的NSURLRequest的URL请求对象。

- (instancetype)initWithURL:(NSURL *)URL cachePolicy:(NSURLRequestCachePolicy)cachePolicy timeoutInterval:(NSTimeInterval)timeoutInterval NS_DESIGNATED_INITIALIZER;

函数描述初始化请求的实例函数,使用给定的URL、缓存策略和超时间隔初始化请求。

参数 :

URL :用于初始化请求的URL。

cachePolicy :用于初始化请求的缓存策略。

timeoutInterval :用于初始化请求的超时间隔。

返回值 :完成初始化的NSURLRequest的URL请求对象。

NSURLRequest (NSHTTPURLRequest) - 分类对HTTP相关URL请求的扩展

常用属性
@property (nullable, readonly, copy) NSString *HTTPMethod;

属性描述HTTP请求方法,默认的HTTP方法是“GET”。

@property (nullable, readonly, copy) NSDictionary<NSString *, NSString *> *allHTTPHeaderFields;

属性描述:包含请求的所有HTTP请求头字段的字典。

@property (nullable, readonly, copy) NSData *HTTPBody;

属性描述请求主体。该数据作为请求的消息体发送,如HTTP中POST请求那样。

@property (nullable, readonly, retain) NSInputStream *HTTPBodyStream;

属性描述请求主体作为输入流。如果请求主体中没有设置流,则为nil。返回的流仅供检查,以任何方式操作流都是不安全的。调用方会有一个HTTP正文或者一个HTTP正文流,对于一个请求只能设置一个。复制NSURLRequest对象时,HTTP主体流会保留,但使用NSCoding协议归档请求时,HTTP主体流就会丢失。

@property (readonly) BOOL HTTPShouldHandleCookies;

属性描述:一个布尔值,指示是否将对此请求使用默认cookie处理。如果此请求将使用默认cookie处理,则为YES,否则为NO。默认值为YES。

@property (readonly) BOOL HTTPShouldUsePipelining API_AVAILABLE(macos(10.7), ios(4.0), watchos(2.0), tvos(9.0));

属性描述:一个布尔值,指示请求在接收到来自先前传输的响应之前是否应继续传输数据。如果请求应继续传输数据,则为YES,否则为NO。

常用函数
- (nullable NSString *)valueForHTTPHeaderField:(NSString *)field;

函数描述:返回指定HTTP请求头字段的值。

参数 :

field :要返回的请求头字段的名称。为了与HTTP RFC保持一致,HTTP请求头字段名不区分大小写。

返回值 :与请求头字段相关联的值,如果没有相应的请求头字段,则为nil。

NSMutableURLRequest - NSURLRequest的可变子类

独立于协议或URL模式的可变URL加载请求。NSMutableURLRequest是NSURLRequest的一个子类,它允许更改请求的属性

NSMutableURLRequest仅表示有关请求的信息。使用其他类(如NSURLSession)将请求发送到服务器。

基于请求创建网络操作的类对该请求进行深拷贝。因此,在创建网络操作之后更改请求对正在进行的操作没有影响。例如,如果您使用dataTaskWithRequest:completionHandler:从请求创建数据任务,然后稍后更改请求,那么数据任务将继续使用原始请求。

NSMutableURLRequest常用属性

重写了自NSURLRequest类中继承的属性,在NSMutableURLRequest类中,这些属性的值是可读写的:

//请求的URL
@property (nullable, copy) NSURL *URL;
//请求的缓存策略
@property NSURLRequestCachePolicy cachePolicy;
//请求超时的时间间隔
@property NSTimeInterval timeoutInterval;
//主HTML文档的用于顶级框的URL
@property (nullable, copy) NSURL *mainDocumentURL;
//请求的网络服务类型
@property NSURLRequestNetworkServiceType networkServiceType API_AVAILABLE(macos(10.7), ios(4.0), watchos(2.0), tvos(9.0));
//是否允许请求使用蜂窝无线网络
@property BOOL allowsCellularAccess API_AVAILABLE(macos(10.8), ios(6.0), watchos(2.0), tvos(9.0));
//连接是否可以使用系统认为昂贵的网络接口
@property BOOL allowsExpensiveNetworkAccess API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0));
//当用户指定了低数据模式时,连接是否可以使用网络
@property BOOL allowsConstrainedNetworkAccess API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0));
//服务器端点是否支持HTTP/3
@property BOOL assumesHTTP3Capable API_AVAILABLE(macos(11.3), ios(14.5), watchos(7.4), tvos(14.5));
//请求的DNS查找是否需要DNSSEC验证
@property BOOL requiresDNSSECValidation API_AVAILABLE(macos(13.0), ios(16.0), watchos(9.0), tvos(16.0));
//发起网络请求的实体
@property NSURLRequestAttribution attribution API_AVAILABLE(macos(12.0), ios(15.0), watchos(8.0), tvos(15.0));

\color{red}{例如:设置请求的部分属性:}

//通过属性设置Url
[request setURL:url];
//通过属性设置请求方式
[request setHTTPMethod:@"POST"];
//通过属性设置请求主体
//字符串格式化百度key与百度语音key
NSString *valueStr = [NSString stringWithFormat:@"grant_type=client_credentials&client_id=%@&client_secret=%@",BaiduKey,BaiduSpeechSecretKey];
//返回一个NSData对象,该对象包含使用给定编码编码的接收器的表示形式。
NSData *postData = [valueStr dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
[request setHTTPBody:postData];
//通过属性设置请求头
//application/x-www-form-urlencoded : 数据被编码为名称/值对,标准编码格式
request.allHTTPHeaderFields = @{@"Content-Type":@"application/x-www-form-urlencoded",@"User-Agent":@"szyapp/ios"};

NSMutableURLRequest (NSMutableHTTPURLRequest) - 分类对HTTP相关可变URL请求的扩展

重写了自NSURLRequest类中继承的属性,在NSMutableURLRequest (NSMutableHTTPURLRequest)分类中,这些属性的值是可读写的。

常用属性
//HTTP请求方法
@property (copy) NSString *HTTPMethod;
//含请求的所有HTTP请求头字段的字典
@property (nullable, copy) NSDictionary<NSString *, NSString *> *allHTTPHeaderFields;
//请求主体
@property (nullable, copy) NSData *HTTPBody;
//请求主体作为输入流
@property (nullable, retain) NSInputStream *HTTPBodyStream;
//是否将对此请求使用默认cookie处理
@property BOOL HTTPShouldHandleCookies;
//在接收到来自先前传输的响应之前是否应继续传输数据
@property BOOL HTTPShouldUsePipelining API_AVAILABLE(macos(10.7), ios(4.0), watchos(2.0), tvos(9.0));
常用函数
- (void)setValue:(nullable NSString *)value forHTTPHeaderField:(NSString *)field;

函数描述设置请求头字段的值。某些请求头字段是保留的。不要使用此方法设置此类请求头头。例如不需要设置Content-Length标头。具体请参阅保留的HTTP标头。

参数 :

value :请求头字段的新值,字段的任何现有值都将被新值替换。

field :要设置的请求头字段的名称。根据HTTP RFC,HTTP请求头字段名称不区分大小写。

- (void)addValue:(NSString *)value forHTTPHeaderField:(NSString *)field;

函数描述向请求头字段添加值。此方法提供了向请求头字段增量添加值的能力。如果先前为指定字段设置了值,则使用适当的字段分隔符(逗号)将提供的值附加到现有值。某些请求头字段是保留的(请参阅保留的HTTP标头),不要使用此方法更改此类请求头。

参数 :

value :添加到请求头字段的值。

field :请求头字段的名称。根据HTTP RFC,HTTP请求头字段名称不区分大小写。

NSURLResponse - 与URL加载请求的响应相关联的元数据

与URL加载请求的响应相关联的元数据,独立于协议和URL方案。相关的NSHTTPURLResponse类是NSURLResponse的一个常用子类,它的对象表示对HTTPURL加载请求的响应,并存储额外的协议特定信息,如响应头。每当发送一个HTTP请求,你得到的NSURLResponse对象实际上是NSHTTPURLResponse类的一个实例。NSURLResponse对象不包含表示URL内容的实际字节。相反,通过代理调用一次返回一部分数据,或者在请求完成时返回全部数据,这取决于用于发起请求的方法和类。

NSURLResponse常用属性

@property (nullable, readonly, copy) NSURL *URL;

属性描述响应的URL

@property (nullable, readonly, copy) NSString *MIMEType;

属性描述响应的MIME(互联网媒体类型)类型。MIME类型通常由响应的原始源提供。但是,如果可以确定响应的源不正确地报告了信息,则协议实现可以更改或纠正该值。如果响应的原始源没有提供MIME类型,则可能会尝试猜测MIME类型。

@property (readonly) long long expectedContentLength;

属性描述响应内容的预期长度。如果不能确定长度,这个属性的值是NSURLResponseUnknownLength。一些协议实现报告内容长度作为响应的一部分,但并不是所有协议都保证交付这么多数据。应用程序应该准备好处理或多或少的数据。

@property (nullable, readonly, copy) NSString *textEncodingName;

属性描述由响应的原始源提供的文本编码的名称。如果协议没有提供文本编码,该属性的值为nil。可以通过调用CFStringConvertIANACharSetNameToEncoding将此字符串转换为CFStringEncoding值。随后可以通过调用CFStringConvertEncodingToNSStringEncoding将该值转换为NSStringEncoding值。

\color{red}{例如:使用由响应的原始源提供的文本编码的名称转换数据}

//通过基于请求对象创建请求任务
[[session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
  if(error == nil){
    //请求成功
    NSLog(@"%@",response.textEncodingName);
    //返回与给定IANA注册表“字符集”名称最接近的映射的CoreFoundation编码常量。如果名称无法识别,则返回kCFStringEncodingInvalidId常量。
    CFStringEncoding cfEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef) [response textEncodingName]);
    //返回与给定的CoreFoundation编码常量最接近的Cocoa编码常量。
    NSStringEncoding encoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding);
    //通过使用编码将数据中的字节转换为UTF-16代码单元而初始化的NSString对象
    NSString *text = [[NSString alloc] initWithData:data encoding:encoding];
    NSLog(@"%@",text);
  }else{
    NSLog(@"%@",error);
  }
}]resume];
@property (nullable, readonly, copy) NSString *suggestedFilename;

属性描述响应数据的建议文件名。访问此属性将尝试使用以下信息生成文件名,顺序如下:

  • 使用内容处置标头指定的文件名。
  • URL的最后一个路径组件。
  • URL的主机。
  • 如果URL的主机无法转换为有效的文件名,则使用文件名“unknown”。
    在大多数情况下,此属性会根据MIME类型附加适当的文件扩展名。无论资源是否保存到磁盘,访问此属性始终返回有效的文件名。

NSHTTPURLResponse - 提供访问特定于HTTP协议响应的信息的方法

NSHTTPURLResponse类是NSURLResponse的一个子类,它提供了访问特定于HTTP协议响应的信息的方法。无论何时发出HTTP URL加载请求,从NSURLSession、NSURLConnection或NSURLDownload类返回的任何响应对象都是NSHTTPURLResponse类的实例。

NSHTTPURLResponse常用属性

@property (readonly) NSInteger statusCode;

属性描述 :响应的HTTP状态代码

@property (readonly, copy) NSDictionary *allHeaderFields;

属性描述所有的HTTP响应头字段。此属性的值是一个字典,其中包含作为服务器响应的一部分接所收到的所有HTTP响应头字段。通过检查这个字典,客户端可以看到HTTP服务器返回的“原始”响应头信息。这个字典中的键是响应头字段名,从服务器接收到的。

NSHTTPURLResponse常用函数

- (nullable NSString *)valueForHTTPHeaderField:(NSString *)field API_AVAILABLE(macos(10.15), ios(13.0), watchos(6.0), tvos(13.0));

函数描述返回与给定响应头字段对应的值。与HTTP RFC保持一致,HTTP响应头字段名不区分大小写。

参数 :

field : 要检索的响应头字段的名称。名称不区分大小写。

返回值 : 与给定响应头字段关联的值,如果没有值与字段关联,则为nil。

+ (NSString *)localizedStringForStatusCode:(NSInteger)statusCode;

函数描述 :返回与指定的HTTP状态代码相对应的本地化字符串

参数 :

statusCode : HTTP状态代码。详见RFC 2616。

返回值 :适合向用户显示的描述指定状态代码的本地化字符串。

\color{red}{请求百度语音识别REST的练习代码片段:}

//
//  TextCodeController.m
//  FrameworksTest

#import "TestCodeController.h"
#import <AVFoundation/AVFoundation.h>


@interface TestCodeController ()

@property (nonatomic, strong) AVPlayer *player;

@end

@implementation TestCodeController

- (void)viewDidLoad {
    
    [super viewDidLoad];
    self.navigationItem.title = @"测试代码控制器";
    [self playContent:@"中国加油"];

}

- (void)playContent:(NSString *)content{
    AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:[NSURL URLWithString:[self requestBaiDuREST:content]] options:nil];
    AVPlayerItem *songitem = [[AVPlayerItem alloc] initWithAsset:asset];
    self.player = [[AVPlayer alloc] initWithPlayerItem:songitem];
    //播放音量
    self.player.volume = 1;
    //添加观察者
    [songitem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
}

///观察者执行的函数
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    if ([keyPath isEqualToString:@"status"]) {
        AVPlayerItem *item = (AVPlayerItem *)object;
        if (item.status == AVPlayerItemStatusReadyToPlay) {
            //开始播放当前项
            [self.player play];
        }
    }
}

///请求百度语音识别REST
- (NSString *)requestBaiDuREST:(NSString *)content{
    
    NSString *unencodedString = [self URLEncodedString:content];
    
    static NSString * const BaiduKey = @"不给你们看";
    static NSString * const BaiduSpeechSecretKey = @"这个也不给你们看";
    //路径
    NSURL *url = [[NSURL alloc]initWithString:@"https://aip.baidubce.com/oauth/2.0/token"];
    //可变URL加载请求
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc]init];
    //设置要请求的路径
    [request setURL:url];
    //设置HTTP请求方式
    [request setHTTPMethod:@"POST"];
    //字符串格式化百度key与百度语音key
    NSString *valueStr = [NSString stringWithFormat:@"grant_type=client_credentials&client_id=%@&client_secret=%@",BaiduKey,BaiduSpeechSecretKey];
    //返回一个NSData对象,该对象包含使用给定编码编码的接收器的表示形式。
    NSData *postData = [valueStr dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
    //设置请求主体
    [request setHTTPBody:postData];
    //纪录completionHandler代码块中的异常
    NSError __block *blockError = nil;
    //纪录completionHandler代码块中的数据
    NSData __block *blockdData = nil;
    //纪录completionHandler代码块中的响应对象
    NSURLResponse __block *URLResponse = nil;
    //使用初始值创建新的计数信号量。
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    
    NSURLSession *session = [NSURLSession sharedSession];
    [[session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        blockError = error;
        blockdData = data ;
        URLResponse = response;
        //信号量(递增),唤醒线程
        dispatch_semaphore_signal(semaphore);
        
    }] resume];
    //等待(减少)信号量。阻塞线程
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    //如果blockdData为nil说明发生了异常
    if (blockdData == nil) {
        NSLog(@"send request failed: %@", blockError);
    }else{
        //将blockdData以NSUTF8StringEncoding解析为字符串
        NSString *receiveStr = [[NSString alloc]initWithData:blockdData encoding:NSUTF8StringEncoding];
        //返回以NSUTF8StringEncoding编码NSData对象
        NSData * datas = [receiveStr dataUsingEncoding:NSUTF8StringEncoding];
        //将datas转换为字典
        NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:datas options:NSJSONReadingMutableLeaves error:nil];
        //如果字典中有access_token对应的值
        if([[jsonDict objectForKey:@"access_token"] length] > 0){
            //字符串格式化access_token对应的值
            NSString *valueStr = [NSString stringWithFormat:@"tex=%@&tok=%@&cuid=013473005841386&ctp=1&lan=zh",unencodedString,[jsonDict objectForKey:@"access_token"]];
            
            NSURLSession *session = [NSURLSession sharedSession];
            [[session dataTaskWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"https://tsn.baidu.com/text2audio?%@",valueStr]]
                      completionHandler:^(NSData *data,
                                          NSURLResponse *response,
                                          NSError *error) {
                if(error != nil){
                    NSLog(@"发生异常了");
                }
                
              }] resume];
            //将这段字符串返回
            NSString *urlStr = [NSString stringWithFormat:@"https://tsn.baidu.com/text2audio?%@",valueStr];
            return urlStr;
        }
    }
    return nil;
}

///URL编码的字符串
- (NSString *)URLEncodedString:(NSString *)str{
    
    NSString *unencodedString = str;
    NSString *encodedString = [unencodedString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
    
    return encodedString;
}

///URL解码字符串
- (NSString *)URLDecodedString:(NSString *)str{
    
    NSString *encodedString = str;
    NSString *decodedString = [encodedString stringByRemovingPercentEncoding];
    return decodedString;
}

@end

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

推荐阅读更多精彩内容