Token导航 LogoToken导航TokenDH.com
开发操作浏览器github未标认证来源可访问许可证需确认审计通过

salvo-caching齐射缓存

Agent Skill

salvo-caching 用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要围绕仓库状态、代码变更或协作事项进行整理时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

总安装

279

周安装

12

GitHub Stars

16

下载量

98
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

unknown

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:salvo-caching(齐射缓存)
来源仓库:https://github.com/salvo-rs/salvo-skills
仓库路径:skills/salvo-caching
安装命令:
npx skills add https://github.com/salvo-rs/salvo-skills --skill salvo-caching
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/salvo-rs/salvo-skills --skill salvo-caching

简介

用于处理 GitHub 仓库、Issue、Pull Request 和代码协作信息。

  • 适合围绕仓库状态、代码变更或协作事项进行整理。
  • 可结合来源仓库和原始 README 继续核验具体用法。
  • 安装前建议确认权限范围和维护状态。salvo-caching 属于开发类 Skill,可作为该场景下的辅助能力补充。
  • 注意是否会触发联网、命令执行或文件读写。

SKILL.md

Salvo Caching Strategies

[dependencies]
salvo = { version = "0.89.3", features = ["cache", "caching-headers"] }

The cache feature activates salvo-cache with the default moka-store backend.

Response cache middleware

salvo::cache::Cache caches full responses (status, headers, body). By default it caches GET only and skips streaming responses and error bodies.

use std::time::Duration;
use salvo::cache::{Cache, MokaStore, RequestIssuer};
use salvo::prelude::*;

#[handler]
async fn expensive() -> String {
    format!("computed at {}", chrono::Utc::now())
}

#[tokio::main]
async fn main() {
    let cache = Cache::new(
        MokaStore::builder()
            .time_to_live(Duration::from_secs(60))
            .max_capacity(10_000)
            .build(),
        RequestIssuer::default(),
    );

    let router = Router::new().hoop(cache).get(expensive);
    let acceptor = TcpListener::new("0.0.0.0:8080").bind().await;
    Server::new(acceptor).serve(router).await;
}

Cache::new(store, issuer) is the only constructor. Tunable:

  • .skipper(impl Skipper) — default is MethodSkipper::new().skip_all().skip_get(false) (GET only). Pass a closure |req, depot| bool to override.

RequestIssuer

Builds cache keys from the request URI and method. Toggle parts:

let issuer = RequestIssuer::new()
    .use_scheme(false)
    .use_authority(false)
    .use_path(true)
    .use_query(true)
    .use_method(true);

All five are true by default.

Custom CacheIssuer

Implement CacheIssuer (or pass a closure) to vary cache keys by user, tenant, etc.:

use salvo::cache::CacheIssuer;

let issuer = |req: &mut Request, depot: &Depot| -> Option<String> {
    let user_id = depot.get::<String>("user_id").ok()?;
    Some(format!("{user_id}:{}", req.uri().path()))
};
let cache = Cache::new(store, issuer);

Returning None disables caching for that request.

CachingHeaders (ETag + Last-Modified)

salvo::caching_headers::CachingHeaders adds ETag and Last-Modified handling to downstream handlers, responding 304 when If-None-Match / If-Modified-Since match:

use salvo::caching_headers::CachingHeaders;

let router = Router::new()
    .hoop(CachingHeaders::new())
    .get(handler);

For ETag-only or Last-Modified-only, use ETag::new() or Modified::new() from the same module.

HTTP cache headers manually

#[handler]
async fn cached(res: &mut Response) -> &'static str {
    res.headers_mut().insert(
        "cache-control",
        "public, max-age=3600, stale-while-revalidate=86400".parse().unwrap(),
    );
    "hello"
}

Directives cheat sheet:

  • public, max-age=N — shared caches may store for N seconds
  • private, max-age=N — browser only
  • no-store — never cache
  • no-cache — must revalidate with origin
  • stale-while-revalidate=N — serve stale for N seconds while revalidating

Manual 304 response

StatusError has no not_modified(). Set the status directly:

#[handler]
async fn with_etag(req: &mut Request, res: &mut Response) {
    let etag = compute_etag();
    if req.header::<String>("if-none-match").as_deref() == Some(etag.as_str()) {
        res.status_code(StatusCode::NOT_MODIFIED);
        return;
    }
    res.headers_mut().insert("etag", etag.parse().unwrap());
    res.render(Json(load_data().await));
}

Data-layer caching with Moka

For caching values inside handlers (not full responses), use moka::future::Cache directly and share via affix_state:

moka = { version = "0.12", features = ["future"] }
use moka::future::Cache as MokaCache;
use std::sync::Arc;

type UserCache = Arc<MokaCache<i64, User>>;

#[handler]
async fn get_user(req: &mut Request, depot: &mut Depot) -> Result<Json<User>, StatusError> {
    let id = req.param::<i64>("id").ok_or_else(StatusError::bad_request)?;
    let cache = depot.obtain::<UserCache>().unwrap();
    let pool = depot.obtain::<PgPool>().unwrap();

    if let Some(user) = cache.get(&id).await { return Ok(Json(user)); }

    let user = sqlx::query_as::<_, User>("select id, name from users where id = $1")
        .bind(id).fetch_optional(pool).await
        .map_err(|_| StatusError::internal_server_error())?
        .ok_or_else(StatusError::not_found)?;

    cache.insert(id, user.clone()).await;
    Ok(Json(user))
}

#[tokio::main]
async fn main() {
    let cache: UserCache = Arc::new(
        MokaCache::builder()
            .max_capacity(1_000)
            .time_to_live(std::time::Duration::from_secs(60))
            .build(),
    );
    let router = Router::new()
        .hoop(affix_state::inject(cache))
        .push(Router::with_path("users/{id}").get(get_user));
    // ...
}

Invalidate on write:

#[handler]
async fn update_user(req: &mut Request, depot: &mut Depot) -> StatusCode {
    let id = req.param::<i64>("id").unwrap();
    // update db...
    depot.obtain::<UserCache>().unwrap().invalidate(&id).await;
    StatusCode::OK
}

invalidate_all() clears everything; iterate over IDs for bulk invalidation.

Gotchas

  • Cache is full-response. It needs a deterministic key — beware caching per-user data under a shared RequestIssuer; use a custom issuer keyed on user ID.
  • Streaming responses (ResBody::Stream) and error bodies are never cached, silently.
  • Cache::new does not return a builder — configure the store via MokaStore::builder().
  • Method names: MokaStore::builder() (not Cache::builder), RequestIssuer::default() or new().
  • StatusError::not_modified() does NOT exist — set StatusCode::NOT_MODIFIED directly.

Related Skills

  • salvo-compression: Compress before Cache so the stored body is already gzipped.
  • salvo-static-files: serve-static sets ETag/Last-Modified automatically.
  • salvo-database: Cache hot query results at the data layer.

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Codex

34.35%
按下载量换算34

Claude

27.3%
按下载量换算27

Cursor

19%
按下载量换算19

Gemini CLI

9.5%
按下载量换算9

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

操作浏览器

该 Skill 可能涉及浏览器控制能力,使用时可能读取或操作网页内容,需要在受控环境中确认权限边界。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills