rust api接口开发(以登陆和中间件鉴权为例)

2024-08-21 07:52

本文主要是介绍rust api接口开发(以登陆和中间件鉴权为例),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

rust rest api接口开发

所需依赖

  • axum
  • tokio
  • redis
cargo add axum redis
cargo add tokio --features=full

路由服务创建和运行

//子路由
let v1router = axum::Router::new();
//主路由,并将子路由绑定到主路由
let router=axum::Router::new().nest("/v1",v1router);let l = tokio::net::TcpListener::bind("0.0.0.0:8080").await.expect("bind 8080 failed");axum::serve(l, router).await.expect("serve server failed");

handle 函数到路由

router调用route等函数后会转移自身,所以你可以选择两种方式使用router:链式调用,和重新赋值
链式调用

use axum::routing::get;
let router=axum::Router::new().route("/echo1",get(echo1)).route("/echo2",post(echo2));

重新赋值

use axum::routing::get;
let router=axum::Router::new();
let router=router.route("/echo1",get(echo1));
let router=router.route("/echo2",get(echo2));

handler 函数
rust axum的handler函数相对于golang 的web框架来讲要比较智能,他已经帮你自动做好mvc中的controller层,而golang的gin框架和iris都需要或多或少自己实现或者使用mvc脚手架(例如iris/mvc),更要命的是大部分脚手架都是使用golang运行时反射实现的,性能相对于在编译期间通过宏来静态反射生成的要差许多
这是一个简单的不需要任何参数,直接返回hello的接口。当然如果你需要从body中读取json或者原body都可以在函数参数加,axum会自动识别,响应如果需要制定status也可以在响应里添加StatusCode

let router=router.route("/greet",get(||async{Html("hello")
}));

这个接口也可以这样写

let router=router.route("/greet",get(greets));//函数定义
async fn greets()->Html<&'static str>{return Html("hello");}

中间件

let router=router.layer(axum::middleware::from_fn(|req:Request,next:axum::middleware::Next|async{
//做你想做的操作,next.run等效于golang web框架中xxx.Context 下的Next()next.run(req).await
}));

Service注册

let router = router.layer(axum::Extension(Arc::new(WebService::new(AuthService::new("redis://localhost:6379"),
))));

以登陆和鉴权接口演示

这里以登陆和鉴权接口进行演示,登陆成功后将token存入redis中. 为了方便演示流程,就直接忽略数据库里查询匹配,用户名和密码一样就模拟通过

