iOS 如何为每个 UIWebView 请求添加 cookie?

Posted

技术标签:

【中文标题】iOS 如何为每个 UIWebView 请求添加 cookie?【英文标题】:iOS How to add cookie to each UIWebView request? 【发布时间】:2018-01-17 06:40:06 【问题描述】:

我需要在 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  $0.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?

附:我还在HTTPCookieStorage中保存了cookie,但看起来UIWebView的请求和共享存储之间根本没有联系。

【问题讨论】:

【参考方案1】:

我更喜欢 WKWebView 而不是 UIWebView,因为 UIWebView 是 deprecated。

现在关于将会话 ID 添加到您的 Angular 发出的每个请求中 项目。

我建议不要通过从委托方法创建新请求来添加会话 ID,而是在 Angular 级别添加 request interceptor,这将使您能够更好地控制 Angular 项目发出的每个请求。

通过匿名工厂

注册拦截器
$httpProvider.interceptors.push(function($q, dependency1, dependency2) 
  return 
   'request': function(config) 
       config.headers['SESSIONID'] = 'Your Session id here';
    ,

    'response': function(response) 

    
  ;
);

现在如何使用它。

您可以将上述代码转换为 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")
        )

【讨论】:

感谢您的回答。我尝试了您的解决方案,但现在我得到了白屏。页面根本不加载 webView.evaluateJavaScript() 返回错误:Error Domain=WKErrorDomain Code=4 "A JavaScript exception occurred" UserInfo=WKJavaScriptExceptionLineNumber=2, WKJavaScriptExceptionMessage=ReferenceError: Can't find variable: $httpProvider, WKJavaScriptExceptionColumnNumber=15, WKJavaScriptExceptionSourceURL=about:blank, NSLocalizedDescription=A JavaScript exception occurred @BadCodeDeveloper 您需要在加载 Angular 项目后执行此操作【参考方案2】:

您可以将代码更改为:

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 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

【讨论】:

感谢您的回答,但它的工作原理相同。一些请求在没有 cookie 的情况下执行 @BadCodeDeveloper 你能检查一下带cookie和不带cookie的btw请求有什么区别吗? Angular 执行的请求没有 cookie。这就是我能看到的所有差异【参考方案3】:

感谢您的帮助。通过创建具有特定域的 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)
        )
    

【讨论】:

以上是关于iOS 如何为每个 UIWebView 请求添加 cookie?的主要内容,如果未能解决你的问题,请参考以下文章

iOS UIWebview添加请求头的两种方式

IOS UIWebView添加Cookie值请求

如何为嵌套的对象数组添加验证

如何为图像添加渐变?

具有UIWebView请求的Xamarin iOS授权标头

如何为每个请求制作一个 beanscript 函数