如何在 RequestInterceptor 中正确设置 QWebEngine HTTP 标头

Posted

技术标签:

【中文标题】如何在 RequestInterceptor 中正确设置 QWebEngine HTTP 标头【英文标题】:How to correctly set QWebEngine HTTP headers in a RequestInterceptor 【发布时间】:2018-03-07 23:44:27 【问题描述】:

我在 Python3 上遇到了 PyQt5 的 QWebEngineUrlRequestInterceptor 的问题,更重要的是 setHttpHeader 函数。这是我的代码:

class WebEngineUrlRequestInterceptor(QWebEngineUrlRequestInterceptor):
def __init__(self, parent=None):
    super().__init__(parent)

def interceptRequest(self, info):
    info.setHttpHeader("X-Frame-Options", "ALLOWALL")
    print(info.requestUrl())

不幸的是,使用此功能的正确方法似乎绝对找不到,因此我不得不求助于我能想到的所有可能的方法,但无济于事。

我也试过用QByteArray 包围setHttpHeader 的参数,这导致QByteArray 给我这个抱怨......

    Traceback (most recent call last):
  File "test.py", line 30, in interceptRequest
    info.setHttpHeader(QByteArray("X-Frame-Options"), QByteArray("ALLOWALL"))
TypeError: arguments did not match any overloaded call:
  QByteArray(): too many arguments
  QByteArray(int, str): argument 1 has unexpected type 'str'
  QByteArray(Union[QByteArray, bytes, bytearray]): argument 1 has unexpected type 'str'

我还尝试使用.encode('ascii') 甚至.encode('utf-8') 对字符串进行编码。虽然两者都没有引发错误,但标头也拒绝更改,这让我相信返回的值与函数不兼容。

更新:即使QByteArray(b"X-Frame-Options") 也没有设置标题。 js: Refused to display 'https://www.google.co.uk/?gfe_rd=cr&dcr=0&ei=rX2gWtDJL8aN8Qfv3am4Bw' in a frame because it set 'X-Frame-Options' to 'SAMEORIGIN'. 是我从 WebEngine 得到的错误。

要补充一点,我 100% 确定正在调用 interceptRequest。我可以在终端中看到print 调用的输出。

完整的 MCVE 代码位于 [更新链接]:https://paste.ee/p/Y0mRs

【问题讨论】:

您是否要在 IFRAME 中显示 Google?您是否确保对 google 请求或主页请求进行拦截?此外,您的 hastebin 链接似乎也不起作用 您的 MCVE 的链接已被删除。 @TarunLalwani 是的,我正在尝试在 iframe 中显示外部网页。我确实确保 iframe 内容会发生拦截,因为我的完整代码会打印它拦截的任何请求的 URL。另外,我更新了帖子中的 Hastebin 链接。 所以我知道问题出在哪里,但我不确定QWebEngineView 是否存在解决方案,您可以改用QWebView 吗? 对不起@TarunLalwani,但我不能使用 QWebView 来完成我正在处理的项目,因为它需要对我的应用程序进行重大修改。不知何故,我需要让 setHttpHeader 在 QWebEngine 上工作。 【参考方案1】:

所以首先,问题是为什么现有代码不起作用?

class WebEngineUrlRequestInterceptor(QWebEngineUrlRequestInterceptor):
    def __init__(self, parent=None):
        super().__init__(parent)

    def interceptRequest(self, info):
        info.setHttpHeader("X-Frame-Options", "ALLOWALL")
        print(info.requestUrl())

现在,当您安装UrlRequestInterceptor 时,它绝对是一个请求拦截器。 WebEngineView发起的请求通过这个传递,你可以用它做很多事情

一起更改网址 阻止下载(AdBlocking 等...) 为请求添加更多标头 重定向到其他网址

现在当您拥有info.setHttpHeader("X-Frame-Options", "ALLOWALL") 时,它会将其添加到请求而不是响应中。这可以通过将 url 更改为 http://postman-echo.com/get 来验证,您将得到以下响应


  "args": 
    
  ,
  "headers": 
    "host": "postman-echo.com",
    "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
    "accept-encoding": "gzip, deflate",
    "cookie": "sails.sid=s%3AXNukTzCE5ucYNEv_NB8ULCf4esVES3cW.%2BmpA77H2%2F%2B6YcnypvZ7I8RQFvVJrdOFs8GD%2FPymF0Eo",
    "if-none-match": "W/\"1e1-rYSDjZun8qsI1ZojoxMuVg\"",
    "upgrade-insecure-requests": "1",
    "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) QtWebEngine/5.10.1 Chrome/61.0.3163.140 Safari/537.36",
    "x-frame-options": "ALLOW",
    "x-forwarded-port": "80",
    "x-forwarded-proto": "http"
  ,
  "url": "http://postman-echo.com/get"