#[cfg(test)]
mod web{use std::sync::Arc;use axum::{extract::Request, http::HeaderMap, middleware::Next, response::Html, Extension, Json,};use tokio::sync::Mutex;#[tokio::test]async fn start() {let v1router = axum::Router::new().route("/greet", axum::routing::get(greet)).layer(axum::middleware::from_fn(|Extension(ext): Extension<Arc<WebService>>,mut req: Request, next: Next| async move {//token校验,没有什么也不返回,当前中间件只对v1router中服务生效let token = req.headers().get("token");if let None = token {return axum::http::Response::<axum::body::Body>::new(axum::body::Body::empty(),);}let token = token.unwrap().to_str().unwrap();let mut bl = ext.auth_service.lock().await;let username=bl.check_token(token.to_string());if let None=username{eprintln!("not found token {token}");return axum::http::Response::<axum::body::Body>::new(axum::body::Body::empty(),);}let username=username.unwrap();req.headers_mut().insert("userName", username.as_str().parse().unwrap());drop(bl);let response: axum::http::Response<axum::body::Body> = next.run(req).await;response},));let router = axum::Router::new().route("/login", axum::routing::post(login)).nest("/v1", v1router).layer(axum::Extension(Arc::new(WebService::new(AuthService::new("redis://localhost:6379"),))));let l = tokio::net::TcpListener::bind("0.0.0.0:8080").await.expect("bind 8080 failed");axum::serve(l, router).await.expect("serve server failed");}async fn login(Extension(ext): Extension<Arc<WebService>>,Json(req): Json<args::Login>,) -> Json<resp::Response<String>> {let mut bll = ext.auth_service.lock().await;match bll.login(req.username, req.password) {None => Json(resp::Response::error("login failed")),Some(token) => Json(resp::Response::ok(token)),}}async fn greet(headers: HeaderMap) -> Json<resp::Response<String>> {let username = headers.get("userName").unwrap().to_str().unwrap();Json(resp::Response::ok(format!("hello {username}")))}struct WebService {auth_service: Mutex<AuthService>,}impl WebService {pub fn new(auth: AuthService) -> Self {Self {auth_service: Mutex::new(auth),}}}struct AuthService {red_conn: redis::Client,}impl AuthService {pub fn new(uri: &str) -> Self {Self {red_conn: redis::Client::open(uri).expect("connect to redis failed"),}}pub fn login(&mut self, username: String, password: String) -> Option<String> {if username != password {return None;}let now = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_millis();let now = (now % (1 << 32)) as u32;let token = format!("{:2x}", now);let mut conn = self.red_conn.get_connection().expect("get redis connection failed");let ans = redis::cmd("set").arg(token.as_str()).arg(username).arg("EX").arg(60 * 60).exec(&mut conn);if let Err(err) = ans {eprintln!("set token to redis error {err}");}Some(token)}pub fn check_token(&mut self, token: String) -> Option<String> {let mut conn = self.red_conn.get_connection().expect("get redis connection failed");let ans = redis::cmd("get").arg(token.as_str()).query::<String>(&mut conn);match ans {Ok(data) => Some(data),Err(err) => {eprintln!("check from redis failed {err}");None}}}}mod args {#[derive(serde::Deserialize)]pub struct Login {pub username: String,pub password: String,}}mod resp {#[derive(serde::Serialize)]pub struct Response<T> {ok: bool,reason: &'static str,data: Option<T>,}impl<T> Response<T> {pub fn ok(data: T) -> Self {Self {ok: true,reason: "",data: Some(data),}}pub fn error(reason: &'static str) -> Self {Self {ok: false,reason: reason,data: None,}}}}}

结果展示

测试脚本

#!/bin/bash
function login(){curl -H 'Content-Type:application/json' -X POST http://localhost:8080/login -d '{"username":"jesko","password":"jesko"}'
}
function greet(){curl -H "token:$token" -X GET http://localhost:8080/v1/greet
}
for va in "$@";do$va
done

在这里插入图片描述

这篇关于rust api接口开发(以登陆和中间件鉴权为例)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1092570

相关文章

Spring Boot集成/输出/日志级别控制/持久化开发实践

《SpringBoot集成/输出/日志级别控制/持久化开发实践》SpringBoot默认集成Logback,支持灵活日志级别配置(INFO/DEBUG等),输出包含时间戳、级别、类名等信息,并可通过... 目录一、日志概述1.1、Spring Boot日志简介1.2、日志框架与默认配置1.3、日志的核心作用

全面解析Golang 中的 Gorilla CORS 中间件正确用法

《全面解析Golang中的GorillaCORS中间件正确用法》Golang中使用gorilla/mux路由器配合rs/cors中间件库可以优雅地解决这个问题,然而,很多人刚开始使用时会遇到配... 目录如何让 golang 中的 Gorilla CORS 中间件正确工作一、基础依赖二、错误用法(很多人一开

SpringBoot监控API请求耗时的6中解决解决方案

《SpringBoot监控API请求耗时的6中解决解决方案》本文介绍SpringBoot中记录API请求耗时的6种方案,包括手动埋点、AOP切面、拦截器、Filter、事件监听、Micrometer+... 目录1. 简介2.实战案例2.1 手动记录2.2 自定义AOP记录2.3 拦截器技术2.4 使用Fi

PyQt5 GUI 开发的基础知识

《PyQt5GUI开发的基础知识》Qt是一个跨平台的C++图形用户界面开发框架,支持GUI和非GUI程序开发,本文介绍了使用PyQt5进行界面开发的基础知识,包括创建简单窗口、常用控件、窗口属性设... 目录简介第一个PyQt程序最常用的三个功能模块控件QPushButton(按钮)控件QLable(纯文本

基于Python开发一个图像水印批量添加工具

《基于Python开发一个图像水印批量添加工具》在当今数字化内容爆炸式增长的时代,图像版权保护已成为创作者和企业的核心需求,本方案将详细介绍一个基于PythonPIL库的工业级图像水印解决方案,有需要... 目录一、系统架构设计1.1 整体处理流程1.2 类结构设计(扩展版本)二、核心算法深入解析2.1 自

Knife4j+Axios+Redis前后端分离架构下的 API 管理与会话方案(最新推荐)

《Knife4j+Axios+Redis前后端分离架构下的API管理与会话方案(最新推荐)》本文主要介绍了Swagger与Knife4j的配置要点、前后端对接方法以及分布式Session实现原理,... 目录一、Swagger 与 Knife4j 的深度理解及配置要点Knife4j 配置关键要点1.Spri

SpringBoot开发中十大常见陷阱深度解析与避坑指南

《SpringBoot开发中十大常见陷阱深度解析与避坑指南》在SpringBoot的开发过程中,即使是经验丰富的开发者也难免会遇到各种棘手的问题,本文将针对SpringBoot开发中十大常见的“坑... 目录引言一、配置总出错?是不是同时用了.properties和.yml?二、换个位置配置就失效?搞清楚加

SpringBoot+Redis防止接口重复提交问题

《SpringBoot+Redis防止接口重复提交问题》:本文主要介绍SpringBoot+Redis防止接口重复提交问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不... 目录前言实现思路代码示例测试总结前言在项目的使用使用过程中,经常会出现某些操作在短时间内频繁提交。例

Python中对FFmpeg封装开发库FFmpy详解

《Python中对FFmpeg封装开发库FFmpy详解》:本文主要介绍Python中对FFmpeg封装开发库FFmpy,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录一、FFmpy简介与安装1.1 FFmpy概述1.2 安装方法二、FFmpy核心类与方法2.1 FF

基于Python开发Windows屏幕控制工具

《基于Python开发Windows屏幕控制工具》在数字化办公时代,屏幕管理已成为提升工作效率和保护眼睛健康的重要环节,本文将分享一个基于Python和PySide6开发的Windows屏幕控制工具,... 目录概述功能亮点界面展示实现步骤详解1. 环境准备2. 亮度控制模块3. 息屏功能实现4. 息屏时间