我需要执行同步HTTP请求,而不是遵循重定向,最好不使用实例变量,因为这将被合并到j2objc项目中。
我尝试过使用NSURLConnection sendSynchronousRequest
,遗憾的是不能轻易告诉他们不要关注重定向。
在告诉我不应该使用同步请求之前,请记住,此代码用于模拟HttpUrlConnection
项目的Java j2objc,这在行为上本质上是同步的。 IosHttpUrlConnections
' native makeSynchronousRequest
的实施目前始终遵循重定向。它应该尊重HttpUrlConnection.instanceFollowRedirects
field。
sendSynchronousRequest
。但是,我无法修改它以使用委托,因此无法遵循重定向。答案 0 :(得分:1)
您可以将NSURLSession与信号量一起使用,创建如下:
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:self delegateQueue:nil];
NSURLSessionTask *task = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (data)
{
// do whatever you want with the data here
}
else
{
NSLog(@"error = %@", error);
}
dispatch_semaphore_signal(semaphore);
}];
[task resume];
// but have the thread wait until the task is done
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
您必须实现NSURLSessionTaskDelegate
的以下方法,并调用completionHandler块传递null以停止重定向。
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
willPerformHTTPRedirection:(NSHTTPURLResponse *)response
newRequest:(NSURLRequest *)request
completionHandler:(void (^)(NSURLRequest *))completionHandler
答案 1 :(得分:1)
我想我会离开他们离开的地方,但是在Swift中这么多年以后。
class ViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let semaphore = DispatchSemaphore(value: 0)
let configuration = URLSessionConfiguration.ephemeral
configuration.timeoutIntervalForRequest = 10
let session = URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
// Redirects to google.com
guard let url = URL(string: "https://bit(dot)ly/19BiSHW") else {
return
}
var data: Data?
var response: URLResponse?
var error: Error?
let task = session.dataTask(with: url) { (innerData, innerResponse, innerError) in
// For clarity, we'll call this the data task's completion closure
// Pass the data back to the calling scope
data = innerData
response = innerResponse
error = innerError
semaphore.signal()
}
task.resume()
if semaphore.wait(timeout: .now() + .seconds(15)) == .timedOut {
// The task's completion closure wasn't called within the time out, handle appropriately
} else {
if let e = error as NSError? {
if e.domain == NSURLErrorDomain && e.code == NSURLErrorTimedOut {
print("The request timed out")
}
return
}
if let d = data {
// do whatever you want with the data here, such as print it
// (the data is the HTTP response body)
print(String.init(data: d, encoding: .utf8) ?? "Response data could not be turned into a string")
return
}
if let r = response {
print("No data and no error, but we received a response, we'll inspect the headers")
if let httpResponse = r as? HTTPURLResponse {
print(httpResponse.allHeaderFields)
}
}
}
}
}
extension ViewController: URLSessionTaskDelegate {
func urlSession(_ session: URLSession, task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest, completionHandler: @escaping (URLRequest?) -> Swift.Void) {
// Inside the delegate method, we will call the delegate's completion handler
// completionHandler: A block that your handler should call with
// either the value of the request parameter, a modified URL
// request object, or NULL to refuse the redirect and return
// the body of the redirect response.
// I found that calling the block with nil only triggers the
// return of the body of the redirect response if the session is ephemeral
// Calling this will trigger the data task's completion closure
// which signals the semaphore and allows execution to continue
completionHandler(nil)
}
}
代码正在做什么:
它正在创建一个固有的异步任务(URLSessionTask),通过调用resume()
告诉它执行,然后通过等待DispatchSemaphore来暂停当前的执行上下文。这是我已经习惯使用的技巧,并且在很多场合亲自使用以使异步行为以同步方式运行。
要点是代码在当前上下文中停止执行。在这个例子中,该上下文是主线程(因为它在UIViewController方法中),这通常是不好的做法。因此,如果您的同步代码永远不会继续执行(因为信号量永远不会发出信号),那么您的UI线程将永远停止,从而导致UI被冻结。
最后一部分是委托方法的实现。评论表明,调用completionHandler(nil)
应该足够了,文档支持这一点。我发现只有ephemeral
URLSessionConfiguration才足够。如果您具有默认配置,则不会调用数据任务的完成闭包,因此信号量永远不会发出信号,因此代码永远不会向前移动。这就是导致评论者/提问者冻结用户界面问题的原因。