Actix Cloud is an all-in-one web framework based on Actix Web.
Actix Cloud is highly configurable. You can only enable needed features, implement your own feature backend or even use other libraries.
- logger (Default: Enable)
- i18n (Default: Disable)
- security (Embedded)
- memorydb (Default: Disable)
- auth (Embedded)
- session (Default: Disable)
- config (Default: Disable)
- config-json
- config-yaml
- config-toml
- request (Embedded)
- response (Default: Disable)
- response-json
- traceid (Default: Disable)
- seaorm (Default: Disable)
- csrf (Default: Disable)
You can refer to Hello world example for basic usage.
Since application configuration can be quite dynamic, you need to build on your own. Here are some useful middlewares:
App::new()
.wrap(middleware::Compress::default()) // compress page
.wrap(SecurityHeader::default().build()) // default security header
.wrap(SessionMiddleware::builder(memorydb.clone(), Key::generate()).build()) // session
...
.app_data(state_cloned.clone())
We use tracing as our logger library. It is thread safe. You can use it everywhere.
Start logger:
LoggerBuilder::new().level(Level::DEBUG).start() // colorful output
LoggerBuilder::new().json().start() // json output
You can also customize the logger with filter
, transformer
, etc.
Reinit logger (e.g., in plugins), or manually send logs:
logger.init(LoggerBuilder::new());
logger.sender().send(...);
Reserved field:
_time
: timestamp in microseconds, override the log timestamp.
We use rust-i18n-support
from rust-i18n as our i18n core.
Load locale:
let locale = Locale::new("en-US").add_locale(i18n!("locale"));
Translate:
t!(locale, "hello.world")
t!(locale, "hello.name", name = "MEME")
See examples for more usage.
Middleware to add security headers:
app.wrap(SecurityHeader::default().build())
Default header:
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Cross-Origin-Opener-Policy: same-origin
Content-Security-Policy: default-src 'none'; script-src 'none'; object-src 'none'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'
Enable HSTS when using HTTPS:
security_header.set_default_hsts();
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Actix Cloud has a default memory database backend used for sessions. You can also use your own backend if you implement actix_cloud::memorydb::MemoryDB
.
Note: the default backend does not have memory limitation, DDoS is possible if gateway rate limiting is not implemented
DefaultBackend::new()
Redis can be used as another backend for memory database.
RedisBackend::new("redis://user:pass@127.0.0.1:6379/0").await.unwrap(),
Authentication is quite simple, you only need to implement a checker.
Checker is used to check the permission, the server will return 403 if the return value is false:
struct AuthChecker {
need_admin: bool,
}
impl AuthChecker {
fn new(need_admin: bool) -> Self {
Self { need_admin }
}
}
#[async_trait(?Send)]
impl Checker for AuthChecker {
async fn check(&self, req: &mut ServiceRequest) -> Result<bool> {
let qs = QString::from(req.query_string());
let is_admin = if qs.get("admin").is_some_and(|x| x == "1") {
true
} else {
false
};
if (is_admin && self.need_admin) || !self.need_admin {
Ok(true)
} else {
Ok(false)
}
}
}
Then build the Router
and configure in the App using build_router
:
app.service(scope("/api").configure(build_router(...)))
Most features and usages are based on actix-session. Except for these:
- MemoryDB is the only supported storage.
- Error uses
actix-cloud::error::Error
. - You can set
_ttl
in the session to override the TTL of the session. - You can set
_id
in the session for reverse search.- Quote(") will be trimmed.
- Another key will be set in memorydb:
{_id}_{session_key}
. You can usekeys
function to find all session key binding to a specific id.
app.wrap(SessionMiddleware::builder(memorydb.clone(), Key::generate()).build())
config-rs is the underlying library.
Supported features:
- config-json: Support for JSON files.
- config-yaml: Support for YAML files.
- config-toml: Support for TOML files.
Provide per-request extension.
Built-in middleware:
- Store in extensions.
- If
i18n
feature is enabled, language is identified through the callback, orlocale.default
inGlobalState
.
Enable built-in middleware:
app.wrap(request::Middleware::new())
Usage:
async fn handler(req: HttpRequest) -> impl Responder {
let ext = req.extensions();
let ext = ext.get::<Arc<actix_cloud::request::Extension>>().unwrap();
...
}
async fn handler(ext: ReqData<Arc<actix_cloud::request::Extension>>) -> impl Responder {
...
}
Provide useful response type.
If i18n
feature is enabled, response message will be translated automatically.
If response-json
feature is enabled, response message will be converted to JSON automatically.
- Create response yml files.
- Use
build.rs
to generate source files. - Use
include!
to include generated files.
See examples for detailed usage.
Add trace ID for each request based on tracing-actix-web.
app.wrap(request::Middleware::new())
.wrap(TracingLogger::default()) // This should be after request::Middleware
If you enable request
feature, make sure it is before TracingLogger
since the trace_id
field is based on it.
Provide useful macros for seaorm.
#[derive(...)]
#[sea_orm(...)]
pub struct Model {
#[sea_orm(primary_key, auto_increment = false)]
pub id: Uuid,
pub created_at: i64,
pub updated_at: i64,
}
#[entity_id(Uuid::new_v4())] // generate new for `id` field.
#[entity_timestamp] // automatically handle `created_at` and `updated_at` field.
impl ActiveModel {}
#[entity_behavior] // enable `entity_id` and `entity_timestamp`.
impl ActiveModelBehavior for ActiveModel {}
We use double submit to protect against CSRF attacks.
You can use memorydb
to store and check CSRF tokens.
By default, CSRF checker is applied to:
- All unsafe methods unless
CSRFType
isDisabled
. - All methods if
CSRFType
isForceHeader
orForceParam
.
Generally, Param
and ForceParam
type should only be used for websocket.
build_router(
route,
csrf::Middleware::new(
String::from("CSRF_TOKEN"), // csrf cookie
String::from("X-CSRF-Token"), // csrf header/param
|req, token| Box::pin(async { Ok(true) }) // csrf checker
),
);
This project is licensed under the MIT license.