【Tauri2】37——后端處理invoke

目錄

前言

正文

隨便看看

看看get

看看parse_invoke_request

看看message_handler

看看handle_ipc_message

看看webview的on_message方法

第一種情況的處理

第二種情況的處理

運行通信函數

返回的處理

?整個流程


前言

【Tauri2】033 __TAURI_INTERNALS__和invoke-CSDN博客文章瀏覽閱讀1k次,點贊24次,收藏24次。前面說過許多關于的invoke的事情,有通信函數,fetch、invoke_key等這些。這篇再來看看關于invoke的東西看來內部的通信是通過window.__TAURI_INTERNALS__對象來實現的。注冊通過函數用了聲明宏。筆者發現看github上的源碼,比看打包后的源碼更清晰,以后就使用github上的tauri的源碼了,不錯。哈哈哈哈t=P1C7t=P1C7t=P1C7t=P1C7t=P1C7t=P1C7t=P1C7。 https://blog.csdn.net/qq_63401240/article/details/147523382?spm=1001.2014.3001.5502前面介紹了前端invoke本質就是通過fetch發送一個post請求。

發送過去后,在后端是如何處理請求,如何調用并返回結果?

這篇就來看看這個核心的問題

正文

隨便看看

解析請求核心源代碼如下——protocol.rs

tauri/crates/tauri/src/ipc/protocol.rs at dev · tauri-apps/taurihttps://github.com/tauri-apps/tauri/blob/dev/crates/tauri/src/ipc/protocol.rsprotocol.rs主要的方法

看名字,簡單說說

message_handler,消息處理器,不知道干什么

get:得到,得到什么,直覺認為,應該得到請求。

handle_ipc_message :處理ipc消息,干什么,后面再說。

parse_invoke_message:解析invoke消息。

而且,get方法和message_handler方法是pub的

這個pub就非常有趣了。

pub說明可能會被調用,只能可能,不一定。

因此,在github中的tauri源碼中搜索一下

repo:tauri-apps/tauri ipc::protocol

結果如下

居然搜到了,進去看看

tauri/crates/tauri/src/manager/webview.rs at dev · tauri-apps/taurihttps://github.com/tauri-apps/tauri/blob/dev/crates/tauri/src/manager/webview.rs在里面,筆者發現了更多東西

在如下代碼調用了get方法

    if !registered_scheme_protocols.contains(&"ipc".into()) {let protocol = crate::ipc::protocol::get(manager.manager_owned());pending.register_uri_scheme_protocol("ipc", move |webview_id, request, responder| {protocol(webview_id, request, UriSchemeResponder(responder))});registered_scheme_protocols.push("ipc".into());}

這段代碼的意思,調用get獲得了protocol ,然后注冊ipc 這個uri_scheme_protocolscheme_protocol

這個pending是什么?

PendingWebview<EventLoopMessage, R>

可以發現是個?PendingWebview。雖然不知道干什么。

tauri/crates/tauri-runtime/src/webview.rs at dev · tauri-apps/taurihttps://github.com/tauri-apps/tauri/blob/dev/crates/tauri-runtime/src/webview.rs#L82可以找到,就是一個結構體,

里面有uri_scheme_protocols和ipc_handler等之類的字段

還可以發現ipc是個協議,tauri自定義的協議

雖然前面說過

發送的確是post請求,但是并不是使用http協議

總之,注冊ipc這個protocol。

    pending.ipc_handler = Some(crate::ipc::protocol::message_handler(manager.manager_owned(),));

也使用了message_handler。

注冊了這個ipc,發現請求,就會被webview處理。

以及message_handler處理handler

上面還注冊了tauri這個protocol

    if !registered_scheme_protocols.contains(&"tauri".into()) {......}

看看get