但响应端没有任何改变,您仍然拥有原始请求实际返回的任何内容。

使用QWebView 可以安装QNetworkAccessManager 并返回带有修改响应的QNetworkReply。显示的东西

How to write content to QNetworkReply (was: QWebview with generated content)

但如果您阅读Porting from Qt WebKit to Qt WebEngine 指南,则需要注意一个重要的区别

Qt WebEngine 不与 QNetworkAccessManager 交互

Qt Network 的某些类(例如 QAuthenticator)被重用于其接口,但与 Qt WebKit 不同,Qt WebEngine 有自己的 HTTP 实现,不能通过 QNetworkAccessManager。

仍然支持的 QNetworkAccessManager 的信号和方法已移至 QWebEnginePage 类。

我挖了很多线程来询问响应修改方法。不幸的是,所有未答复的人

Capture server response with QWebEngineView

QWebEngineView modify web content before render

https://forum.qt.io/topic/81450/capture-client-request-headers-with-qwebengineview

Intercept AJAX POST request and read data using QWebEngine?

所以这并不容易。但是有一种我认为可行的解决方法,但我还不能验证它

方法是添加一个新的scheme url handler

self.conn_handler = AppSchemeHandler()
self.profile.installUrlSchemeHandler("conapp".encode(), self.conn_handler)
self.webpage = MyQWebEnginePage(self.profile, self.view)

现在我们更新拦截器,使其修改 google url 以将请求重定向到我们的处理程序

class WebEngineUrlRequestInterceptor(QWebEngineUrlRequestInterceptor):
    def __init__(self, parent=None):
        super().__init__(parent)

    def interceptRequest(self, info):
        info.setHttpHeader(b'x-frame-options', b'ALLOW')
        print(info.requestUrl())

        if str(info.requestUrl().host()) == "google.com":
            url = info.requestUrl().toString()
            item = url.split("/")[-1]

            info.redirect(QUrl(r"conapp://webresource?url=" + url))

然后在我们的方案处理程序中

class AppSchemeHandler(QWebEngineUrlSchemeHandler):
    def __init__(self, parent=None):
        super().__init__(parent)

    def requestStarted(self, request):
        url = request.requestUrl().toString().replace("conapp://webresource?url=", "")
        response = QWebEngineHttpRequest(QUrl(url))

        # Do something here which returns the response back to the url

我们读取响应并将其发回的部分是我尚未在任何地方找到示例的部分

【讨论】:

对于回复部分,此网址可能会有所帮助fossies.org/linux/eric6/eric/WebBrowser/Network/… 感谢您的所有帮助。我会很快看看我是否可以为回复提供一个解决方案并回复你 - 我不敢相信我没有意识到我正在处理请求而不是响应! 不幸的是,该解决方案对我不起作用。我发现在此任务中使用 info.redirect 存在问题 - 添加到重定向 URL 的任何内容也将用于计算下一个相对定义的 URL(例如 ./assets/my_image.png)。如果有某种方法可以使用 RequestInterceptor 重定向而不导致 Chromium 更改将相对 URL 转换为绝对 URL 的方式,这将容易得多。我想我得等到 Qt 开发人员真正添加 NetworkAccessManager 之类的东西(QWebEngine 除外)。 @unknownA,我怀疑这会发生,因为他们会迁移到 chrome Stack,而网络访问管理器意味着位于 chrome 内部的东西。但是您可以在他们的论坛上询问,看看维护团队中是否有人可以参与并发表评论。但正如我怀疑的那样,QWebEngine 可能无法做到这一点 @unknownA,我不认为有一个完整的答案。如果答案对您有所帮助,如果您可以而不是仅仅接受奖励积分,那就太好了

以上是关于如何在 RequestInterceptor 中正确设置 QWebEngine HTTP 标头的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Core Data 中正确保存?

如何在iOS7中正确定位后退按钮

如何在c#中正确实现等待异步[重复]

如何在 django 中正确保存多个文件?

如何在python的类中正确实现辅助函数

在这种情况下,如何在 Flutter 中正确实现 FutureBuilder?