LNHttpManager.swift 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. //
  2. // LNHttpManager.swift
  3. // Lanu
  4. //
  5. // Created by OneeChan on 2025/11/6.
  6. //
  7. import Foundation
  8. /// HTTP请求方法枚举
  9. enum HTTPMethod: String {
  10. case get = "GET"
  11. case post = "POST"
  12. case put = "PUT"
  13. case delete = "DELETE"
  14. }
  15. /// HTTP请求错误枚举
  16. enum LNHttpError: Error, LocalizedError {
  17. case invalidURL
  18. case invalidResponse
  19. case networkError(Error)
  20. case parsingError(Error)
  21. case statusCode(Int)
  22. case serverError(Int, String)
  23. var errorDesc: String {
  24. switch self {
  25. case .invalidURL: "无效的URL"
  26. case .invalidResponse: "无效的响应"
  27. case .networkError(let error): error.localizedDescription
  28. case .parsingError(let error): error.localizedDescription
  29. case .statusCode(let code): .init(key: "B00125", code)
  30. case .serverError(_, let error): error
  31. }
  32. }
  33. }
  34. enum LNHttpCommonErrorCode: Int {
  35. case beKicked = 100004
  36. }
  37. /// HTTP请求管理器
  38. class LNHttpManager {
  39. static let shared = LNHttpManager()
  40. private let session: URLSession
  41. private init() {
  42. let configuration = URLSessionConfiguration.default
  43. configuration.timeoutIntervalForRequest = 30
  44. configuration.timeoutIntervalForResource = 60
  45. session = URLSession(configuration: configuration)
  46. }
  47. /// 通用HTTP请求方法
  48. /// - Parameters:
  49. /// - urlString: 请求URL字符串
  50. /// - method: HTTP方法
  51. /// - parameters: 请求参数
  52. /// - headers: 请求头
  53. /// - completion: 完成回调
  54. func request<T: Decodable>(
  55. path: String,
  56. method: HTTPMethod = .get,
  57. parameters: [String: Any]? = nil,
  58. headers: [String: String]? = nil,
  59. completion: @escaping (T?, LNHttpError?) -> Void
  60. ) {
  61. var sign = ""
  62. var commonHeader: [String: String] = [:]
  63. let id = "\(Int(curTimeInNano))"
  64. sign += id
  65. commonHeader["id"] = id
  66. let udid = curDeviceId
  67. sign += udid
  68. commonHeader["udid"] = udid
  69. let app = curAppBundleIdentifier
  70. sign += app
  71. commonHeader["app"] = app
  72. let device = curDeviceModelName
  73. sign += device
  74. commonHeader["device"] = device
  75. let platform = "2"
  76. sign += platform
  77. commonHeader["platform"] = platform
  78. let channel = "appStore"
  79. sign += channel
  80. commonHeader["channel"] = channel
  81. let api = "1"
  82. sign += api
  83. commonHeader["api"] = api
  84. let version = curBuildVersion
  85. sign += version
  86. commonHeader["version"] = version
  87. let network = LNNetworkMonitor.curNetworkType.desc
  88. sign += network
  89. commonHeader["network"] = network
  90. let time = "\(Int(curTimeInMicro))"
  91. sign += time
  92. commonHeader["time"] = time
  93. let token = LNAccountManager.shared.token
  94. if !token.isEmpty {
  95. sign += token
  96. commonHeader["token"] = token
  97. }
  98. let secret = "abc|abc|edg|9527|1234"
  99. sign += secret
  100. let mergedHeader = commonHeader.merging(headers ?? [:]) { $1 }
  101. // 检查URL是否有效
  102. guard let url = buildURL(from: path, method: method, parameters: parameters) else {
  103. completion(nil, .invalidURL)
  104. return
  105. }
  106. // 创建请求
  107. var request = URLRequest(url: url)
  108. request.httpMethod = method.rawValue
  109. // 设置请求头
  110. mergedHeader.forEach { key, value in
  111. request.addValue(value, forHTTPHeaderField: key)
  112. }
  113. request.addValue(LNAppConfig.shared.curLang.bundleName, forHTTPHeaderField: "Accept-Language")
  114. // 设置请求体(POST, PUT等方法)
  115. if method != .get, let parameters = parameters {
  116. let body = try? JSONSerialization
  117. .data(withJSONObject: parameters)
  118. request.httpBody = body
  119. if let body, let bodyStr = String(data: body, encoding: .utf8) {
  120. sign += bodyStr
  121. }
  122. }
  123. request.addValue(sign.md5, forHTTPHeaderField: "sign")
  124. request.addValue("application/json", forHTTPHeaderField: "Content-Type")
  125. // 执行请求
  126. let task = session.dataTask(with: request) {
  127. [weak self] data,
  128. response,
  129. error in
  130. guard let self else { return }
  131. // 处理网络错误
  132. if let error = error {
  133. Log.d("receive \(request.url?.absoluteString ?? "") error: \(error.localizedDescription)")
  134. completion(nil, .networkError(error))
  135. return
  136. }
  137. // 检查响应是否有效
  138. guard let httpResponse = response as? HTTPURLResponse else {
  139. Log.d("receive \(request.url?.absoluteString ?? "") response error")
  140. completion(nil, .invalidResponse)
  141. return
  142. }
  143. if let data {
  144. Log.d("receive \(request.url?.absoluteString ?? "") code:\(httpResponse.statusCode) data \(String(data: data, encoding: .utf8) ?? "")")
  145. } else {
  146. Log.d("receive \(request.url?.absoluteString ?? "") code:\(httpResponse.statusCode)")
  147. }
  148. // 检查状态码
  149. guard 200...299 ~= httpResponse.statusCode else {
  150. completion(nil, .statusCode(httpResponse.statusCode))
  151. return
  152. }
  153. // 处理响应数据
  154. guard let data = data else {
  155. completion(nil, .invalidResponse)
  156. return
  157. }
  158. // 解析JSON数据
  159. self.parseJSON(data: data, completion: completion)
  160. }
  161. if let body = request.httpBody {
  162. Log.d("send \(request.httpMethod ?? "") - \(request.url?.absoluteString ?? "") \(String(data: body, encoding: .utf8) ?? "")")
  163. } else {
  164. Log.d("send \(request.httpMethod ?? "") - \(request.url?.absoluteString ?? "")")
  165. }
  166. task.resume()
  167. }
  168. /// 构建请求URL(处理GET参数)
  169. private func buildURL(from path: String, method: HTTPMethod, parameters: [String: Any]?) -> URL? {
  170. guard var urlComponents = URLComponents(string: LNNetworkConfig.host + (path.starts(with: "/") ? path : "/\(path)")) else {
  171. return nil
  172. }
  173. // GET方法的参数拼接到URL上
  174. if method == .get, let parameters = parameters {
  175. urlComponents.queryItems = parameters.map { key, value in
  176. URLQueryItem(name: key, value: "\(value)")
  177. }
  178. }
  179. return urlComponents.url
  180. }
  181. /// 解析JSON数据
  182. private func parseJSON<T: Decodable>(
  183. data: Data,
  184. completion: @escaping (T?, LNHttpError?) -> Void
  185. ) {
  186. do {
  187. let decoder = JSONDecoder()
  188. decoder.keyDecodingStrategy = .useDefaultKeys // 处理蛇形命名
  189. let result = try decoder.decode(LNHttpResponse<T>.self, from: data)
  190. if result.code != 0 {
  191. if result.code == LNHttpCommonErrorCode.beKicked.rawValue {
  192. LNAccountManager.shared.clean()
  193. }
  194. completion(nil, .serverError(result.code, result.msg))
  195. } else {
  196. completion(result.data, nil)
  197. }
  198. } catch {
  199. completion(nil, .parsingError(error))
  200. }
  201. }
  202. }
  203. // MARK: - 便捷请求方法扩展
  204. extension LNHttpManager {
  205. /// 发送GET请求
  206. func get<T: Decodable>(
  207. path: String,
  208. params: [String: Any]? = nil,
  209. completion: @escaping (T?, LNHttpError?) -> Void
  210. ) {
  211. request(
  212. path: path,
  213. method: .get,
  214. parameters: params,
  215. completion: completion
  216. )
  217. }
  218. /// 发送POST请求
  219. func post<T: Decodable>(
  220. path: String,
  221. params: [String: Any]? = nil,
  222. completion: @escaping (T?, LNHttpError?) -> Void
  223. ) {
  224. request(
  225. path: path,
  226. method: .post,
  227. parameters: params,
  228. completion: completion
  229. )
  230. }
  231. /// 发送PUT请求
  232. func put<T: Decodable>(
  233. path: String,
  234. params: [String: Any]? = nil,
  235. completion: @escaping (T?, LNHttpError?) -> Void
  236. ) {
  237. request(
  238. path: path,
  239. method: .put,
  240. parameters: params,
  241. completion: completion
  242. )
  243. }
  244. /// 发送DELETE请求
  245. func delete<T: Decodable>(
  246. path: String,
  247. params: [String: Any]? = nil,
  248. completion: @escaping (T?, LNHttpError?) -> Void
  249. ) {
  250. request(
  251. path: path,
  252. method: .delete,
  253. parameters: params,
  254. completion: completion
  255. )
  256. }
  257. }
  258. extension LNHttpManager {
  259. func request(
  260. path: String,
  261. method: HTTPMethod = .get,
  262. parameters: [String: Any]? = nil,
  263. headers: [String: String]? = nil,
  264. completion: @escaping (LNHttpError?) -> Void
  265. ) {
  266. let handler: (
  267. LNHttpEmptyResponse?, LNHttpError?
  268. ) -> Void = { _, err in
  269. completion(err)
  270. }
  271. request(
  272. path: path,
  273. method: method,
  274. parameters: parameters,
  275. headers: headers,
  276. completion: handler
  277. )
  278. }
  279. /// 发送GET请求
  280. func get(
  281. path: String,
  282. params: [String: Any]? = nil,
  283. completion: @escaping (LNHttpError?) -> Void
  284. ) {
  285. request(
  286. path: path,
  287. method: .get,
  288. parameters: params,
  289. completion: completion
  290. )
  291. }
  292. /// 发送POST请求
  293. func post(
  294. path: String,
  295. params: [String: Any]? = nil,
  296. completion: @escaping (LNHttpError?) -> Void
  297. ) {
  298. request(
  299. path: path,
  300. method: .post,
  301. parameters: params,
  302. completion: completion
  303. )
  304. }
  305. /// 发送PUT请求
  306. func put(
  307. path: String,
  308. params: [String: Any]? = nil,
  309. completion: @escaping (LNHttpError?) -> Void
  310. ) {
  311. request(
  312. path: path,
  313. method: .put,
  314. parameters: params,
  315. completion: completion
  316. )
  317. }
  318. /// 发送DELETE请求
  319. func delete(
  320. path: String,
  321. params: [String: Any]? = nil,
  322. completion: @escaping (LNHttpError?) -> Void
  323. ) {
  324. request(
  325. path: path,
  326. method: .delete,
  327. parameters: params,
  328. completion: completion
  329. )
  330. }
  331. }