当前位置: 首页 > news >正文

【Tauri2】016——后端Invoke结构体和invoke_key

前言

【Tauri2】015——前端的事件、方法和invoke函数-CSDN博客https://blog.csdn.net/qq_63401240/article/details/147003241?spm=1001.2014.3001.5502【Tauri2】005——tauri::command属性与invoke函数-CSDN博客https://blog.csdn.net/qq_63401240/article/details/146581991?spm=1001.2014.3001.5502

前面说过,前端的invoke函数本质就是发送一个post请求。

这篇就来看看后端的Invoke,这个结构体

#[default_runtime(crate::Wry, wry)]
pub struct Invoke<R: Runtime> {
  /// The message passed.
  pub message: InvokeMessage<R>,

  /// The resolver of the message.
  pub resolver: InvokeResolver<R>,

  /// Resolved ACL for this IPC invoke.
  pub acl: Option<Vec<ResolvedCommand>>,
}

还有 InvokeMessage

pub struct InvokeMessage<R: Runtime> {
  /// The webview that received the invoke message.
  pub(crate) webview: Webview<R>,
  /// Application managed state.
  pub(crate) state: Arc<StateManager>,
  /// The IPC command.
  pub(crate) command: String,
  /// The JSON argument passed on the invoke message.
  pub(crate) payload: InvokeBody,
  /// The request headers.
  pub(crate) headers: HeaderMap,
}

正文

准备

首先,写一段简单地通信函数

#[command(rename_all = "snake_case")]
fn greet(name_go: &str) -> String {
    format!("Hello, {}!", name_go)
}

这个rename_all 是command属性的参数,里面有三个参数

struct WrapperAttributes {
  root: TokenStream2,
  execution_context: ExecutionContext,
  argument_case: ArgumentCase,
}

更具体来说,是root、rename_all、async

rename_all 可设为camelCase和snake_case,总之,设置变量的命名规则。

async,设置为异步函数

root:用于指定命令的根路径

总之,上面通信函数就是接受一个字符串切片,返回字符串。

注册

.invoke_handler(tauri::generate_handler![greet])

简单通信一下

    function handleClick() {
       invoke('greet', { name_go: 'World' })

    }

结果如下

没问题

cargo expand宏展开

命令如下

cargo expand >expanded.rs

宏展开的结果如下

  .invoke_handler(move |__tauri_invoke__| {
            let __tauri_cmd__ = __tauri_invoke__.message.command();
            match __tauri_cmd__ {
                "greet" => {
                    #[allow(unused_imports)]
                    use ::tauri::ipc::private::*;
                    #[allow(unused_variables)]
                    let ::tauri::ipc::Invoke {
                        message: __tauri_message__,
                        resolver: __tauri_resolver__,
                        acl: __tauri_acl__,
                    } = __tauri_invoke__;
                    let result = greet(
                        match ::tauri::ipc::CommandArg::from_command(::tauri::ipc::CommandItem {
                            plugin: ::core::option::Option::None,
                            name: "greet",
                            key: "name_go",
                            message: &__tauri_message__,
                            acl: &__tauri_acl__,
                        }) {
                            Ok(arg) => arg,
                            Err(err) => {
                                __tauri_resolver__.invoke_error(err);
                                return true;
                            }
                        },
                    );
                    let kind = (&result).blocking_kind();
                    kind.block(result, __tauri_resolver__);
                    return true;
                }
                _ => {
                    return false;
                }
            }
        })

实际上,笔者把用于注册中的代码,替换成宏展开的结果的代码依然可以运行,很显然。

笔者本来想打印__tauri_invoke__,这个__tauri_invoke__就是Invoke,但是打印不了,没有实现Debug trait,被迫只能打印message,代码如下

            let __tauri_cmd__ = __tauri_invoke__.message.command();
            println!("{:#?}",__tauri_invoke__.message);

再次运行

打印了非常多的东西

笔者给出关键的打印

 command: "greet",
 payload: Json(
        Object {
            "name_go": String("World"),
        },
    ),
 headers: {
        "accept": "*/*",
        "content-type": "application/json",
        "origin": "http://localhost:1420",
        "referer": "http://localhost:1420/",
        "tauri-callback": "509703145",
        "tauri-error": "2011447483",
        "tauri-invoke-key": "k25Bp{sKYlL]8PUx26sG",
        "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36 Edg/134.0.0.0",
        "sec-ch-ua": "\"Microsoft Edge\";v=\"134\", \"Chromium\";v=\"134\", \"Not:A-Brand\";v=\"24\", \"Microsoft Edge WebView2\";v=\"134\"",
        "sec-ch-ua-mobile": "?0",
        "sec-ch-ua-platform": "\"Windows\"",
    },

这个headers正是InvokeMessage中的。

后面调用command方法

let __tauri_cmd__ = __tauri_invoke__.message.command();

获取到greet字符串,然后通过match匹配,执行greet函数,返回结果,完成了通信。

现在看来,过程也就这么回事。

当然,宏展开后,就可以改前端的名字了

如果把match中的"greet"改成"greet123"

那么前端代码

    function handleClick() {
       invoke('greet123', { name_go: 'World' })

    }

正常运行。 

通过fetch,进行通信

代码如下


export async function useWindowEvent() {
    const error=transformCallback((event:Event) => {
        console.log("失败")
    })
    const success=transformCallback((event:Event) => {
        console.log("成功")
    })

    const __TAURI_INVOKE_KEY__ = JSON.parse('"MW76q<X9iZ}Cv<yWv#l."')
    fetch("http://ipc.localhost/greet123", {
        method: 'POST',
        body: JSON.stringify({
            'name_go':"123123"
        }),
        headers: {
            'Content-Type': "application/json",
            'Tauri-Callback': success,
            'Tauri-Error': error,
            'Tauri-Invoke-Key': __TAURI_INVOKE_KEY__,
        }
    })

}

