Rust Web Services
Routing
psinode
passes most HTTP requests to the SystemService::HttpServer service, which then routes requests to the appropriate service's serveSys action (see diagram). The services run in RPC mode; this prevents them from writing to the database, but allows them to read data they normally can't. See psibase::DbId.
SystemService::CommonApi provides services common to all domains under the /common
tree. It also serves the chain's main page.
SystemService::Sites provides web hosting for non-service accounts or service accounts that did not register for HTTP handling.
psinode
directly handles requests which start with /native
, e.g. /native/push_transaction
. Services don't serve these.
Registration
Services which wish to serve HTTP requests need to register using the SystemService::HttpServer service's SystemService::HttpServer::registerServer action. There are multiple ways to do this:
psibase deploy
andcargo psibase deploy
have a--register-proxy
option (shortcut-p
) that can do this while deploying the service.psibase register-proxy
can also do it. TODO: implementpsibase register-proxy
.- A service may call
registerServer
during its own initialization action.
A service doesn't have to serve HTTP requests itself; it may delegate this to another service during registration.
HTTP Interfaces
Services which serve HTTP implement these interfaces:
- psibase::server_interface (required)
- psibase::storage_interface (optional)
Helpers
These help implement basic functionality:
- psibase::serve_simple_ui
- psibase::serve_simple_index
- psibase::serve_action_templates
- psibase::serve_schema
- psibase::serve_pack_action
Here's a common pattern for using these functions.
#[psibase::service]
defines Wrapper
;
the serve_*
functions fetch action definitions from Wrapper.
#[psibase::service]
#[allow(non_snake_case)]
mod service {
use psibase::*;
#[action]
fn serveSys(request: HttpRequest) -> Option<HttpReply> {
if request.method == "GET"
&& (request.target == "/" || request.target == "/index.html")
{
return Some(HttpReply {
contentType: "text/html".into(),
body: "<b>This is my UI</b>".into(),
headers: vec![],
});
}
None.or_else(|| serve_schema::<Wrapper>(&request))
.or_else(|| serve_action_templates::<Wrapper>(&request))
.or_else(|| serve_pack_action::<Wrapper>(&request))
}
}