QtWebEngine Fetch API fails with custom scheme - qt

Im trying to create custom schema for QtWebEngineView with QWebEngineUrlSchemeHandler to serve web page from local drive. Page is using Fetch API to request some json files.
Here is implementation for my custom scheme
appschemehandler.cpp
AppSchemeHandler::AppSchemeHandler(QObject *parent):
QWebEngineUrlSchemeHandler(parent),
m_scheme(QWebEngineUrlScheme("app"))
{
m_scheme.setFlags(
QWebEngineUrlScheme::SecureScheme |
QWebEngineUrlScheme::LocalAccessAllowed |
QWebEngineUrlScheme::ViewSourceAllowed |
QWebEngineUrlScheme::ContentSecurityPolicyIgnored |
QWebEngineUrlScheme::CorsEnabled
);
QWebEngineUrlScheme::registerScheme(m_scheme);
}
void AppSchemeHandler::install()
{
QWebEngineProfile::defaultProfile()->installUrlSchemeHandler("app", this);
}
void AppSchemeHandler::requestStarted(QWebEngineUrlRequestJob *job)
{
...
}
main.cpp
int main(int argc, char *argv[])
{
AppSchemeHandler appSchemeHandler(nullptr);
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
appSchemeHandler.install();
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
main.qml
Window {
id: browser
visible: true
width: 800
height: 600
WebEngineView {
id: webengine
anchors.fill: parent
url: 'app://my-app'
}
}
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Lets Fetch!</title>
</head>
<body>
<h1>Lets Fetch!</h1>
<script>
fetch("/myFile.json").then(res => console.log(res.json()));
</script>
</body>
</html>
Everything works as it should until web page is trying to fetch(). This is what I get to chromium console.
Fetch API cannot load app://my-app/myFile.json. URL scheme "app" is not supported.
I made quick test with Electron that provides protocol.registerSchemesAsPrivileged() and there is no problems using Fetch API. So then I inspected electron process args and found --fetch-schemes=app. I think that is not Chromium (Blink?) arg since there is no change even if I pass it to my Qt application straight or through QTWEBENGINE_CHROMIUM_FLAGS environment variable.
What is the Qt way of adding custom fetch schemes?

A fix was integrated into the dev branch and will be available in Qt 6.6. You can then add QWebEngineUrlScheme::FetchApiAllowed to the flags and it should work.
m_scheme.setFlags(
QWebEngineUrlScheme::SecureScheme |
QWebEngineUrlScheme::LocalAccessAllowed |
QWebEngineUrlScheme::ViewSourceAllowed |
QWebEngineUrlScheme::ContentSecurityPolicyIgnored |
QWebEngineUrlScheme::CorsEnabled |
QWebEngineUrlScheme::FetchApiAllowed
);

Not a proper answer to your question (which seems to be a known bug), but a possible workaround:
It seems like fetch() works fine with requests that start with a http URL and are then redirected to the custom scheme with a QWebEngineUrlRequestInterceptor. So for your example, do fetch("http://my-app/myFile.json") in JavaScript, and then redirect this to app://my-app/myFile.json with the interceptor. Unfortunately, that means changing the JavaScript code, but it might be better than nothing.
This is the Python code I used, which should be straightforward to port to C++:
class Interceptor(QtWebEngineCore.QWebEngineUrlRequestInterceptor):
def interceptRequest(self, info):
url = info.requestUrl()
if url.scheme() != 'http' or url.host() != 'my-app':
return
url.setScheme('app')
info.redirect(url)
This interceptor is then installed on the QWebEngineProfile with profile.setUrlRequestInterceptor(interceptor).

Related

nextjs links without strings

Im new to nextjs, and Im checking if it will be good for the app that will have pretty complex and messy internal navigation. Just checked their documentation and I see that they recommend usage
of Link component like this <Link href="/your_path">Path</Link>. A bit scary is that I have to provide 'your_path' as a string so every time i change page file name I have to manually update code that redirects to this page. Is there any solution that allows me to define routing on my own so I can write something like (pseudocode)
routes = [
...
{
page : 'page_name',
path : 'path_to_page'
}
...
]
So instead of using string I can do <Link href="{route.path}">Path</Link> or Im condemned to use this file-system based router with all consequences?
The simple answer is yes!
When you want to change a user route in NextJs you have 2 options,
The first is with the <Link> Element that you can specify a href to where it directs.
And you also have a useRouter hook for more complex routing for example if the user does an action that requires moving him into a different route you can do it internally in your handlers.
For more information about useRouter hook.
What I usually do is storing my routes in an object
const ROUTES = {
HOME: "/",
ABOUT: "/about"
}
and wherever you call routes you just use the object so F.E
With Link tag
<Link href={ROUTES.ABOUT}>ABOUT PAGE</Link>`
with useRouter hook
// Inside a React Component
const router = useRouter();
const handleNavigateToAbout = () => {
router.push(ROUTES.ABOUT);
}
return (
// SOME JSX
<button onClick={handleNavigateToAbout}> Go to about page! </button>
)

ReferenceError: document is not defined inside Next.js client component [duplicate]

This question already has answers here:
Next.js: document is not defined
(9 answers)
Closed 10 days ago.
I have a client component ("use client") created using Next.js
"use client"
import type { ReactPortal } from "react"
import { createPortal } from "react-dom"
interface PortalProps {
children: React.ReactNode
}
export function Portal(props: PortalProps): ReactPortal | null {
return createPortal(props.children, document.body)
}
Whenever I visit the page which uses <Portal /> component it throws an error in the console
event - compiled client and server successfully in 469 ms (1685 modules)
ReferenceError: document is not defined
at Portal (webpack-internal:///(sc_client)/./src/components/portal/portal.component.tsx:9:98)
How can I fix that?
P.S. This is my package.json
// package.json
{
// cut
"next": "13.1.6"
// cut
}
I just found that Client components prerender on the server which doesn't have a access to the document object. That's why it says document is not defined.
There are many options how it can be fixed
Wrap document in typeof window !== undefiend check to make sure the document is only accessible in the browser enrironment (window is undefined during prerender).
Use useEffect + useState + condition
My solution to the problem looks like this
"use client"
import { type ReactPortal, useEffect, useState } from "react"
import { createPortal } from "react-dom"
interface PortalProps {
children: React.ReactNode
}
export function Portal(props: PortalProps): ReactPortal | null {
const [isMounted, setIsMounted] = useState(false)
useEffect(() => {
setIsMounted(true)
}, [])
return isMounted ? createPortal(props.children, document.body) : null // createPortal will not be rendered on the server. Only on the client after hydration
}
See also https://beta.nextjs.org/docs/rendering/server-and-client-components#client-components
Client Components
Client Components enable you to add client-side interactivity to your
application. In Next.js, they are prerendered on the server and
hydrated on the client. You can think of Client Components as how
Next.js 12 and previous versions worked (i.e. the pages/ directory)
See

NextJS looses global vars from script on deep link / page refresh

In Pages/_document.tsx I have a script that loads some global public configuration like below.
The script just sets some global variable on the window object.
If I visit the home route of the app, the page loads everything is fine, but if I refresh the page on a nested route, (or deep link) it throws an error saying ReferenceError: window is not defined.
I'm guessing this is the server complaining, but I only need this config on the client, and I don't want to package the config vars up during build time, as I want to promote the built app down a pipeline and just update a few variables. Is this the right approach?
import Script from 'next/script'
...
...
render() {
return (
<Html lang='en'>
<Head>
<meta charSet='UTF-8' />
<meta httpEquiv='X-UA-Compatible' content='IE=edge' />
</Head>
<body>
<Main />
<NextScript />
<Script src='/scripts/public-config.js' strategy='beforeInteractive' />
</body>
</Html>
)
}
Maybe there's a better way, but I ended up checking if window exists anywhere I use it, so in my public-config.js file I did this:
if (typeof window !== 'undefined') {
window.MY_PUBLIC_CONFIG = {
MY_VAR: 'HELLO WORLD'
}
}
and anywhere referencing it needs to do the same:
export const config = (typeof window !== 'undefined')
? window.MY_PUBLIC_CONFIG
: {}

Why is env variable token not being parsed properly by mapbox-gl-js?

Building in latest version of Next. Have installed mapbox-gl-js.
Created a .env file with:
MapboxAccessToken=my_special_token
I have a map.js component with basic template that pulls in that variable with the following:
import { useState } from "react";
import ReactMapGL from 'react-map-gl';
const MapView = () => {
const [viewport, setViewport] = useState({
width: 400,
height: 400,
latitude: 37.7577,
longitude: -122.4376,
zoom: 8
});
console.log(process.env.MapboxAccessToken)
return (
<div>
<ReactMapGL {...viewport} mapboxApiAccessToken={process.env.MapboxAccessToken} />
</div>
);
}
export default MapView;
The console log shows the actual token so I know its pulling it in.
However, this produces no map and an error, "A valid API token is required to use Mapbox data."
<ReactMapGL {...viewport} mapboxApiAccessToken={process.env.MapboxAccessToken} />
But when I put the token in as a string such as below, the map generates.
<ReactMapGL {...viewport} mapboxApiAccessToken="my_special_token" />
I restarted dev server of course and other env variables work just fine. Not sure if Mapbox is looking for something unique, but their docs don't recognize this issue.
Anyone have an idea as to why the env variable being passed doesn't work but the literal string does?

Next.js _app and _document use?

I'm totally new with next.js and I need your help for something I guess really basic but I cannot find my mistake or an explanation, I found nothing on the internet about it, so here I am :
Everything works when I create a file in the pages folder(I mean every file in pages folder is ok except _app.js or _document.js), I can reach the URL, but I would like to use context, layout or authentification in the future and I need to use the _app and _document override cool things but I can write anything I want in it, it seems my _app.js or _document.js are just useless, never called or I don't know but they just never work.
I tried on 2 projects, here is what I do according to the next documentation :
first, npx create-next-app to create the project, and then add an _app.js for example in pages folder and add :
import React from 'react'
import App from 'next/app'
import Nav from '../components/nav'
class MyApp extends App {
// Only uncomment this method if you have blocking data requirements for
// every single page in your application. This disables the ability to
// perform automatic static optimization, causing every page in your app to
// be server-side rendered.
//
// static async getInitialProps(appContext) {
// // calls page's `getInitialProps` and fills `appProps.pageProps`
// const appProps = await App.getInitialProps(appContext);
//
// return { ...appProps }
// }
render() {
const { Component, pageProps } = this.props
return (
<>
<Nav />
<Component {...pageProps} />
</>
);
}
}
export default MyApp
Anybody could tell me what I am doing wrong?
Well, if anybody is going through the same issue, I found what was going on, in fact, after creating for the first time _app.js, I have to restart my docker container, or restart my app with yarn next dev if I want to see the changes or they never appear. I am going to look for more explanations on how SSR and next.js more globaly exactly work to understand their behaviour on this point. Good luck all !

Resources