如何快速在 iOS 的全局请求中添加 HTTP 标头

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28984212/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 05:09:31  来源:igfitidea点击:

How to add HTTP headers in request globally for iOS in swift

iosswifthttpwkwebviewrequest-headers

提问by sandip

func webView(webView: WKWebView!, decidePolicyForNavigationAction navigationAction: WKNavigationAction!, decisionHandler: ((WKNavigationActionPolicy) -> Void)!) {
     var request = NSMutableURLRequest(URL: navigationAction.request.URL)
     request.setValue("value", forHTTPHeaderField: "key")
     decisionHandler(.Allow)
}

In the above code I want to add a header to the request. I have tried to do navigationAction.request.setValue("IOS", forKey: "DEVICE_APP")but it doesn't work.

在上面的代码中,我想向请求添加一个标头。我试图这样做,navigationAction.request.setValue("IOS", forKey: "DEVICE_APP")但它不起作用。

please help me in any way.

请以任何方式帮助我。

回答by Stefan Arentz

AFAIK sadly you cannot do this with WKWebView.

AFAIK 遗憾的是你不能用WKWebView.

It most certainly does not work in webView:decidePolicyForNavigationAction:decisionHandler:because the navigationAction.requestis read-only and a non-mutable NSURLRequestinstance that you cannot change.

它肯定不起作用,webView:decidePolicyForNavigationAction:decisionHandler:因为它navigationAction.request是只读的并且是NSURLRequest您无法更改的非可变实例。

If I understand correctly, WKWebViewruns sandboxed in a separate content and network process and, at least on iOS, there is no way to intercept or change it's network requests.

如果我理解正确,WKWebView它会在单独的内容和网络进程中运行沙盒,并且至少在 iOS 上,无法拦截或更改它的网络请求。

You can do this if you step back to UIWebView.

如果您退回到UIWebView.

回答by Gabriel Cartier

There are many different ways to do that, I found that the easiest solution was to subclass WKWebView and override the loadRequest method. Something like this:

有很多不同的方法可以做到这一点,我发现最简单的解决方案是子类化 WKWebView 并覆盖 loadRequest 方法。像这样的东西:

class CustomWebView: WKWebView {
    override func load(_ request: URLRequest) -> WKNavigation? {
        guard let mutableRequest: NSMutableURLRequest = request as? NSMutableURLRequest else {
            return super.load(request)
        }
        mutableRequest.setValue("custom value", forHTTPHeaderField: "custom field")
        return super.load(mutableRequest as URLRequest)
    }
}

Then simply use the CustomWebView class as if it was a WKWebView.

然后只需像使用 WKWebView 一样使用 CustomWebView 类。

EDIT NOTE: This will only work on the first request as pointed out by @Stefan Arentz.

编辑注意:这仅适用于@Stefan Arentz 指出的第一个请求。

NOTE: Some fields cannot be overridden and will not be changed. I haven't done a thorough testing but I know that the User-Agentfield cannot be overridden unless you do a specific hack (check here for an answer to that)

注意:某些字段不能被覆盖,也不会被更改。我还没有进行彻底的测试,但我知道User-Agent除非您进行特定的黑客攻击,否则无法覆盖该字段(请在此处查看答案

回答by Roben

I have modified Au Ris answer to use NavigationActioninstead of NavigationResponse, as jonny suggested. Also, this fixes situations where the same url is called subsequently and you don't have to keep track of the current url anymore. This only works for GET requests but can surely be adapted for other request types if neccessary.

我已经修改了 Au Ris 的答案以NavigationAction代替使用NavigationResponse,正如 jonny 建议的那样。此外,这解决了随后调用相同 url 并且您不必再跟踪当前 url 的情况。这仅适用于 GET 请求,但如果需要,肯定可以适用于其他请求类型。

import UIKit
import WebKit
class ViewController: UIViewController, WKNavigationDelegate  {
    var webView: WKWebView?

    override func viewDidLoad() {
        super.viewDidLoad()
        webView = WKWebView(frame: CGRect.zero)
        webView!.navigationDelegate = self
        view.addSubview(webView!)
        // [...] set constraints and stuff

        // Load first request with initial url
        loadWebPage(url: "https://my.url")
    }

    func loadWebPage(url: URL)  {
        var customRequest = URLRequest(url: url)
        customRequest.setValue("true", forHTTPHeaderField: "x-custom-header")
        webView!.load(customRequest)
    }

    func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping
    (WKNavigationActionPolicy) -> Void) {
        if navigationAction.request.httpMethod != "GET" || navigationAction.request.value(forHTTPHeaderField: "x-custom-header") != nil {
            // not a GET or already a custom request - continue
            decisionHandler(.allow)
            return
        }
        decisionHandler(.cancel)
        loadWebPage(url: navigationAction.request.url!)
    }

}

}

