• Qt(QtWebEngine)加载本地网页跨域问题的总结


    1. 概述

    浏览器直接加载本地网页的时候,如果网页涉及到加载本地资源(如图片),会出现跨域的问题。Qt的Qt WebEngine模块基于Chromium项目,遇到这样的情况也会出现跨域的问题。

    2. 详论

    2.1. 传参

    理论上,我们可以像设置chrome浏览器跨域一样(设置chrome浏览器跨域网上的资料非常多),给我们使用的Qt程序传参:

    char ARG_DISABLE_WEB_SECURITY[] = "--disable-web-security";
    int newArgc = argc+1+1;
    char** newArgv = new char*[newArgc];
    for(int i=0; i<argc; i++) {
        newArgv[i] = argv[i];
    }
    newArgv[argc] = ARG_DISABLE_WEB_SECURITY;
    newArgv[argc+1] = nullptr;
    
    QApplication myApplication(newArgc, newArgv);
    

    Qt会将跨域参数传递到Qt WebEngine模块的Chromium内核中,从而实现跨域。

    2.2. JS module

    即使设置跨域,当使用JavaScript ES6 module的时候,仍然有可能会出现跨域的问题。

    一个显而易见的错误提示如下:

    js: Failed to load module script: The server responded with a non-JavaScript MIME type of "". Strict MIME type checking is enforced for module scripts per HTML spec.
    

    原因在于,在Chrome浏览器的某些版本中,ES6 module的功能不支持跨域(但是require.js却可以)。现在最新版本的Chrome的跨域设置已经可以支持ES6 module,但是Qt WebEngine模块却可能是比较低的Chromium版本,从而造成使用ES6 module遇到跨域问题。通常来说,越新的Qt版本,Chromium版本也会越高。

    如果还是不想要服务器环境,那么一种解决方案就是自定义URL方案:

    #include <QApplication>
    #include <QWebEngineView>
    #include <QWebEngineUrlScheme>
    #include <QWebEngineProfile>
    #include <QWebEngineUrlSchemeHandler>
    #include <QWebEngineUrlRequestJob>
    #include <QFile>
    #include <QFile>
    #include <QFileInfo>
    #include <QMimeDatabase>
    
    class QtSchemeHandler : public QWebEngineUrlSchemeHandler
    {
    public:
        QtSchemeHandler(QObject *parent = nullptr):QWebEngineUrlSchemeHandler(parent)
        {
        }
    
        void requestStarted(QWebEngineUrlRequestJob *request) override
        {
            QByteArray request_method = request->requestMethod();
            if(request_method != "GET")
            {
                request->fail(QWebEngineUrlRequestJob::RequestDenied);
                return;
            }
    
            QUrl request_url = request->requestUrl();
            QString request_path = request_url.path();
            //qDebug()<<request_url<<endl;
    
            QString application_path = "D:/";
            QFile *file = new QFile(application_path + request_path);
            file->setParent(request);
            connect(request, &QObject::destroyed, file, &QFile::deleteLater);
            //qDebug()<<file->size()<<endl;
            if(!file->exists()||file->size()==0)
            {
                printf("resource '{request_path}' not found or is empty");
                request->fail(QWebEngineUrlRequestJob::UrlNotFound);
                return;
            }
    
            QFileInfo file_info = QFileInfo(*file);
            QMimeDatabase mime_database;
            QMimeType mime_type = mime_database.mimeTypeForFile(file_info);
            request->reply(QUrl(mime_type.name()).toEncoded(), file);
        }
    };
    
    int main(int argc, char *argv[])
    {
        char ARG_DISABLE_WEB_SECURITY[] = "--disable-web-security";
        int newArgc = argc+1+1;
        char** newArgv = new char*[newArgc];
        for(int i=0; i<argc; i++) {
            newArgv[i] = argv[i];
        }
        newArgv[argc] = ARG_DISABLE_WEB_SECURITY;
        newArgv[argc+1] = nullptr;
    
        qputenv("QTWEBENGINE_REMOTE_DEBUGGING", "7542");    //用于调试
    
        QWebEngineUrlScheme scheme = QWebEngineUrlScheme("qt");
        scheme.setFlags(QWebEngineUrlScheme::CorsEnabled);
        QWebEngineUrlScheme::registerScheme(scheme);
    
        //QApplication a(argc, argv);
        QApplication a(newArgc, newArgv);
    
        QWebEngineView view;
    
        QtSchemeHandler *scheme_handler = new QtSchemeHandler();
        view.page()->profile()->installUrlSchemeHandler("qt", scheme_handler);
    
        view.page()->profile()->clearHttpCache();       //删除缓存
    
        //view.load(QUrl("D:/cesium/CesiumWork/3DTilesPhotogrammetry/3DTilesPhotogrammetry.html"));
        view.load(QUrl("qt://local/cesium/CesiumWork/3DTilesPhotogrammetry/3DTilesPhotogrammetry.html"));
    
        view.show();
    
        return a.exec();
    }
    

    这个方案的本质是将URL定义地址的资源给转发了一遍。但是这种方案还是有局限性,经过测试,在Qt5.15.2版本中可行,但在Qt5.12.5版本中不行。而且这样所有的资源地址都得采用这一套URL方案。

    3. 建议

    其实个人还是不太建议再轻易尝试使用本地网页跨域了,毕竟这一点与Web的安全性背道而驰。最好还是让网页在服务器环境下吧,出问题的可能性会小一点。

    4. 参考

    1. Qt QWebEngineView not allowed to load local resource
    2. PyQt5 How To Use JavaScript Modules
  • 相关阅读:
    最详解JavaScript常见的创建对象的七种方式(推荐)
    详解数组的迭代方法every()、filter()、forEach()、map()以及some()的用法
    详解数组的concat()、slice()、splice()方法
    解决微信小程序中web-view无法调用微信支付
    SQLAlchemy_定义(一对一/一对多/多对多)关系
    常用算法
    Python框架之Tornado (源码之褪去模板外衣)
    Python框架之Tornado(源码之褪去模板外衣的前戏)
    Python框架之Tornado(请求)
    Python框架之Tornado(请求阶段)
  • 原文地址:https://www.cnblogs.com/charlee44/p/16204546.html
Copyright © 2020-2023  润新知