Merge pull request #784 from dungeonfog/actix-cbor-support
Actix integration: cbor response support + error handling improvements
This commit is contained in:
commit
e5141b0f64
|
@ -24,7 +24,13 @@ serde_json = "1.0.64"
|
|||
serde_urlencoded = "0.7.0"
|
||||
futures-channel = "0.3.13"
|
||||
thiserror = "1.0.30"
|
||||
serde_cbor = { version = "0.11.2", optional = true }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
cbor = ["serde_cbor"]
|
||||
|
||||
[dev-dependencies]
|
||||
actix-rt = "2.2.0"
|
||||
async-mutex = "1.4.0"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use actix_http::body::BoxBody;
|
||||
use actix_web::error::JsonPayloadError;
|
||||
use std::future::Future;
|
||||
use std::io::{self, ErrorKind};
|
||||
use std::pin::Pin;
|
||||
|
@ -153,20 +154,62 @@ impl From<async_graphql::BatchResponse> for GraphQLResponse {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "cbor")]
|
||||
mod cbor {
|
||||
use actix_web::{http::StatusCode, ResponseError};
|
||||
use core::fmt;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Error(pub serde_cbor::Error);
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
impl ResponseError for Error {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Responder for GraphQLResponse {
|
||||
type Body = BoxBody;
|
||||
|
||||
fn respond_to(self, _req: &HttpRequest) -> HttpResponse {
|
||||
fn respond_to(self, req: &HttpRequest) -> HttpResponse {
|
||||
let mut res = HttpResponse::build(StatusCode::OK);
|
||||
res.content_type("application/json");
|
||||
if self.0.is_ok() {
|
||||
if let Some(cache_control) = self.0.cache_control().value() {
|
||||
res.append_header(("cache-control", cache_control));
|
||||
res.append_header((http::header::CACHE_CONTROL, cache_control));
|
||||
}
|
||||
}
|
||||
for (name, value) in self.0.http_headers() {
|
||||
res.append_header((name, value));
|
||||
}
|
||||
res.body(serde_json::to_string(&self.0).unwrap())
|
||||
let accept = req
|
||||
.headers()
|
||||
.get(http::header::ACCEPT)
|
||||
.and_then(|val| val.to_str().ok());
|
||||
let (ct, body) = match accept {
|
||||
// optional cbor support
|
||||
#[cfg(feature = "cbor")]
|
||||
// this avoids copy-pasting the mime type
|
||||
Some(ct @ "application/cbor") => (
|
||||
ct,
|
||||
match serde_cbor::to_vec(&self.0) {
|
||||
Ok(body) => body,
|
||||
Err(e) => return HttpResponse::from_error(cbor::Error(e)),
|
||||
},
|
||||
),
|
||||
_ => (
|
||||
"application/json",
|
||||
match serde_json::to_vec(&self.0) {
|
||||
Ok(body) => body,
|
||||
Err(e) => return HttpResponse::from_error(JsonPayloadError::Serialize(e)),
|
||||
},
|
||||
),
|
||||
};
|
||||
res.content_type(ct);
|
||||
res.body(body)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -220,3 +220,51 @@ async fn test_count() {
|
|||
.into_bytes()
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "cbor")]
|
||||
#[actix_rt::test]
|
||||
async fn test_cbor() {
|
||||
let srv = test::init_service(
|
||||
App::new()
|
||||
.app_data(Data::new(Schema::new(
|
||||
AddQueryRoot,
|
||||
EmptyMutation,
|
||||
EmptySubscription,
|
||||
)))
|
||||
.service(
|
||||
web::resource("/")
|
||||
.guard(guard::Post())
|
||||
.to(gql_handle_schema::<AddQueryRoot, EmptyMutation, EmptySubscription>),
|
||||
),
|
||||
)
|
||||
.await;
|
||||
let response = srv
|
||||
.call(
|
||||
test::TestRequest::with_uri("/")
|
||||
.method(Method::POST)
|
||||
.set_payload(r#"{"query":"{ add(a: 10, b: 20) }"}"#)
|
||||
.insert_header((actix_http::header::ACCEPT, "application/cbor"))
|
||||
.to_request(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(response.status().is_success());
|
||||
#[derive(Debug, serde::Deserialize, PartialEq)]
|
||||
struct Response {
|
||||
data: ResponseInner,
|
||||
}
|
||||
#[derive(Debug, serde::Deserialize, PartialEq)]
|
||||
struct ResponseInner {
|
||||
add: i32,
|
||||
}
|
||||
let body = actix_web::body::to_bytes(response.into_body())
|
||||
.await
|
||||
.unwrap();
|
||||
let response: Response = serde_cbor::from_slice(&body).unwrap();
|
||||
assert_eq!(
|
||||
response,
|
||||
Response {
|
||||
data: ResponseInner { add: 30 }
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user