pub fn get<R: Runtime>(manager: Arc<AppManager<R>>) -> UriSchemeProtocolHandler {Box::new(move |label, request, responder| {#[cfg(feature = "tracing")]let span =...let respond = ...match *request.method() {Method::POST => {if let Some(webview) = manager.get_webview(label) {match parse_invoke_request(&manager, request) {...}} else {....}}Method::OPTIONS => {....}_ => {let mut r = http::Response::new("only POST and OPTIONS are allowed".as_bytes().into());....}}})
}

可以發現,這個get方法

首先判斷request的method,是post請求和option請求,其他請求返回,只能使用post和option。

如果是post請求就獲取webview,然后調用parse_invoke_request方法,處理后續的結果

看看parse_invoke_request

fn parse_invoke_request<R: Runtime>(#[allow(unused_variables)] manager: &AppManager<R>,request: http::Request<Vec<u8>>,
) -> std::result::Result<InvokeRequest, String> {#[allow(unused_mut)]let (parts, mut body) = request.into_parts();let cmd =....let has_payload = !body.is_empty();let invoke_key = parts...let url = Url::parse...let callback = CallbackFn(...)let error = CallbackFn(...)let body = ....let payload = InvokeRequest {cmd,callback,error,url,body,headers: parts.headers,invoke_key,};Ok(payload)
}

筆者刪減了許多,總體上看,就是從request請求中提取數據

cmd、callback、error、url、body、header、invoke_key

將提取到的數據合并成一個InvokeRequest ,返回。

筆者省略了許多東西。

傳入的是http::Request<Vec<u8>>

返回InvokeRequest

總之——將前端的request變成Rust可以處理的InvokeRequest

看看message_handler

pub fn message_handler<R: Runtime>(manager: Arc<AppManager<R>>,
) -> crate::runtime::webview::WebviewIpcHandler<crate::EventLoopMessage, R> {Box::new(move |webview, request| handle_ipc_message(request, &manager, &webview.label))
}

返回WebviewIpcHandler

pub type WebviewIpcHandler<T, R> = Box<dyn Fn(DetachedWebview<T, R>, Request<String>) + Send>;

WebviewIpcHandler是一個Box,Box指向一個實現了send的動態閉包。

有點復雜。

看看handle_ipc_message

fn handle_ipc_message<R: Runtime>(request: Request<String>, manager: &AppManager<R>, label: &str) {if let Some(webview) = manager.get_webview(label) {#[derive(Deserialize, Default)]#[serde(rename_all = "camelCase")]struct RequestOptions {....}#[derive(Deserialize)]struct Message {...}#[allow(unused_mut)]let mut invoke_message: Option<crate::Result<Message>> = None;let message = invoke_message.unwrap_or_else(|| {...serde_json::from_str::<Message>(request.body()).map_err(Into::into)});match message {Ok(message) => {let options = message.options.unwrap_or_default();let request = InvokeRequest {...};webview.on_message(request,Box::new(move |webview, cmd, response, callback, error| {...});}Err(e) => {....}}}
}

傳入了http::Request,

這一段代碼

serde_json::from_str::<Message>(request.body()).map_err(Into::into)

從請求中提取到Message,然后變成InvokeRequest?

這個邏輯和get+parse_invoke_request差不多

獲得了InvokeRequest之后,使用webview的on_message方法

實際上在get中也是這樣的,從?parse_invoke_request返回InvokeRequest然后使用

看看webview的on_message方法

 pub fn on_message(self, request: InvokeRequest, responder: Box<OwnedInvokeResponder<R>>{//獲取mangerlet manager = self.manager_owned();// 判斷invoke_keylet expected = manager.invoke_key();....// 初始化resolverlet resolver =  ...// 初始化messagelet message = InvokeMessage...);//判斷請求的來源let acl_origin = ...let (resolved_acl, has_app_acl_manifest) = ...// 初始化Invoke  let mut invoke = Invoke {message,resolver: resolver.clone(),acl: resolved_acl,};// 獲取插件名字和cmd的名字let plugin_command = request.cmd.strip_prefix("plugin:").map(|raw_command| {...(plugin, command)});// 判斷插件是否存在以及相關權限if (plugin_command.is_some() || has_app_acl_manifest){...}

InvokeRequest傳進來之后,

前面一大推都是在處理初始化和權限問題。

現在獲得了plugin_command?

后面就要對plugin_command?進行操作了

    if let Some((plugin, command_name)) = plugin_command {...} else {...}

然后就分成了兩部分,因為cmd有兩種情況

1、插件和插件的cmd,比如plugin:window|theme

2、自定義的cmd,比如greet

分別處理這兩種情況

第一種情況的處理

     //  為invoke的command設置要執行cmd的名字invoke.message.command = command_name;// 克隆一下,后面會發生所有權的轉移,無法使用command let command = invoke.message.command.clone();#[cfg(mobile)]let message = invoke.message.clone();#[allow(unused_mut)] // 執行cmd,返回boollet mut handled = manager.extend_api(plugin, invoke);#[cfg(mobile)]{// 移動端的插件            }// 不是true,說明沒找到命令if !handled {resolver.reject(format!("Command {command} not found"));}

核心處理是這一行代碼,把plugin和invoke傳進去

      let mut handled = manager.extend_api(plugin, invoke);

extend_api一直往下走

tauri/crates/tauri/src/plugin.rs at dev · tauri-apps/taurihttps://github.com/tauri-apps/tauri/blob/dev/crates/tauri/src/plugin.rs#L777發現如下代碼

  fn extend_api(&mut self, invoke: Invoke<R>) -> bool {(self.invoke_handler)(invoke)}

里面的代碼就是執行結構體TauriPlugin中的invoke_handler方法中的閉包。

感覺有點繞口,總之,執行閉包。

為什么是這樣寫的代碼?(self.invoke_handler)(invoke)

看看插件中的invoke_handler的定義

  invoke_handler: Box<InvokeHandler<R>>,
pub type InvokeHandler<R> = dyn Fn(Invoke<R>) -> bool + Send + Sync + 'static;

提取關鍵的部分,如下

Box<dyn Fn>

這表示一種動態分發的函數類型。簡單地說

1、左邊的括號是用于獲取Box包裝的閉包

2、右邊括號運行Box里面的閉包

總之,獲取閉包,運行閉包。

舉個簡單地例子

如下代碼,

fn use_world() {type World = Box<dyn Fn(String)>;struct Api {world: World,}let api = Api {world: Box::new(|s| {println!("hello {}", s);}),};(api.world)("world".to_string());
}

或者直接使用

fn use_world() {(Box::new(|s|{ println!("hello {}", s);}))("world".to_string());
}

閉包傳參Invoke。沒問題。

第二種情況的處理

只有cmd命令,沒有插件

  let command = invoke.message.command.clone();let handled = manager.run_invoke_handler(invoke);if !handled {resolver.reject(format!("Command {command} not found"));}

使用的是run_invoke_handler這個函數

  pub fn run_invoke_handler(&self, invoke: Invoke<R>) -> bool {(self.webview.invoke_handler)(invoke)}

一模一樣,不必細說

再看看注冊通信函數用的invoke_handler

  #[must_use]pub fn invoke_handler<F>(mut self, invoke_handler: F) -> SelfwhereF: Fn(Invoke<R>) -> bool + Send + Sync + 'static,{self.invoke_handler = Box::new(invoke_handler);self}

不必細說。?

運行通信函數

現在把Invoke傳進來了,至于內部又是如何運行的,可參考如下

【Tauri2】005——tauri::command屬性與invoke函數_tauri invoke-CSDN博客https://blog.csdn.net/qq_63401240/article/details/146581991?spm=1001.2014.3001.5502

【Tauri2】007——Tauri2和cargo expand-CSDN博客https://blog.csdn.net/qq_63401240/article/details/146632055?spm=1001.2014.3001.5502不必細說。

返回的處理

調用完閉包,還是是on_message這個函數中處理結果,返回給前端

webview.on_message(request,Box::new(move |_webview, _cmd, response, _callback, _error| {...respond(response);}),);

調用的respond 或者使用from_callback_fn、responder_eval

  match response {....Channel::from_callback_fn(...);} else {....responder_eval(...)}}

這就不細說,就是返回結果,比如需要考慮返回的字符串、還是使用回調函數callback、還是二進制數組等的原始數據等。?

比如事件Event,就需要callback

返回不一樣,處理不一樣。

 fn responder_eval<R: Runtime>(webview: &crate::Webview<R>,js: crate::Result<String>,error: CallbackFn,)

responder_eval的函數簽名中可以看出,好像還是處理錯誤的回調函數。

?整個流程

綜上所述,簡單地說,后端解析的流程如下

??????? -> invoke

??????? -> windon._TAURI_INTERNALS__.invoke

??????? -> fetch

????????-> parse_invoke_request / handle_ipc_message?

????????-> on_message?

????????->?extend_api /?? run_invoke_handler

??????? -> invoke_handler

????????->?response

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/79533.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/79533.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/79533.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

kotlin 05flow -從 LiveData 遷移到 Kotlin Flow 完整教程

一 從 LiveData 遷移到 Kotlin Flow 完整教程 LiveData 長期以來是 Android 架構組件中狀態管理的核心&#xff0c;但隨著 Kotlin Flow 的成熟&#xff0c;Google 官方推薦將現有 LiveData 遷移到 Flow。本教程基于官方文章并擴展實踐細節&#xff0c;完成平滑遷移。 一、為什…

C++負載均衡遠程調用學習之獲取主機信息功能

目錄 01Lars-lbAgentV0.2-賦值均衡數據結構關系分析 02 Lars-lbAgent0.2-host_info-load_balance-route_lb數據結構的定義 03Lars-lbAgentV0.2-proto協議的定義 04 Lars-lbAgentV0.2-route_lb與UDP server的關聯 05 -Lars-lbAgentV0.2-route_lb與UDP server的關聯 06Lars…

2025系統架構師---論軟件的設計模式論文

2023 年,我所在的公司承擔了某部網絡靶場的研發任務。我作為公司的技 術總監,希望能打造基于網絡靶場的系列產品,參與到項目的設計中,以期開發 擴展性和可維護性良好的網絡靶場,為以后的產品開發打下基礎。網絡靶場是網 絡安全技術研究的基礎支撐平臺,它利用虛擬的和實物…

Kubernetes排錯(七)-節點排錯

1、節點 Crash 與 Vmcore 分析 kdump 介紹? 目前大多 Linux 發新版都會默認開啟 kdump 服務&#xff0c;以方便在內核崩潰的時候, 可以通過 kdump 服務提供的 kexec 機制快速的啟用保留在內存中的第二個內核來收集并轉儲內核崩潰的日志信息(vmcore 等文件), 這種機制需要服務…

【QT】QT中的軟鍵盤設計

QT的軟鍵盤設計 1.軟鍵盤制作步驟2.介紹有關函數的使用3.出現的編譯錯誤及解決辦法示例代碼1&#xff1a;按鍵事件實現軟鍵盤現象&#xff1a;示例代碼2&#xff1a;按鍵事件實現軟鍵盤&#xff08;加特殊按鍵&#xff09;現象&#xff1a; 軟鍵盤移植到新的工程的步驟&#xf…

【LaTeX+VSCode本地Win11編譯教程】

LaTeXVSCode本地編譯教程參考視頻&#xff1a; LaTeXVSCode本地編譯教程 下面提供一種Win11的Latex環境配置和設置方案&#xff0c;首先vscode安裝參考博客&#xff1a;【VscodeGit教程】&#xff0c;然后準備安裝Latex相關組件 在 https://miktex.org/download 下載 miktex 并…

2025五一杯數學建模ABC題賽題已出

2025五一杯數學建模ABC題賽題已出 A: B: C:

Springclound常用五大組件及其使用原理

注冊中心Eureka Eureka-Server&#xff1a;就是服務注冊中心&#xff08;可以是一個集群&#xff09;&#xff0c;對外暴露自己的地址。 提供者&#xff1a;啟動后向Eureka注冊自己信息&#xff08;地址&#xff0c;服務名稱等&#xff09;&#xff0c;并且定期進行服務續約 …

Docker —— 隔離的基本操作(2)

Docker —— 隔離的基本操作&#xff08;2&#xff09; unshareunshare 命令詳解基本語法常用選項常用示例實際應用場景注意事項與 Docker 的關系1. 執行命令2. 修改主機名3. 退出命名空間4. 驗證宿主機主機名關鍵原理類比 Docker 容器總結 實戰操作一&#xff08;PID 隔離&…

Java List分頁工具

PageUtil.java import com.google.common.collect.Lists; import com.jd.platform.hotkey.dashboard.common.domain.Page; import org.springframework.util.CollectionUtils;import java.util.ArrayList; import java.util.List;public class PageUtil {/*** 通用分頁工具類*…

中陽策略:如何從K線行為中提取交易邏輯信號?

中陽策略&#xff1a;如何從K線行為中提取交易邏輯信號&#xff1f; 在量化趨勢研究中&#xff0c;中陽形態常被視作市場動能變化的重要標志。它不僅代表價格的強勢上行&#xff0c;更隱含著主力資金換手與情緒轉換的信號。將“中陽”這一結構元素抽象為模型中的“強動能突破”…

Java SE(8)——繼承

1.繼承的概念&作用 在Java中&#xff0c;繼承是面向對象編程的三大基本特性之一&#xff08;還有封裝和多態&#xff09;&#xff0c;允許一個類&#xff08;子類/繼承類&#xff09;繼承另一個類&#xff08;父類/基類&#xff09;的屬性和方法 繼承的核心目的是&#xf…

Python爬蟲(18)反爬攻防戰:動態IP池構建與代理IP實戰指南(突破95%反爬封禁率)

目錄 引言一、背景&#xff1a;為什么代理IP是爬蟲的“第二生命”&#xff1f;1.1 反爬系統的IP檢測三把刀1.2 代理IP的核心價值 二、基礎實戰&#xff1a;快速搭建代理IP系統2.1 免費代理IP的獲取與篩選2.2 代理IP的智能容錯機制 三、高階攻防&#xff1a;突破企業級反爬封鎖3…

LFU算法解析

文章目錄 LFU緩存中關鍵變量的訪問與更新機制1. min_freq - 最小頻率訪問時機更新時機更新示例 2. capacity - 緩存容量訪問時機更新時機訪問示例 3. key_to_node - 鍵到節點的映射訪問時機更新時機更新示例 4. freq_to_dummy - 頻率到鏈表啞節點的映射訪問時機更新時機更新示例…

ByteArrayInputStream 類詳解

ByteArrayInputStream 類詳解 ByteArrayInputStream 是 Java 中用于從字節數組讀取數據的輸入流&#xff0c;位于 java.io 包。它允許將內存中的字節數組當作輸入流來讀取&#xff0c;是處理內存數據的常用工具。 1. 核心特性 內存數據源&#xff1a;從字節數組&#xff08;b…

rvalue引用()

一、先確定基礎:左值(Lvalue)和右值(Rvalue) 理解Rvalue引用,首先得搞清楚左值和右值的概念。 左值(Lvalue):有明確內存地址的表達式,可以取地址。比如變量名、引用等。 復制代碼 int a = 10; // a是左值 int& ref = a; // ref也是左值右值(Rval…

吳恩達深度學習作業 RNN模型——字母級語言模型

一. 簡單復習一下RNN RNN RNN適用于處理序列數據&#xff0c;令是序列的第i個元素&#xff0c;那么就是一個長度為的序列&#xff0c;NLP中最常見的元素是單詞&#xff0c;對應的序列是句子。 RNN使用同一個神經網絡處理序列中的每一個元素。同時&#xff0c;為了表示序列的…

基于python的哈希查表搜索特定文件

Python有hashlib庫&#xff0c;支持多種哈希算法&#xff0c;比如MD5、SHA1、SHA256等。通常SHA256比較安全&#xff0c;但MD5更快&#xff0c;但可能存在碰撞風險&#xff0c;得根據自己需求決定。下面以SHA256做例。 import hashlib import os from typing import Dict, Lis…

idea創建springboot項目無法創建jdk8原因及多種解決方案

idea創建springboot項目無法創建jdk8原因及多種解決方案 提示&#xff1a;幫幫志會陸續更新非常多的IT技術知識&#xff0c;希望分享的內容對您有用。本章分享的是springboot的使用。前后每一小節的內容是存在的有&#xff1a;學習and理解的關聯性。【幫幫志系列文章】&#x…

【C++進階十】多態深度剖析

【C進階十】多態深度剖析 1.多態的概念及條件2.虛函數的重寫3.重寫、重定義、重載區別4.C11新增的override 和final5.抽象類6.虛表指針和虛表6.1什么是虛表指針6.2指向誰調用誰&#xff0c;傳父類調用父類&#xff0c;傳子類調用子類 7.多態的原理8.單繼承的虛表狀態9.多繼承的…