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

View File

@ -9,7 +9,8 @@ use actix_web::http::HeaderName;
use actix_web::web::{Bytes, BytesMut}; use actix_web::web::{Bytes, BytesMut};
use actix_web::{web, HttpMessage, HttpRequest}; use actix_web::{web, HttpMessage, HttpRequest};
use async_trait::async_trait; use async_trait::async_trait;
use futures::StreamExt; use futures::future::LocalBoxFuture;
use futures::{FutureExt, StreamExt};
use std::convert::TryFrom; use std::convert::TryFrom;
/// Wrapper for [`HttpRequest`] that implements [`MessageDeserializer`] trait. /// Wrapper for [`HttpRequest`] that implements [`MessageDeserializer`] trait.
@ -112,6 +113,19 @@ pub async fn request_to_event(
.map_err(actix_web::error::ErrorBadRequest) .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()`]. /// 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. /// 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 crate::Event;
use actix_web::dev::HttpResponseBuilder; use actix_web::dev::HttpResponseBuilder;
use actix_web::http::{HeaderName, HeaderValue}; use actix_web::http::{HeaderName, HeaderValue, StatusCode};
use actix_web::HttpResponse; use actix_web::HttpResponse;
use async_trait::async_trait; use async_trait::async_trait;
use futures::future::LocalBoxFuture;
use futures::FutureExt;
use std::str::FromStr; use std::str::FromStr;
/// Wrapper for [`HttpResponseBuilder`] that implements [`StructuredSerializer`] and [`BinarySerializer`]. /// Wrapper for [`HttpResponseBuilder`] that implements [`StructuredSerializer`] and [`BinarySerializer`].
@ -76,6 +78,16 @@ pub async fn event_to_response(
.map_err(actix_web::error::ErrorBadRequest) .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()`]. /// 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. /// This trait is sealed and cannot be implemented for types outside of this crate.