服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - IOS - iOS 使用Moya网络请求的实现方法

iOS 使用Moya网络请求的实现方法

2021-05-07 15:24海神Lewis IOS

这篇文章主要介绍了iOS 使用Moya网络请求的实现方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

由于前段时间写了这篇文章,最新moya已更新最新版本,故此也更新了下用法,本人已使用,故特意奉上最新的使用demo供参考。moya11.0.2demo

moya简介

moya是你的 app 中缺失的网络层。不用再去想在哪儿(或者如何)安放网络请求,moya 替你管理。

moya有几个比较好的特性:

  • 编译时检查正确的api端点访问.
  • 使你定义不同端点枚举值对应相应的用途更加明晰.
  • 提高测试地位从而使单元测试更加容易.

swift我们用 alamofire 来做网络库.而 moya 在alamofire的基础上又封装了一层,如下流程图说明 moya 的简单工作流程图:

 

 
iOS 使用Moya网络请求的实现方法

 

moya的官方下载地址点我强大的moya ,有具体的使用方法在demo里面有说明。

本文主要介绍一下 moya 的用法

  • 设置请求头部信息 设
  • 置超时时间
  • 自定义插件
  • 自签名证书

注意:以下所出现的 netapimanager 跟官网上demo的** github**是一样类型的文件,都是这个enum实现一个协议targettype,点进去可以看到targettype定义了我们发送一个网络请求所需要的东西,什么baseurl,parameter,method等一些计算性属性,我们要做的就是去实现这些东西,当然有带默认值的我们可以不去实现,但是设置头部信息跟超时时间就要修改这些系统默认设置了。

为了看得更加清楚,贴上 netapimanager 文件的内容

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
//
// netapimanager.swift
// nn110
//
// created by 陈亦海 on 2017/5/12.
// copyright © 2017年 陈亦海. all rights reserved.
//
 
import foundation
import moya
 
 
enum netapimanager {
 case show
 case upload(bodydata: data)
 case download
 case request(istouch: bool, body: dictionary<string, any>? ,isshow: bool)
}
 
 
extension netapimanager: targettype {
 var baseurl: url {//服务器地址
  
  switch self {
  case .request( _, _, _):
   return url(string: "https://www.pmphmall.com")!
  default:
   return url(string: "https://httpbin.org")!
  }
  
  
 }
 
 var path: string {//具体某个方法的路径
  switch self {
  case .show:
   return ""
  case .upload(_):
   return ""
  case .request(_, _, _):
   return "/app/json.do"
  case .download:
   return ""
  }
 }
 
 var method: moya.method {//请求的方法 get或者post之类的
  switch self {
  case .show:
   return .get
  case .request(_, _, _):
   return .post
  default:
   return .post
  }
 }
 
 var parameters: [string: any]? {//请求的get post给服务器的参数
  switch self {
  case .show:
   return nil
  case .request(_, _, _):
   return ["msg":"h4siaaaaaaaaa11sszjfiqi7eqpaegtvf6tp62w7smosqhkswdrs6zukvwoglwyv7rjhfbzjlnlzlon6lvqid4a+puxqrdukvnlwe1trczic/fjf2rpotuxmb84r1gmxbiaszizbhqdkeewjlz41zndkujchuqu3du7g4/pmvrnwarmlxukbv0j23xvahno3vx35wlgce6tluzzgpqjfuhngaczl6vhanxpmrlxjblmml6gdlwixxtdo7i+ieyc7xutircqxok4dotgargkh/inxvjfntne/uy46++hyailfufl4cv1z8wh5dgb2gnvfxmh5gm53tr13vqqreytcdxfknsmwkb+9saq77grnjmqufwohfxa/dellmb0kkfthoc/ronj1ml+z7qas82l3vwicvq+heitjtvzofw8risfn/jjxby4awvq427mcxqnyrfcsl7oeeu6wygw9yjtj1lokx0ell5fw4z071navzra9ebxwxkfyothgbb445cprmtc+//f73r1koyq3ltpec12xndr00nnq5/ymjitw3+w1z27lsolqgvctrxg4xdl9wvpdkh1tkiz/pukbghadaaa="]
  default:
   return nil
  
  }
 }
 