回答by Au Ris

With some limitations, but you can do it. Intercept the response in the delegate function webView:decidePolicyFornavigationResponse:decisionHandler:, if the url changes cancel it by passing decisionHandler(.cancel)and reload the webview with newURLRequestwhich sets the custom headers and the intercepted url. In this way each time a url changes (e.g. users tap on links) you cancel that request and create a new one with custom headers.

有一些限制,但你可以做到。拦截委托函数中的响应webView:decidePolicyFornavigationResponse:decisionHandler:,如果 url 更改,则通过传递decisionHandler(.cancel)并使用 new 重新加载 webview 来取消它,该 webviewURLRequest设置自定义标头和拦截的 url。通过这种方式,每次 url 更改(例如用户点击链接)时,您都会取消该请求并创建一个带有自定义标题的新请求。

import UIKit
import WebKit
class ViewController: UIViewController, WKNavigationDelegate  {
    var webView: WKWebView?
    var loadUrl = URL(string: "https://www.google.com/")!

    override func viewDidLoad() {
        super.viewDidLoad()

        webView = WKWebView(frame: CGRect.zero)
        webView!.navigationDelegate = self
        view.addSubview(webView!)
        webView!.translatesAutoresizingMaskIntoConstraints = false
        webView!.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0).isActive = true
        webView!.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0).isActive = true
        webView!.topAnchor.constraint(equalTo: view.topAnchor, constant: 0).isActive = true
        webView!.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true

        // Load first request with initial url
        loadWebPage(url: loadUrl)
    }

    func loadWebPage(url: URL)  {
        var customRequest = URLRequest(url: url)
        customRequest.setValue("some value", forHTTPHeaderField: "custom header key")
        webView!.load(customRequest)
    }

    // MARK: - WKNavigationDelegate

    func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
        guard let url = (navigationResponse.response as! HTTPURLResponse).url else {
            decisionHandler(.cancel)
            return
        }

        // If url changes, cancel current request which has no custom headers appended and load a new request with that url with custom headers
        if url != loadUrl {
            loadUrl = url
            decisionHandler(.cancel)
            loadWebPage(url: url)
        } else {
            decisionHandler(.allow)
        }
    }
}

回答by Jonny

private var urlrequestCurrent: URLRequest?

func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
    //print("WEB decidePolicyFor navigationAction: \(navigationAction)")
    if let currentrequest = self.urlrequestCurrent {
        //print("currentrequest: \(currentrequest), navigationAction.request: \(navigationAction.request)")
        if currentrequest == navigationAction.request {
            self.urlrequestCurrent = nil
            decisionHandler(.allow)
            return
        }
    }

    decisionHandler(.cancel)

    var customRequest = navigationAction.request
    customRequest.setValue("myvaluefffs", forHTTPHeaderField: "mykey")
    self.urlrequestCurrent = customRequest
    webView.load(customRequest)
}

回答by jbelkins

Here's how you do it: The strategy is to have your WKNavigationDelegate cancel the request, modify a mutable copy of it and re-initiate it. An if-else is used to allow the request to proceed if it already has the desired header; otherwise you will end up in an endless load / decidePolicy loop.

以下是您的操作方法:策略是让您的 WKNavigationDelegate 取消请求,修改它的可变副本并重新启动它。if-else 用于在请求已经具有所需的标头时允许继续进行;否则,您将陷入无休止的加载/decidePolicy 循环。

Not sure what's up, but weird things happen if you set the header on every request, so for best results only set the header on requests to the domain(s) you care about.

不确定发生了什么,但是如果您在每个请求上设置标头,就会发生奇怪的事情,因此为了获得最佳结果,仅将请求标头设置为您关心的域。

The example here sets a header field for requests to header.domain.com, and allows all other requests without the header:

此处的示例为对 header.domain.com 的请求设置了标头字段,并允许所有其他没有标头的请求:

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    NSURL * actionURL = navigationAction.request.URL;
    if ([actionURL.host isEqualToString:@"header.domain.com"]) {
        NSString * headerField = @"x-header-field";
        NSString * headerValue = @"value";
        if ([[navigationAction.request valueForHTTPHeaderField:headerField] isEqualToString:headerValue]) {
            decisionHandler(WKNavigationActionPolicyAllow);
        } else {
            NSMutableURLRequest * newRequest = [navigationAction.request mutableCopy];
            [newRequest setValue:headerValue forHTTPHeaderField:headerField];
            decisionHandler(WKNavigationActionPolicyCancel);
            [webView loadRequest:newRequest];
        }
    } else {
        decisionHandler(WKNavigationActionPolicyAllow);
    }
}