Implement actix-web FromRequest and Responder

Fixes #130

I'm not entirely sure why this works, but the compiler seems to like
it! :D

The example is intentionally as simple as it gets, but a "real" app
should probably return Result<Event, Error> from its handlers.

Signed-off-by: Jim Crossley <jim@crossleys.org>
This commit is contained in:
Jim Crossley 2021-06-25 13:54:50 -04:00
parent bf21c52869
commit d45811604d
3 changed files with 41 additions and 21 deletions

View File

@ -1,31 +1,25 @@
use actix_web::{get, post, web, App, HttpRequest, HttpResponse, HttpServer};
use cloudevents::actix::{HttpRequestExt, HttpResponseBuilderExt};
use cloudevents::{EventBuilder, EventBuilderV10};
use actix_web::{get, post, App, HttpServer};
use cloudevents::{Event, EventBuilder, EventBuilderV10};
use serde_json::json;
#[post("/")]
async fn post_event(req: HttpRequest, payload: web::Payload) -> Result<String, actix_web::Error> {
let event = req.to_event(payload).await?;
async fn post_event(event: Event) -> Event {
println!("Received Event: {:?}", event);
Ok(format!("{:?}", event))
event
}
#[get("/")]
async fn get_event() -> Result<HttpResponse, actix_web::Error> {
async fn get_event() -> Event {
let payload = json!({"hello": "world"});
Ok(HttpResponse::Ok()
.event(
EventBuilderV10::new()
.id("0001")
.ty("example.test")
.source("http://localhost/")
.data("application/json", payload)
.extension("someint", "10")
.build()
.unwrap(),
)
.await?)
EventBuilderV10::new()
.id("0001")
.ty("example.test")
.source("http://localhost/")
.data("application/json", payload)
.extension("someint", "10")
.build()
.unwrap()
}
#[actix_web::main]

View File

@ -9,7 +9,8 @@ use actix_web::http::HeaderName;
use actix_web::web::{Bytes, BytesMut};
use actix_web::{web, HttpMessage, HttpRequest};
use async_trait::async_trait;
use futures::StreamExt;
use futures::future::LocalBoxFuture;
use futures::{FutureExt, StreamExt};
use std::convert::TryFrom;
/// Wrapper for [`HttpRequest`] that implements [`MessageDeserializer`] trait.
@ -112,6 +113,19 @@ pub async fn request_to_event(
.map_err(actix_web::error::ErrorBadRequest)
}
/// So that an actix-web handler may take an Event parameter
impl actix_web::FromRequest for Event {
type Config = ();
type Error = actix_web::Error;
type Future = LocalBoxFuture<'static, std::result::Result<Self, Self::Error>>;
fn from_request(r: &HttpRequest, p: &mut actix_web::dev::Payload) -> Self::Future {
let payload = web::Payload(p.take());
let request = r.to_owned();
async move { request_to_event(&request, payload).await }.boxed_local()
}
}
/// Extention Trait for [`HttpRequest`] which acts as a wrapper for the function [`request_to_event()`].
///
/// This trait is sealed and cannot be implemented for types outside of this crate.

View File

@ -5,9 +5,11 @@ use crate::message::{
};
use crate::Event;
use actix_web::dev::HttpResponseBuilder;
use actix_web::http::{HeaderName, HeaderValue};
use actix_web::http::{HeaderName, HeaderValue, StatusCode};
use actix_web::HttpResponse;
use async_trait::async_trait;
use futures::future::LocalBoxFuture;
use futures::FutureExt;
use std::str::FromStr;
/// Wrapper for [`HttpResponseBuilder`] that implements [`StructuredSerializer`] and [`BinarySerializer`].
@ -76,6 +78,16 @@ pub async fn event_to_response(
.map_err(actix_web::error::ErrorBadRequest)
}
/// So that an actix-web handler may return an Event
impl actix_web::Responder for Event {
type Error = actix_web::Error;
type Future = LocalBoxFuture<'static, std::result::Result<HttpResponse, Self::Error>>;
fn respond_to(self, _: &actix_web::HttpRequest) -> Self::Future {
async { HttpResponse::build(StatusCode::OK).event(self).await }.boxed_local()
}
}
/// Extension Trait for [`HttpResponseBuilder`] which acts as a wrapper for the function [`event_to_response()`].
///
/// This trait is sealed and cannot be implemented for types outside of this crate.