跳转到主内容

protocol

注册自定义协议并拦截基于现有协议的请求。

进程:主进程

实现与 file:// 协议具有相同效果的协议的示例:

const { app, protocol, net } = require('electron')
const path = require('node:path')
const url = require('node:url')

app.whenReady().then(() => {
protocol.handle('atom', (request) => {
const filePath = request.url.slice('atom://'.length)
return net.fetch(url.pathToFileURL(path.join(__dirname, filePath)).toString())
})
})

** 注意: **除了指定的方法, 其他方法只能在 app 模块的 ready 事件被触发后使用。

用自定义的 partitionsession 来使用 protocol

一个 protocol 是注册到特定 Electron 的 session 对象。 如果你不指定一个 session,那 protocol 将使用 Electron 默认提供的 session 。 然而, 如果你定义了 partition 或者 session 在你的 browserWindowwebPreferences, 对应 window 将使用不同的 session 。同时如果你使用 electron.protocol.XXX ,则自定义的 protocol 将不会工作

要使自定义协议与自定义会话相结合,你需要明确注册该会话。

const { app, BrowserWindow, net, protocol, session } = require('electron')
const path = require('node:path')
const url = require('url')

app.whenReady().then(() => {
const partition = 'persist:example'
const ses = session.fromPartition(partition)

ses.protocol.handle('atom', (request) => {
const filePath = request.url.slice('atom://'.length)
return net.fetch(url.pathToFileURL(path.resolve(__dirname, filePath)).toString())
})

const mainWindow = new BrowserWindow({ webPreferences: { partition } })
})

方法

protocol 模块具有以下方法:

protocol.registerSchemesAsPrivileged(customSchemes)

注意. 此方法只能在 appready 事件触发前调用,且只能调用一次

Registers the scheme as standard, secure, bypasses content security policy for resources, allows registering ServiceWorker, supports fetch API, streaming video/audio, and V8 code cache. Specify a privilege with the value of true to enable the capability.

下面是一个注册特权协议的示例代码,可以绕过内容安全策略:

const { protocol } = require('electron')
protocol.registerSchemesAsPrivileged([
{ scheme: 'foo', privileges: { bypassCSP: true } }
])

A standard scheme adheres to what RFC 3986 calls generic URI syntax. For example http and https are standard schemes, while file is not.

按标准将一个scheme注册, 将保证相对和绝对资源在使用时能够得到正确的解析。 否则, 该协议将表现为 file 协议, 而且,这种文件协议将不能解析相对路径。

例如, 当您使用自定义协议加载以下内容时,如果你不将其注册为标准scheme, 图片将不会被加载, 因为非标准scheme无法识别相对 路径:

<body>
<img src='test.png'>
</body>

注册一个scheme作为标准scheme将允许其通过FileSystem 接口访问文件。 否则, 渲染器将会因为该scheme,而抛出一个安全性错误。

在非标准 schemes 下,网络存储 Api (localStorage, sessionStorage, webSQL, indexedDB, cookies) 默认是被禁用的。 所以一般来说如果你想注册一个自定义协议来替换http协议,你必须将其注册为标准 scheme:

如果 Protocols 需要使用流 (http 和 stream 协议) 应设置 stream: true<video><audio> HTML 元素默认需要协议缓冲其响应内容。 stream 标志将这些元素配置为正确的流媒体响应

protocol.handle(scheme, handler)

  • scheme string - scheme to handle, for example https or my-app. This is the bit before the : in a URL.
  • handler Function<GlobalResponse | Promise<GlobalResponse>>

Register a protocol handler for scheme. Requests made to URLs with this scheme will delegate to this handler to determine what response should be sent.

Either a Response or a Promise<Response> can be returned.

示例:

const { app, net, protocol } = require('electron')
const path = require('node:path')
const { pathToFileURL } = require('url')

protocol.registerSchemesAsPrivileged([
{
scheme: 'app',
privileges: {
standard: true,
secure: true,
supportFetchAPI: true
}
}
])

app.whenReady().then(() => {
protocol.handle('app', (req) => {
const { host, pathname } = new URL(req.url)
if (host === 'bundle') {
if (pathname === '/') {
return new Response('<h1>hello, world</h1>', {
headers: { 'content-type': 'text/html' }
})
}
// NB, this checks for paths that escape the bundle, e.g.
// app://bundle/../../secret_file.txt
const pathToServe = path.resolve(__dirname, pathname)
const relativePath = path.relative(__dirname, pathToServe)
const isSafe = relativePath && !relativePath.startsWith('..') && !path.isAbsolute(relativePath)
if (!isSafe) {
return new Response('bad', {
status: 400,
headers: { 'content-type': 'text/html' }
})
}

return net.fetch(pathToFileURL(pathToServe).toString())
} else if (host === 'api') {
return net.fetch('https://api.my-server.com/' + pathname, {
method: req.method,
headers: req.headers,
body: req.body
})
}
})
})

