实现后台管理web服务

本章将使用 axum 调用 gRPC 服务来实现后台管理的 web 服务。

状态共享 AppState 增加管理员服务

后台管理的 AppState 增加了管理员服务的连接。相应地,main()函数也需要增加对其的初始化

main() 初始化管理员服务客户端


#[tokio::main]
async fn main() {
    let addr = "0.0.0.0:59527";

    let cate = CategoryServiceClient::connect("http://127.0.0.1:19527")
        .await
        .unwrap();
    let topic = TopicServiceClient::connect("http://127.0.0.1:29527")
        .await
        .unwrap();
    let admin = AdminServiceClient::connect("http://127.0.0.1:49527")
        .await
        .unwrap();
    let tera = Tera::new("blog-backend/templates/**/*.html").unwrap();

    let m_router = Router::new().route("/cate", get(handler::list_cate)).route(
        "/cate/add",
        get(handler::add_cate_ui).post(handler::add_cate),
    );

    let app = Router::new()
        .nest("/m", m_router)
        .route("/", get(handler::index))
        .route("/login", get(handler::login_ui).post(handler::login))
        .route("/logout", get(handler::logout))
        .layer(Extension(Arc::new(model::AppState {
            tera,
            admin,
            cate,
            topic,
        })));

    axum::Server::bind(&addr.parse().unwrap())
        .serve(app.into_make_service())
        .await
        .unwrap();
}

登录状态和Cookie

为了维护登录状态,我们使用 Cookie

handler::logout 注销登录

pub async fn logout() -> Result<(StatusCode, HeaderMap), String> {
    Ok(redirect_with_cookie("/login", Some("axum_rs_token=")))
}

重定向

这两个函数取自axum-rs代码

/// 重定向
pub fn redirect(url: &str) -> (StatusCode, HeaderMap) {
    redirect_with_cookie(url, None)
}

/// 重定向
pub fn redirect_with_cookie(url: &str, cookie: Option<&str>) -> (StatusCode, HeaderMap) {
    let mut header = HeaderMap::new();
    header.insert(axum::http::header::LOCATION, url.parse().unwrap());
    if let Some(cookie) = cookie {
        header.insert(axum::http::header::SET_COOKIE, cookie.parse().unwrap());
    }
    (StatusCode::FOUND, header)
}

handler::list_cate 分类列表

handler::add_cate 添加分类

pub async fn add_cate(
    Extension(state): Extension<Arc<AppState>>,
    Form(frm): Form<form::AddCatetory>,
) -> Result<(StatusCode, HeaderMap), String> {
    let mut cate = state.cate.clone();
    let resp = cate
        .create_category(tonic::Request::new(blog_proto::CreateCategoryRequest {
            name: frm.name,
        }))
        .await
        .map_err(|err| err.to_string())?;
    let repl = resp.into_inner();
    let url = format!("/m/cate?msg=分类(ID为{})添加成功", repl.id);
    Ok(redirect(&url))
}

作业

本章的代码只实现登录、注册登录、分类列表和添加分类。请结合之前章节和源码里的导航菜单,将其余功能实现完整。

要查看完整内容,请先登录