报错了,这个__TAURI_INVOKE_KEY__ 不对

 笔者在RustRover中搜索关键字__TAURI_INVOKE_KEY__ expected

找到了这个打印语句出现的位置

use tauri::webview::Webview

Webview的on_message方法中

 笔者还找到其他东西

    #[derive(Deserialize)]
    struct Message {
      cmd: String,
      callr: CallbackFn,
      payload: serde_json::Value,
      options: Option<RequestOptions>,
      #[serde(rename = "__TAURI_INVOKE_KEY__")]
      invoke_key: String,
    }

原来本来的名字叫invoke_key,

该这么获得呢? 很简单

AppHandle中有invoke_key方法,App也有。

AppHandle in tauri - Rusthttps://docs.rs/tauri/latest/tauri/struct.AppHandle.html#method.invoke_key现在就明白了,哈哈哈哈,原来如此

invoke_key发送给前端,并通过fetch使用

这其实挺麻烦的,笔者想使用事件,写在setup中,居然没反应,检测不到。

笔者暂时使用通信函数传输了

#[command]
pub fn send_key(app:AppHandle) -> String {
    app.invoke_key().to_string().clone()
}

 前端获得并使用

    function handleClick() {
        invoke('send_key').then(
            (res: any) => {
                useWindowEvent(res)
            }
        )
    }

感觉有点怪,简单地意思意思一下

简单修改一下fetch,成功

总之,获得了这个__TAURI_INVOKE_KEY__

 实际上,运行后,这个就相当于全局的状态一样,不会变的,只要不关闭程序,获取一次,就可以了。

再看看invoke_handler的函数签名

#[must_use]
pub fn invoke_handler<F>(mut self, invoke_handler: F) -> Self
where
  F: Fn(Invoke<R>) -> bool + Send + Sync + 'static,

参数invoke_handler的泛型是F,F需要传入一个闭包或者函数,这个闭包中的参数是正是Invoke,返回bool

自定义注册和使用通信函数

从上面这么多信息,实际上,这很简单了

首先,不需要command属性了

fn greet(name_go: &str) -> String {
    format!("Hello, {}!", name_go)
}

 就像是普通函数一样了。

关键导包

use tauri::ipc::private::ResponseKind;
use tauri::ipc::InvokeBody;

 注册

 .invoke_handler(move |invoke| {
            let cmd = invoke.message.command();
            match cmd {
                "greet" => {
                    let name_go = match invoke.message.payload() {
                        InvokeBody::Json(json) => json.get("name_go")
                                .and_then(|v| v.as_str()).unwrap_or_default(),
                        _ => {
                            return false;
                        }
                    };
                    let result = greet(name_go);
                    let kind = (&result).blocking_kind();
                    kind.block(result, invoke.resolver);
                    return true;
                }
                _ => {
                    return false;
                }
            }
        })

 payload方法返回是InvokeBody 的引用,是个enum,通过match处理enum

  #[inline(always)]
  pub fn payload(&self) -> &InvokeBody {
    &self.payload
  }
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(PartialEq))]
pub enum InvokeBody {
  /// Json payload.
  Json(JsonValue),
  /// Bytes payload.
  Raw(Vec<u8>),
}

调用greet函数,返回结果。

前端无论使用invoke函数还是fetch,都没有问题

当然,非常不严谨,有很多细节需要考虑。

总之,大致是这样的。

没问题。哈哈哈哈哈

最后的补充

前端的invoke函数,除了使用

import {invoke} from "@tauri-apps/api/core";

 还可以使用全局的

const invoke = window.__TAURI__.core.invoke;

需要在配置文件中进行如下设置

这个window不是窗口,是一个全局对象

总结

invoke无论在前端和还是在后端,都是重中之重,写了几篇关于invoke,算是差不多了,Invoke暂时就写到这。

后面就写点更具体的,比如托盘,全局状态管理等之类,总之,感觉还有很多东西。

不慌。

相关文章:

  • opus+ffmpeg+c++实现录音
  • Windwos的DNS解析命令nslookup
  • Linux系统的不同发行版的常用命令
  • 大储EMS能量管理系统解决方案:助力企业实现智慧能源转型
  • SSM框架相关面试题
  • Vue3实战七、登录认证与退出登录
  • Spring AOP 核心的技术之一:动态代理
  • 其他合成方式介绍
  • nacos集群部署
  • 【redis】summary
  • rust 同时处理多个异步任务,并在一个任务完成退出
  • PythonJSON解析如何优雅处理嵌套JSON字符串
  • springboot中使用async实现异步编程
  • Docker Compose 部署Nginx反向代理 tomcat
  • 每日算法-250407
  • 数字经济产业标杆:树莓集团如何塑造产业服务价值体系
  • 没有独立显卡如何安装torch
  • 极简设计的力量:用 `apiStore` 提升项目效率与稳定性
  • oracle查询是否锁表了
  • Objective-C语言的编程范式
  • 国寿资产获批参与第三批保险资金长期投资改革试点
  • 讲座预告|以危机为视角解读全球治理
  • 人民日报:从“轻微免罚”看涉企执法方式转变
  • 博柏利上财年营收下降17%,计划裁员1700人助推股价涨超18%
  • 第1现场 | 美国称将取消制裁,对叙利亚意味着什么
  • 河南信阳拟发文严控预售许可条件:新出让土地开发的商品房一律现房销售