See the MDN docs for Request and Response for more details.

protocol.unhandle(scheme)

  • scheme string - scheme for which to remove the handler.

Removes a protocol handler registered with protocol.handle.

protocol.isProtocolHandled(scheme)

  • scheme string

Returns boolean - Whether scheme is already handled.

protocol.registerFileProtocol(scheme, handler) 已废弃

History

Returns boolean - 当前协议是否注册成功

注册一个 scheme 协议, 并将该文件作为响应返回。 当 requestscheme 内需要的请求时,requestcallback 将自动调用 handler

要处理 request, 应当使用文件的路径或具有 path 属性的对象来调用 callback。例如:callback(filePath)callback({ path: filePath }). filePath 必须是绝对路径

默认情况下, scheme 被当作 http:对待,与遵循"通用URI语法"的协议(如 file:)有不同的解析过程。

protocol.registerBufferProtocol(scheme, handler) 已废弃

History

Returns boolean - 当前协议是否注册成功

注册一个 scheme 协议, 将 Buffer作为响应发送

用法类似于 registerFileProtocol ,只是callback 会被Buffer对象或者带有data属性的对象调用。

示例:

protocol.registerBufferProtocol('atom', (request, callback) => {
callback({ mimeType: 'text/html', data: Buffer.from('<h5>Response</h5>') })
})

protocol.registerStringProtocol(scheme, handler) 已废弃

History

Returns boolean - 当前协议是否注册成功

注册一个 scheme 协议, 将 string 作为响应发送

除了 callback 只能被string 或拥有 data 属性的对象调用,使用方法与registerFileProtocol 相同

protocol.registerHttpProtocol(scheme, handler) 已废弃

History

Returns boolean - 当前协议是否注册成功

注册一个 scheme 协议, 将 HTTP 请求作为响应发送

除了 callback 只能被拥有 url 属性的对象调用外,使用方法与 registerFileProtocol相同。

protocol.registerStreamProtocol(scheme, handler) 已废弃

History

Returns boolean - 当前协议是否注册成功

注册一个 scheme 的协议将发送一个流作为响应。

除了 callback 方法只能被 ReadableStream 对象或拥有 data 属性的对象调用外,使用方法与 registerFileProtocol 相同。

示例:

const { protocol } = require('electron')
const { PassThrough } = require('stream')

function createStream (text) {
const rv = new PassThrough() // PassThrough is also a Readable stream
rv.push(text)
rv.push(null)
return rv
}

protocol.registerStreamProtocol('atom', (request, callback) => {
callback({
statusCode: 200,
headers: {
'content-type': 'text/html'
},
data: createStream('<h5>Response</h5>')
})
})

可以传递任何实现可读流 API 的对象 (可触发 data/end/error 事件)。 例如,如何返回文件:

protocol.registerStreamProtocol('atom', (request, callback) => {
callback(fs.createReadStream('index.html'))
})

protocol.unregisterProtocol(scheme) 已废弃

History
  • scheme string

Returns boolean - 协议是否成功取消注册

取消对自定义scheme的注册

protocol.isProtocolRegistered(scheme) 已废弃

History
  • scheme string

Returns boolean - 当前 scheme 是否已注册

protocol.interceptFileProtocol(scheme, handler) 已废弃

History

Returns boolean - 当前协议是否被拦截成功

终止 scheme 协议, 并将 handler 作为该protocol新的处理方式,即返回一个file。

protocol.interceptStringProtocol(scheme, handler) 已废弃

History

Returns boolean - 当前协议是否被拦截成功

终止 scheme 协议, 并将 handler 作为该protocol新的处理方式,即返回一个string

protocol.interceptBufferProtocol(scheme, handler) 已废弃

History

Returns boolean - 当前协议是否被拦截成功

终止 scheme 协议, 并将 handler 作为该protocol新的处理方式,即返回一个Buffer

protocol.interceptHttpProtocol(scheme, handler) 已废弃

History

Returns boolean - 当前协议是否被拦截成功

终止 scheme 协议, 并将 handler 作为该protocol新的处理方式,即返回一个新 HTTP 请求。

protocol.interceptStreamProtocol(scheme, handler) 已废弃

History

Returns boolean - 当前协议是否被拦截成功

它与 registerStreamProtocol方法相同, 不过它是用来替换现有的protocol处理方式。

protocol.uninterceptProtocol(scheme) 已废弃

History
  • scheme string

Returns boolean - 当前协议是否已成功解除拦截

移除为 scheme 安装的拦截器,并还原其原始处理方式。

protocol.isProtocolIntercepted(scheme) 已废弃

History
  • scheme string

Returns boolean - 表示是否已拦截了 scheme