This crate provides [sod::Service
] abstractions around [actix_web
] via [Handler
] implementations.
The [ServiceHandler
] acts as an [actix_web
] [Handler
], dispatching requests to an underlying
[sod::AsyncService
] or [sod::Service
] implementation.
The input to the underlying [AsyncService
] is directly compatible with the native [FromRequest
] trait
in [actix_web
]. As such, a tuple of [FromRequest
] impls can be handled as input for an [AsyncService
].
The output from the underlying [AsyncService
] must implement the native [Responder
] trait from [actix_web
].
This means that all output type from the service should be compatible with all output types from [actix_web
].
This should include a simple [String
] or full [actix_web::HttpResponse
].
The following example mirrors the default [actix_web
] greeter example, except it uses the service abstraction
provided by this library:
```rust use actixweb::{web, App, HttpServer}; use sod::Service; use sodactix_web::ServiceHandler;
async fn main() -> std::io::Result<()> {
struct GreetService;
impl Service
HttpServer::new(|| {
App::new().service(
web::resource("/greet/{name}").route(web::get().to(ServiceHandler::new(GreetService))),
)
})
.bind(("127.0.0.1", 8080))?
.run()
.await
} ```
The following example is slightly more advanced, demonstrating how [AsyncService
] and a typle of inputs may be used:
```rust use std::{io::Error, io::ErrorKind}; use actixweb::{web, App, HttpServer}; use serdederive::Deserialize; use sod::{asynctrait, AsyncService}; use sodactix_web::ServiceHandler;
async fn main() -> std::io::Result<()> { #[derive(Debug, Deserialize)] pub struct MathParams { a: i64, b: i64, }
struct MathService;
#[async_trait]
impl AsyncService<(web::Path<String>, web::Query<MathParams>)> for MathService {
type Output = String;
type Error = Error;
async fn process(
&self,
(func, params): (web::Path<String>, web::Query<MathParams>),
) -> Result<Self::Output, Self::Error> {
let value = match func.as_str() {
"add" => params.a + params.b,
"sub" => params.a - params.b,
"mul" => params.a * params.b,
"div" => params.a / params.b,
_ => return Err(Error::new(ErrorKind::Other, "invalid func")),
};
Ok(format!("{value}"))
}
}
HttpServer::new(|| {
App::new().service(
web::resource("/math/{func}").route(web::get().to(ServiceHandler::new(MathService))),
)
})
.bind(("127.0.0.1", 8080))?
.run()
.await
} ```
An [actix_web
] [Handler
] that instantiates [MutService
] impls to handle individual sessions.
The [WsSessionFactory
] is the entry point to this module. It is used to create session services
as connections are established by client, and acts as a [actix_web
] [Handler
].
The [WsSessionFactory
] encapsulates 2 functions to be defined be the user.
F: Fn(&HttpRequest) -> Result<S, Error> + 'static
- The factory that produces session services,
which can produce either an Ok([Service
]), Ok([MutService
]), or Err([Error
]).E: Fn(&mut S, S::Error)-> Result<(), S::Error>+ Unpin + 'static
- The error handler, which
is used as a callback to handle errors returned by your service implementation.Actix Wiring:
rust
web::resource("/echo").route(web::get().to(WsSessionFactory::new(
|_req| Ok(EchoService),
|_service, err| println!("ERROR: {err}"),
))),
The underlying session [MutService
] impls that are produced by the session service factory
must accept a [WsSessionEvent
] as input and produce a [Option<WsMessage>
] as output.
WsSessionEvent
] input alerts the session of session lifecycle events and received messages.Option<WsMessage>
] output can optionally send response payloads to a session.The [WsSessionFactory
] requires an Fn(&mut S, S::Error) -> Result<(), S::Error>
error handler
function to be provided by the user where S: MutService
or S: Service
. Since actix uses an
asynchronous thread-pool behind the scenes to handle websocket requests, [Service
] [Err
] results
are not able to bubble up outside of the underlying [StreamHandler
].
Instead of make assumptions about how a user wants to handle errors returned by a service, that is
left entirely up to the user via the error handler. When the error handler returns [Ok(())
], no action
will be taken against the underlying session. When the [Err
] is returned by the error handler, the
session will be closed.
A common error handler impl is to log the error and close the session:
rust
|_, err| {
log::error!("Session Error: {err}");
Err(err)
}
To produce messages to a session outside of input event handling, use the [WsSendService
]
provided by the initial [WsSessionEvent::Started
] event. You may take ownership of the
[WsSendService
] to asynchronously produce messages to the service outside of the session's
input handler service, which will return a [SendError
] once the session has been closed/shutdown.
Pong replies are automatically sent by this framework, so you may ignore Ping requests for the purpose of Ping/Pong responses.
```rust use std::convert::Infallible; use actixweb::{web, App, HttpServer}; use sod::MutService; use sodactix_web::ws::{WsMessage, WsSessionEvent, WsSessionFactory};
async fn main() -> std::io::Result<()> {
struct EchoService;
impl MutService
HttpServer::new(|| {
App::new().service(
web::resource("/echo").route(web::get().to(WsSessionFactory::new(
|_| Ok(EchoService),
|_, err| {
println!("ERROR: {err}");
Err(err)
},
))),
)
})
.bind(("127.0.0.1", 8080))?
.run()
.await
} ```