 var sampledata: data { //编码转义
  return "{}".data(using: string.encoding.utf8)!
 }
 
 var task: task { //一个请求任务事件
  
  switch self {
 
  
  case let .upload(data):
  return .upload(.multipart([multipartformdata(provider: .data(data), name: "file", filename: "gif.gif", mimetype: "image/gif")]))
   
  default:
   return .request
 
  }
 
  }
 
 var parameterencoding: parameterencoding {//编码的格式
  switch self {
  case .request(_, _, _):
   return urlencoding.default
  default:
   return urlencoding.default
  }
  
 }
 //以下两个参数是我自己写,用来控制网络加载的时候是否允许操作,跟是否要显示加载提示,这两个参数在自定义插件的时候会用到
 var touch: bool { //是否可以操作
  
  switch self {
  case .request(let istouch, _, _):
   return istouch
  default:
   return false
  }
  
 }
 
 var show: bool { //是否显示转圈提示
  
  switch self {
  case .request( _, _,let isshow):
   return isshow
  default:
   return false
  }
  
 }
 
 
}

如何设置moya请求头部信息

头部信息的设置在开发过程中很重要,如服务器生成的token,用户唯一标识等 我们直接上代码,不说那么多理论的东西,哈哈

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// mark: - 设置请求头部信息
let myendpointclosure = { (target: netapimanager) -> endpoint<netapimanager> in
 
 
 let url = target.baseurl.appendingpathcomponent(target.path).absolutestring
 let endpoint = endpoint<netapimanager>(
  url: url,
  sampleresponseclosure: { .networkresponse(200, target.sampledata) },
  method: target.method,
  parameters: target.parameters,
  parameterencoding: target.parameterencoding
 )
 
 //在这里设置你的http头部信息
 return endpoint.adding(newhttpheaderfields: [
  "content-type" : "application/x-www-form-urlencoded",
  "ecp-cookie" : ""
  ])
 
}

如何设置请求超时时间

?
1
2
3
4
5
6
7
8
// mark: - 设置请求超时时间
let requestclosure = { (endpoint: endpoint<netapimanager>, done: @escaping moyaprovider<netapimanager>.requestresultclosure) in
 
 guard var request = endpoint.urlrequest else { return }
 
 request.timeoutinterval = 30 //设置请求超时时间
 done(.success(request))
}

自定义插件

自定义插件必须 plugintype 协议的两个方法willsend与didreceive

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//
// mynetworkactivityplugin.swift
// nn110
//
// created by 陈亦海 on 2017/5/10.
// copyright © 2017年 cocoapods. all rights reserved.
//
 
import foundation
import result
import moya
 
 
/// network activity change notification type.
public enum mynetworkactivitychangetype {
 case began, ended
}
 
/// notify a request's network activity changes (request begins or ends).
public final class mynetworkactivityplugin: plugintype {
 
 
 
 public typealias mynetworkactivityclosure = (_ change: mynetworkactivitychangetype, _ target: targettype) -> void
 let mynetworkactivityclosure: mynetworkactivityclosure
 
 public init(newnetworkactivityclosure: @escaping mynetworkactivityclosure) {
  self.mynetworkactivityclosure = newnetworkactivityclosure
 }
 
 // mark: plugin
 
 /// called by the provider as soon as the request is about to start
 public func willsend(_ request: requesttype, target: targettype) {
  mynetworkactivityclosure(.began,target)
 }
 
 /// called by the provider as soon as a response arrives, even if the request is cancelled.
 public func didreceive(_ result: result<moya.response, moyaerror>, target: targettype) {
  mynetworkactivityclosure(.ended,target)
 }
}

