iOS 如何给每个UIWebView请求添加cookie?
iOS How to add cookie to each UIWebView request?
我需要在 UIWebView
中使用 Angular 打开 URL,并且我需要在每个 UIWebView
请求中发送 cookie。
我尝试做的事情:
我试图检查请求是否包含 cookie。如果它 UIWebView
执行请求,如果不是我创建相同的请求但使用 cookie 并执行它。为了替换请求,我使用了 UIWebViewDelegate
的方法 func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool
。但它并不像我预期的那样工作,有些请求在没有 cookie 的情况下执行。
我的代码:
final class FieldServiceViewController: UIViewController {
private var webView = UIWebView()
private var sessionID = String()
override func viewDidLoad() {
super.viewDidLoad()
_ = JSONAPI.getSessionID().subscribe(onNext: { [weak self] sessionID in
self?.sessionID = sessionID
self?.configureUI()
let string = "https://someURL"
let url = URL(string: string)
let request = URLRequest(url: url!)
self?.webView.loadRequest(request)
})
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
webView.frame = view.bounds
}
private func configureUI() {
webView.delegate = self
view.addSubview(webView)
}
private func cookedRequest(from: URLRequest) -> URLRequest? {
let cookiesKey = "Cookie"
let headers = from.allHTTPHeaderFields ?? [:]
if (headers.contains { [=11=].0 == cookiesKey }) {
return nil
}
var request = from
request.cachePolicy = .reloadIgnoringLocalAndRemoteCacheData
let cookiesToAdd = "SESSIONID=\(sessionID)"
request.addValue(cookiesToAdd, forHTTPHeaderField: cookiesKey)
return request
}
}
extension FieldServiceViewController: UIWebViewDelegate {
func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {
if let cooked = cookedRequest(from: request) {
webView.loadRequest(cooked)
return false
}
return true
}
}
如何为每个 UIWebView 请求添加 cookie?
P.S。我也在 HTTPCookieStorage
中保存了 cookie,但看起来 UIWebView
的请求和共享存储之间根本没有任何联系。
您可以将代码更改为:
func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {
if request.allHTTPHeaderFields?["SESSIONID"] == nil {
var request: URLRequest = request
request.allHTTPHeaderFields?["SESSIONID"] = "your_session_id"
webView.loadRequest(request)
return false
}
return true
}
更新
据我所知,解决您的问题的最佳方法是使用 URLProtocol
拦截您的请求。
创建下一个 class:
class MyURLProtocol: URLProtocol, NSURLConnectionDelegate, NSURLConnectionDataDelegate {
static let protocolKey = "MyURLProtocolKey"
private var connection: NSURLConnection?
override class func canInit(with request: URLRequest) -> Bool {
if let isCustom = URLProtocol.property(forKey: MyURLProtocol.protocolKey, in: request) as? Bool{
return false
}
return true
}
override class func canonicalRequest(for request: URLRequest) -> URLRequest {
//You can add headers here
var request = request
print("request: \(request.url?.absoluteString ?? " ")")
request.allHTTPHeaderFields?["SESSION_ID"] = "your_session_id"
return request
}
override class func requestIsCacheEquivalent(_ a: URLRequest, to b: URLRequest) -> Bool {
return super.requestIsCacheEquivalent(a, to: b)
}
override func startLoading() {
let newRequest: NSMutableURLRequest = self.request as! NSMutableURLRequest
URLProtocol.setProperty(true, forKey: MyURLProtocol.protocolKey, in: newRequest)
self.connection = NSURLConnection(request: newRequest as URLRequest, delegate: self, startImmediately: true)
}
override func stopLoading() {
self.connection?.cancel()
self.connection = nil
}
//MARK: NSURLConnectionDataDelegate methods
func connection(_ connection: NSURLConnection, didReceive response: URLResponse) {
self.client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
}
func connection(_ connection: NSURLConnection, didReceive data: Data) {
self.client?.urlProtocol(self, didLoad: data as Data)
}
func connectionDidFinishLoading(_ connection: NSURLConnection) {
self.client?.urlProtocolDidFinishLoading(self)
}
func connection(_ connection: NSURLConnection, didFailWithError error: Error) {
self.client?.urlProtocol(self, didFailWithError: error)
}
}
现在在你的 AppDelegate
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
URLProtocol.registerClass(MyURLProtocol.self)
return true
}
我更喜欢WKWebView instead of UIWebView as UIWebView is 。
Now about adding the session id to each request made by your Angular
project.
我建议在 Angular 级别添加 request interceptor,而不是通过委托方法创建新请求来添加会话 ID,这将使您更好地控制 [=] 发出的每个请求29=] 项目。
通过匿名工厂
注册拦截器
$httpProvider.interceptors.push(function($q, dependency1, dependency2) {
return {
'request': function(config) {
config.headers['SESSIONID'] = 'Your Session id here';
},
'response': function(response) {
}
};
});
Now how you can use this.
您可以将上面的代码转换为 Swift 字符串,当您拥有会话 ID 并且 Angular 项目已加载时,您可以通过 wkwebview 的 evaluatejavaScript 方法执行此操作。
例如
var sessionId = "you id"
var s="\n" +
" $httpProvider.interceptors.push(function($q, dependency1, dependency2) {\n" +
" return {\n" +
" \'request\': function(config) {\n" +
" config.headers[\'SESSIONID\'] = \'\(sessionId)\';\n" +
" },\n" +
" \n" +
" \'response\': function(response) {\n" +
" \n" +
" }\n" +
" };\n" +
" });"
webView.evaluateJavaScript(s, completionHandler: {(message,error) in
print(error ?? "No Error")
})
感谢您的帮助。通过创建具有特定域的 cookie 解决了问题:
final class FieldServiceViewController: UIViewController, DLHasHomeTitleView {
private let fieldServiceEndPoint = Bundle.main.object(forInfoDictionaryKey: "FIELD_SERVICE_ENDPOINT") as! String
private var webView = UIWebView()
private var sessionID = String()
override func viewDidLoad() {
super.viewDidLoad()
configureUI()
_ = JSONAPI.getSessionID().subscribe(onNext: { [weak self] sessionID in
guard let `self` = self else { return }
self.sessionID = sessionID
let urlString = "https://\(self.fieldServiceEndPoint)"
let url = URL(string: urlString)
let request = URLRequest(url: url!)
let cookie = HTTPCookie(properties: [
.name: "JSESSIONID",
.value: sessionID,
.path: "/",
.domain: self.fieldServiceEndPoint])
HTTPCookieStorage.shared.setCookie(cookie!)
self.webView.loadRequest(request)
})
}
我需要在 UIWebView
中使用 Angular 打开 URL,并且我需要在每个 UIWebView
请求中发送 cookie。
我尝试做的事情:
我试图检查请求是否包含 cookie。如果它 UIWebView
执行请求,如果不是我创建相同的请求但使用 cookie 并执行它。为了替换请求,我使用了 UIWebViewDelegate
的方法 func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool
。但它并不像我预期的那样工作,有些请求在没有 cookie 的情况下执行。
我的代码:
final class FieldServiceViewController: UIViewController {
private var webView = UIWebView()
private var sessionID = String()
override func viewDidLoad() {
super.viewDidLoad()
_ = JSONAPI.getSessionID().subscribe(onNext: { [weak self] sessionID in
self?.sessionID = sessionID
self?.configureUI()
let string = "https://someURL"
let url = URL(string: string)
let request = URLRequest(url: url!)
self?.webView.loadRequest(request)
})
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
webView.frame = view.bounds
}
private func configureUI() {
webView.delegate = self
view.addSubview(webView)
}
private func cookedRequest(from: URLRequest) -> URLRequest? {
let cookiesKey = "Cookie"
let headers = from.allHTTPHeaderFields ?? [:]
if (headers.contains { [=11=].0 == cookiesKey }) {
return nil
}
var request = from
request.cachePolicy = .reloadIgnoringLocalAndRemoteCacheData
let cookiesToAdd = "SESSIONID=\(sessionID)"
request.addValue(cookiesToAdd, forHTTPHeaderField: cookiesKey)
return request
}
}
extension FieldServiceViewController: UIWebViewDelegate {
func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {
if let cooked = cookedRequest(from: request) {
webView.loadRequest(cooked)
return false
}
return true
}
}
如何为每个 UIWebView 请求添加 cookie?
P.S。我也在 HTTPCookieStorage
中保存了 cookie,但看起来 UIWebView
的请求和共享存储之间根本没有任何联系。
您可以将代码更改为:
func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {
if request.allHTTPHeaderFields?["SESSIONID"] == nil {
var request: URLRequest = request
request.allHTTPHeaderFields?["SESSIONID"] = "your_session_id"
webView.loadRequest(request)
return false
}
return true
}
更新
据我所知,解决您的问题的最佳方法是使用 URLProtocol
拦截您的请求。
创建下一个 class:
class MyURLProtocol: URLProtocol, NSURLConnectionDelegate, NSURLConnectionDataDelegate {
static let protocolKey = "MyURLProtocolKey"
private var connection: NSURLConnection?
override class func canInit(with request: URLRequest) -> Bool {
if let isCustom = URLProtocol.property(forKey: MyURLProtocol.protocolKey, in: request) as? Bool{
return false
}
return true
}
override class func canonicalRequest(for request: URLRequest) -> URLRequest {
//You can add headers here
var request = request
print("request: \(request.url?.absoluteString ?? " ")")
request.allHTTPHeaderFields?["SESSION_ID"] = "your_session_id"
return request
}
override class func requestIsCacheEquivalent(_ a: URLRequest, to b: URLRequest) -> Bool {
return super.requestIsCacheEquivalent(a, to: b)
}
override func startLoading() {
let newRequest: NSMutableURLRequest = self.request as! NSMutableURLRequest
URLProtocol.setProperty(true, forKey: MyURLProtocol.protocolKey, in: newRequest)
self.connection = NSURLConnection(request: newRequest as URLRequest, delegate: self, startImmediately: true)
}
override func stopLoading() {
self.connection?.cancel()
self.connection = nil
}
//MARK: NSURLConnectionDataDelegate methods
func connection(_ connection: NSURLConnection, didReceive response: URLResponse) {
self.client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
}
func connection(_ connection: NSURLConnection, didReceive data: Data) {
self.client?.urlProtocol(self, didLoad: data as Data)
}
func connectionDidFinishLoading(_ connection: NSURLConnection) {
self.client?.urlProtocolDidFinishLoading(self)
}
func connection(_ connection: NSURLConnection, didFailWithError error: Error) {
self.client?.urlProtocol(self, didFailWithError: error)
}
}
现在在你的 AppDelegate
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
URLProtocol.registerClass(MyURLProtocol.self)
return true
}
我更喜欢WKWebView instead of UIWebView as UIWebView is
Now about adding the session id to each request made by your Angular project.
我建议在 Angular 级别添加 request interceptor,而不是通过委托方法创建新请求来添加会话 ID,这将使您更好地控制 [=] 发出的每个请求29=] 项目。
通过匿名工厂
注册拦截器$httpProvider.interceptors.push(function($q, dependency1, dependency2) {
return {
'request': function(config) {
config.headers['SESSIONID'] = 'Your Session id here';
},
'response': function(response) {
}
};
});
Now how you can use this.
您可以将上面的代码转换为 Swift 字符串,当您拥有会话 ID 并且 Angular 项目已加载时,您可以通过 wkwebview 的 evaluatejavaScript 方法执行此操作。
例如
var sessionId = "you id"
var s="\n" +
" $httpProvider.interceptors.push(function($q, dependency1, dependency2) {\n" +
" return {\n" +
" \'request\': function(config) {\n" +
" config.headers[\'SESSIONID\'] = \'\(sessionId)\';\n" +
" },\n" +
" \n" +
" \'response\': function(response) {\n" +
" \n" +
" }\n" +
" };\n" +
" });"
webView.evaluateJavaScript(s, completionHandler: {(message,error) in
print(error ?? "No Error")
})
感谢您的帮助。通过创建具有特定域的 cookie 解决了问题:
final class FieldServiceViewController: UIViewController, DLHasHomeTitleView {
private let fieldServiceEndPoint = Bundle.main.object(forInfoDictionaryKey: "FIELD_SERVICE_ENDPOINT") as! String
private var webView = UIWebView()
private var sessionID = String()
override func viewDidLoad() {
super.viewDidLoad()
configureUI()
_ = JSONAPI.getSessionID().subscribe(onNext: { [weak self] sessionID in
guard let `self` = self else { return }
self.sessionID = sessionID
let urlString = "https://\(self.fieldServiceEndPoint)"
let url = URL(string: urlString)
let request = URLRequest(url: url!)
let cookie = HTTPCookie(properties: [
.name: "JSESSIONID",
.value: sessionID,
.path: "/",
.domain: self.fieldServiceEndPoint])
HTTPCookieStorage.shared.setCookie(cookie!)
self.webView.loadRequest(request)
})
}