如本文题中描述,实际产品中,客户端很大几率是要通过rust内封装好的http的reqwest包,请求远程服务器的内容,类似于web的ajax请求。如果你用fetch或者axios这些库,会遇到类似
Access-Control-Allow-Origin cannot contain more than one origin
这种涉及到跨域错误的问题。如果你不想碰rust的reqwest,最简单的土办法是使用jquery的XHR封装好的$.ajax,当然别忘了在headers里面传输一个字段:
"X-Requested-With":"XMLHttpRequest"
来模拟普通的web ajax请求,可以规避问题,同时适应后端接口的检查。
但在tauri官方看来,这种实现是“不正常”的。他们希望你invoke到rust后端的handler请求列表的方法名,通过rust客户端模块去做统一请求和返回。从代码层面上,这是怎么实现的呢?
代码实现
tauri的基本项目代码,你按照官方给的demo就可以做到。本文只说tauri“标准”设计上http请求有关的变动,我们先看main.rs里的tauri的启动代码
tauri::Builder::default() .invoke_handler(tauri::generate_handler![ close_splashscreen, class_file1::fn_foo, class_file1::fn_bar, class_file2::fn_alpha, ]) .menu(menu) .on_menu_event(|event| { match event.menu_item_id() { "quit" => { std::process::exit(0); } "close" => { event.window().close().unwrap(); } _ => {} } }) .run(tauri::generate_context!()) .expect("Program crashed.");
关注一下上述代码当中的tauri::generate_handler!巨集,有若干方法,绑定的是rust项目里面的class_file1文件下的公开方法fn_foo方法等等。于是,新建一个class_file1.rs, 然后在main.rs里添加mod关联:
mod class_file1;
fn_foo.rs的内部实现一个请求方法:
#[tauri::command] pub async fn fn_foo(some_arg: u32) -> String { match reqwest::get(format!("https://api.wwooo.com/some_url?arg1={}", some_arg)) .await { Ok(resp) => resp.text().await.unwrap(), Err(err) => format!("{}", err) } }
其中上述代码中的api.wwooo.com/some_url?arg1=xxx就是你实际的后端接口地址。返回结果写String? 因为接口返回返回结果变量resp确实也都是一些json字符串。
注意,这里的some_arg的检查是严格的,你如果怕程序报错,可以按需在业务逻辑里分配一些默认的参数。
接下来,前端页面javascript代码里,要invoke关键字和一下rust代码中的方法名一致。实际生产中,我会先封装一个异步的js方法,比如:
async function get_my_data(arg1) { let r = await invoke("fn_foo", {arg1: Number(arg1)}); r = JSON.parse(r); if (typeof r.code != 'undefined' && r.code === 1) { // TODO // 我的后端会返回{"code": XXX, "xxx": XXX... } 这样的json串 // 你的业务逻辑 } // 补充其他的业务逻辑 }
这样,你在适当的场景下调用这个函数就可以和普通的axois/ajax请求一样操作了
有的时候需要先把这个async方法的代码封装到稍微靠前面一点,笔者用的是比较早版本的tauri, JS会报一些奇怪的加载顺序错误,找不到方法。
有些人实在懒得分那么多文件,那可以把所有的handler函数都写到main.rs一个文件里,这也没有问题。
接下来用
cargo tauri dev
测试一下,看看跑通没?
总结
网上有一些说tauri完败或者完胜electron.js的论调。从实际开发我们看,tauri有rust的加持后,甩开了node.js沉重的包袱,有不少可取之处。可以应用于生产。