使用自定义插件方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// mark: - 自定义的网络提示请求插件
let mynetworkplugin = mynetworkactivityplugin { (state,target) in
 if state == .began {
  //  swiftspinner.show("connecting...")
  
  let api = target as! netapimanager
  if api.show {
   print("我可以在这里写加载提示")
  }
  
  if !api.touch {
   print("我可以在这里写禁止用户操作,等待请求结束")
  }
 
  print("我开始请求\(api.touch)")
  
  uiapplication.shared.isnetworkactivityindicatorvisible = true
 } else {
  //  swiftspinner.show("request finish...")
  //  swiftspinner.hide()
  print("我结束请求")
  uiapplication.shared.isnetworkactivityindicatorvisible = false
  
 }
}

自签名证书

在16年的wwdc中,apple已表示将从2017年1月1日起,**所有新提交的app必须强制性应用https协议来进行网络请求。**默认情况下非https的网络访问是禁止的并且不能再通过简单粗暴的向info.plist中添加nsallowsarbitraryloads 设置绕过ats(app transport security)的限制(否则须在应用审核时进行说明并很可能会被拒)。所以还未进行相应配置的公司需要尽快将升级为https的事项提上进程了。本文将简述https及配置数字证书的原理并以配置实例和出现的问题进行说明,希望能对你提供帮助。(比心~)

iOS 使用Moya网络请求的实现方法

https: 简单来说,https就是http协议上再加一层加密处理的ssl协议,即http安全版。相比http,https可以保证内容在传输过程中不会被第三方查看、及时发现被第三方篡改的传输内容、防止身份冒充,从而更有效的保证网络数据的安全。 https客户端与服务器交互过程: 1、 客户端第一次请求时,服务器会返回一个包含公钥的数字证书给客户端; 2、 客户端生成对称加密密钥并用其得到的公钥对其加密后返回给服务器; 3、 服务器使用自己私钥对收到的加密数据解密,得到对称加密密钥并保存; 4、 然后双方通过对称加密的数据进行传输。

iOS 使用Moya网络请求的实现方法

数字证书: 在https客户端与服务器第一次交互时,服务端返回给客户端的数字证书是让客户端验证这个数字证书是不是服务端的,证书所有者是不是该服务器,确保数据由正确的服务端发来,没有被第三方篡改。数字证书可以保证数字证书里的公钥确实是这个证书的所有者(subject)的,或者证书可以用来确认对方身份。证书由公钥、证书主题(subject)、数字签名(digital signature)等内容组成。其中数字签名就是证书的防伪标签,目前使用最广泛的sha-rsa加密。 证书一般分为两种:

  1. 一种是向权威认证机构购买的证书,服务端使用该种证书时,因为苹果系统内置了其受信任的签名根证书,所以客户端不需额外的配置。为了证书安全,在证书发布机构公布证书时,证书的指纹算法都会加密后再和证书放到一起公布以防止他人伪造数字证书。而证书机构使用自己的私钥对其指纹算法加密,可以用内置在操作系统里的机构签名根证书来解密,以此保证证书的安全。
  2. 另一种是自己制作的证书,即自签名证书。好处是不需要花钱购2买,但使用这种证书是不会受信任的,所以 需要我们在代码中将该证书配置为信任证书.

自签名证书具体实现: 我们在使用自签名证书来实现https请求时,因为不像机构颁发的证书一样其签名根证书在系统中已经内置了,所以我们需要在app中内置自己服务器的签名根证书来验证数字证书。首先将服务端生成的.cer格式的根证书添加到项目中,注意在添加证书要一定要记得勾选要添加的targets。

 这里有个地方要注意 :苹果的ats要求服务端必须支持tls 1.2或以上版本;必须使用支持前向保密的密码;证书必须使用sha-256或者更好的签名hash算法来签名,如果证书无效,则会导致连接失败。由于我在生成的根证书时签名hash算法低于其要求,在配置完请求时一直报 nsurlerrorservercertificateuntrusted = -1202错误,希望大家可以注意到这一点。

那么如何在moya中使用自签名的证书来实现https网络请求呢,请期待下回我专门分享......需要自定义一个manager管理

综合使用的方法如下

定义一个公用的moya请求服务对象

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
let myapiprovider = moyaprovider<netapimanager>(endpointclosure: myendpointclosure,requestclosure: requestclosure, plugins: [networkloggerplugin(verbose: true, responsedataformatter: jsonresponsedataformatter),mynetworkplugin])
 
// mark: -创建一个moya请求
func sendrequest(_ postdict: dictionary<string, any>? = nil,
     success:@escaping (dictionary<string, any>)->(),
     failure:@escaping (moyaerror)->()) -> cancellable? {
 
 let request = myapiprovider.request(.show) { result in
  switch result {
  case let .success(moyaresponse):
   
   
   do {
    let any = try moyaresponse.mapjson()
    let data = moyaresponse.data
    let statuscode = moyaresponse.statuscode
    mylog("\(data) --- \(statuscode) ----- \(any)")
    
    success(["":""])
    
 
   } catch {
    
   }
   
   
   
  case let .failure(error):
   
   print(error)
   failure(error)
  }
 }
 
 return request
}

取消所有的moya请求

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// mark: -取消所有请求
func cancelallrequest() {
// myapiprovider.manager.session.invalidateandcancel() //取消所有请求
 myapiprovider.manager.session.gettaskswithcompletionhandler { datatasks, uploadtasks, downloadtasks in
  datatasks.foreach { $0.cancel() }
  uploadtasks.foreach { $0.cancel() }
  downloadtasks.foreach { $0.cancel() }
 }
 
 //let sessionmanager = alamofire.sessionmanager.default
 //sessionmanager.session.gettaskswithcompletionhandler { datatasks, uploadtasks, downloadtasks in
 // datatasks.foreach { $0.cancel() }
 // uploadtasks.foreach { $0.cancel() }
 // downloadtasks.foreach { $0.cancel() }
 //}
 
}

 完毕,待续更高级的用法...

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://juejin.im/post/5b56854451882562814961a5

延伸 · 阅读

精彩推荐
  • IOSIOS开发之字典转字符串的实例详解

    IOS开发之字典转字符串的实例详解

    这篇文章主要介绍了IOS开发之字典转字符串的实例详解的相关资料,希望通过本文能帮助到大家,让大家掌握这样的方法,需要的朋友可以参考下...

    苦练内功5832021-04-01
  • IOSiOS 雷达效果实例详解

    iOS 雷达效果实例详解

    这篇文章主要介绍了iOS 雷达效果实例详解的相关资料,需要的朋友可以参考下...

    SimpleWorld11022021-01-28
  • IOS解析iOS开发中的FirstResponder第一响应对象

    解析iOS开发中的FirstResponder第一响应对象

    这篇文章主要介绍了解析iOS开发中的FirstResponder第一响应对象,包括View的FirstResponder的释放问题,需要的朋友可以参考下...

    一片枫叶4662020-12-25
  • IOS关于iOS自适应cell行高的那些事儿

    关于iOS自适应cell行高的那些事儿

    这篇文章主要给大家介绍了关于iOS自适应cell行高的那些事儿,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的...

    daisy6092021-05-17
  • IOSiOS通过逆向理解Block的内存模型

    iOS通过逆向理解Block的内存模型

    自从对 iOS 的逆向初窥门径后,我也经常通过它来分析一些比较大的应用,参考一下这些应用中某些功能的实现。这个探索的过程乐趣多多,不仅能满足自...

    Swiftyper12832021-03-03
  • IOSIOS 屏幕适配方案实现缩放window的示例代码

    IOS 屏幕适配方案实现缩放window的示例代码

    这篇文章主要介绍了IOS 屏幕适配方案实现缩放window的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要...

    xiari5772021-06-01
  • IOSiOS布局渲染之UIView方法的调用时机详解

    iOS布局渲染之UIView方法的调用时机详解

    在你刚开始开发 iOS 应用时,最难避免或者是调试的就是和布局相关的问题,下面这篇文章主要给大家介绍了关于iOS布局渲染之UIView方法调用时机的相关资料...

    windtersharp7642021-05-04
  • IOSiOS中tableview 两级cell的展开与收回的示例代码

    iOS中tableview 两级cell的展开与收回的示例代码

    本篇文章主要介绍了iOS中tableview 两级cell的展开与收回的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    J_Kang3862021-04-22