multiple fullstack nits/cleanups before release (#4852)
* feat: `:arg` syntax for serverfn * add better decode/encode verify message * allow catch-all query args * add rebind * implement websocket frames for `Streaming` * fix lazy init * wip... serverfnstate * wip... * wip: serverfns require Send/Sync, let's fix that * wip... * small cleanup * fix hang by actually spawning the tokio task * clippy * fix parameter catchall binding * fix lychee * doc tests
This commit is contained in:
1 parent
43373f0be7
commit
5559e463c8
27 files changed
+1395
-1097
No files matched your search
@@ -42,7 +42,7 @@ use crate::{
|
||||
use axum::response::IntoResponse;
|
||||
use axum_core::extract::{FromRequest, Request};
|
||||
use bytes::Bytes;
|
||||
use dioxus_fullstack_core::{DioxusServerState, RequestError};
|
||||
use dioxus_fullstack_core::RequestError;
|
||||
use http::StatusCode;
|
||||
use send_wrapper::SendWrapper;
|
||||
use serde::Serialize;
|
||||
@@ -172,7 +172,7 @@ pub mod req_to {
|
||||
impl<T, O, R> EncodeRequest<T, O, R> for &&&&&&&&&ServerFnEncoder<T, O>
|
||||
where
|
||||
T: 'static,
|
||||
O: FromRequest<DioxusServerState> + IntoRequest<R>,
|
||||
O: IntoRequest<R>,
|
||||
{
|
||||
type VerifyEncode = EncodeIsVerified;
|
||||
fn fetch_client(
|
||||
@@ -214,6 +214,8 @@ pub mod req_to {
|
||||
pub use decode_ok::*;
|
||||
mod decode_ok {
|
||||
|
||||
use crate::{CantDecode, DecodeIsVerified};
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Convert the reqwest response into the desired type, in place.
|
||||
@@ -222,13 +224,16 @@ mod decode_ok {
|
||||
/// This is because FromResponse types are more specialized and can handle things like websockets and files.
|
||||
/// DeserializeOwned types are more general and can handle things like JSON responses.
|
||||
pub trait RequestDecodeResult<T, R> {
|
||||
type VerifyDecode;
|
||||
fn decode_client_response(
|
||||
&self,
|
||||
res: Result<R, RequestError>,
|
||||
) -> impl Future<Output = Result<Result<T, ServerFnError>, RequestError>> + Send;
|
||||
fn verify_can_deserialize(&self) -> Self::VerifyDecode;
|
||||
}
|
||||
|
||||
impl<T: FromResponse<R>, E, R> RequestDecodeResult<T, R> for &&&ServerFnDecoder<Result<T, E>> {
|
||||
type VerifyDecode = DecodeIsVerified;
|
||||
fn decode_client_response(
|
||||
&self,
|
||||
res: Result<R, RequestError>,
|
||||
@@ -240,11 +245,15 @@ mod decode_ok {
|
||||
}
|
||||
})
|
||||
}
|
||||
fn verify_can_deserialize(&self) -> Self::VerifyDecode {
|
||||
DecodeIsVerified
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: DeserializeOwned, E> RequestDecodeResult<T, ClientResponse>
|
||||
for &&ServerFnDecoder<Result<T, E>>
|
||||
{
|
||||
type VerifyDecode = DecodeIsVerified;
|
||||
fn decode_client_response(
|
||||
&self,
|
||||
res: Result<ClientResponse, RequestError>,
|
||||
@@ -304,6 +313,24 @@ mod decode_ok {
|
||||
}
|
||||
})
|
||||
}
|
||||
fn verify_can_deserialize(&self) -> Self::VerifyDecode {
|
||||
DecodeIsVerified
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, R, E> RequestDecodeResult<T, R> for &ServerFnDecoder<Result<T, E>> {
|
||||
type VerifyDecode = CantDecode;
|
||||
|
||||
fn decode_client_response(
|
||||
&self,
|
||||
_res: Result<R, RequestError>,
|
||||
) -> impl Future<Output = Result<Result<T, ServerFnError>, RequestError>> + Send {
|
||||
async move { unimplemented!() }
|
||||
}
|
||||
|
||||
fn verify_can_deserialize(&self) -> Self::VerifyDecode {
|
||||
CantDecode
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RequestDecodeErr<T, E> {
|
||||
@@ -460,33 +487,34 @@ pub use req_from::*;
|
||||
pub mod req_from {
|
||||
use super::*;
|
||||
use axum::{extract::FromRequestParts, response::Response};
|
||||
use dioxus_fullstack_core::FullstackContext;
|
||||
|
||||
pub trait ExtractRequest<In, Out, H, M = ()> {
|
||||
fn extract_axum(
|
||||
&self,
|
||||
state: DioxusServerState,
|
||||
state: FullstackContext,
|
||||
request: Request,
|
||||
map: fn(In) -> Out,
|
||||
) -> impl Future<Output = Result<(H, Out), Response>> + 'static;
|
||||
) -> impl Future<Output = Result<(Out, H), Response>> + 'static;
|
||||
}
|
||||
|
||||
/// When you're extracting entirely on the server, we need to reject client-consuning request bodies
|
||||
/// This sits above priority in the combined headers on server / body on client case.
|
||||
impl<In, M, H> ExtractRequest<In, (), H, M> for &&&&&&&&&&&ServerFnEncoder<In, ()>
|
||||
where
|
||||
H: FromRequest<DioxusServerState, M> + 'static,
|
||||
H: FromRequest<FullstackContext, M> + 'static,
|
||||
{
|
||||
fn extract_axum(
|
||||
&self,
|
||||
state: DioxusServerState,
|
||||
state: FullstackContext,
|
||||
request: Request,
|
||||
_map: fn(In) -> (),
|
||||
) -> impl Future<Output = Result<(H, ()), Response>> + 'static {
|
||||
) -> impl Future<Output = Result<((), H), Response>> + 'static {
|
||||
async move {
|
||||
H::from_request(request, &state)
|
||||
.await
|
||||
.map_err(|e| e.into_response())
|
||||
.map(|out| (out, ()))
|
||||
.map(|out| ((), out))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -496,14 +524,14 @@ pub mod req_from {
|
||||
where
|
||||
In: DeserializeOwned + 'static,
|
||||
Out: 'static,
|
||||
H: FromRequestParts<DioxusServerState>,
|
||||
H: FromRequestParts<FullstackContext>,
|
||||
{
|
||||
fn extract_axum(
|
||||
&self,
|
||||
_state: DioxusServerState,
|
||||
_state: FullstackContext,
|
||||
request: Request,
|
||||
map: fn(In) -> Out,
|
||||
) -> impl Future<Output = Result<(H, Out), Response>> + 'static {
|
||||
) -> impl Future<Output = Result<(Out, H), Response>> + 'static {
|
||||
async move {
|
||||
let (mut parts, body) = request.into_parts();
|
||||
let headers = H::from_request_parts(&mut parts, &_state)
|
||||
@@ -525,7 +553,7 @@ pub mod req_from {
|
||||
.map_err(|e| ServerFnError::from(e).into_response())
|
||||
.unwrap();
|
||||
|
||||
Ok((headers, out))
|
||||
Ok((out, headers))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -533,15 +561,15 @@ pub mod req_from {
|
||||
/// We skip the BodySerialize wrapper and just go for the output type directly.
|
||||
impl<In, Out, M, H> ExtractRequest<In, Out, H, M> for &&&&&&&&&ServerFnEncoder<In, Out>
|
||||
where
|
||||
Out: FromRequest<DioxusServerState, M> + 'static,
|
||||
H: FromRequestParts<DioxusServerState>,
|
||||
Out: FromRequest<FullstackContext, M> + 'static,
|
||||
H: FromRequestParts<FullstackContext>,
|
||||
{
|
||||
fn extract_axum(
|
||||
&self,
|
||||
state: DioxusServerState,
|
||||
state: FullstackContext,
|
||||
request: Request,
|
||||
_map: fn(In) -> Out,
|
||||
) -> impl Future<Output = Result<(H, Out), Response>> + 'static {
|
||||
) -> impl Future<Output = Result<(Out, H), Response>> + 'static {
|
||||
async move {
|
||||
let (mut parts, body) = request.into_parts();
|
||||
let headers = H::from_request_parts(&mut parts, &state)
|
||||
@@ -554,7 +582,7 @@ pub mod req_from {
|
||||
.await
|
||||
.map_err(|e| e.into_response());
|
||||
|
||||
res.map(|out| (headers, out))
|
||||
res.map(|out| (out, headers))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -616,7 +644,7 @@ mod resp {
|
||||
|
||||
#[allow(clippy::result_large_err)]
|
||||
pub trait MakeAxumError<E> {
|
||||
fn make_axum_error(self, result: Result<Response, E>) -> Result<Response, Response>;
|
||||
fn make_axum_error(self, result: Result<Response, E>) -> Response;
|
||||
}
|
||||
|
||||
/// Get the status code from the error type if possible.
|
||||
@@ -639,9 +667,9 @@ mod resp {
|
||||
where
|
||||
E: AsStatusCode + From<ServerFnError> + Serialize + DeserializeOwned + Display,
|
||||
{
|
||||
fn make_axum_error(self, result: Result<Response, E>) -> Result<Response, Response> {
|
||||
fn make_axum_error(self, result: Result<Response, E>) -> Response {
|
||||
match result {
|
||||
Ok(res) => Ok(res),
|
||||
Ok(res) => res,
|
||||
Err(err) => {
|
||||
let status_code = err.as_status_code();
|
||||
let err = ErrorPayload {
|
||||
@@ -656,19 +684,16 @@ mod resp {
|
||||
HeaderValue::from_static("application/json"),
|
||||
);
|
||||
*resp.status_mut() = status_code;
|
||||
Err(resp)
|
||||
resp
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> MakeAxumError<CapturedError> for &&ServerFnDecoder<Result<T, CapturedError>> {
|
||||
fn make_axum_error(
|
||||
self,
|
||||
result: Result<Response, CapturedError>,
|
||||
) -> Result<Response, Response> {
|
||||
fn make_axum_error(self, result: Result<Response, CapturedError>) -> Response {
|
||||
match result {
|
||||
Ok(res) => Ok(res),
|
||||
Ok(res) => res,
|
||||
|
||||
// Optimize the case where we have sole ownership of the error
|
||||
Err(errr) if errr._strong_count() == 1 => {
|
||||
@@ -721,19 +746,16 @@ mod resp {
|
||||
HeaderValue::from_static("application/json"),
|
||||
);
|
||||
*resp.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
|
||||
Err(resp)
|
||||
resp
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> MakeAxumError<anyhow::Error> for &&ServerFnDecoder<Result<T, anyhow::Error>> {
|
||||
fn make_axum_error(
|
||||
self,
|
||||
result: Result<Response, anyhow::Error>,
|
||||
) -> Result<Response, Response> {
|
||||
fn make_axum_error(self, result: Result<Response, anyhow::Error>) -> Response {
|
||||
match result {
|
||||
Ok(res) => Ok(res),
|
||||
Ok(res) => res,
|
||||
Err(errr) => {
|
||||
// The `WithHttpError` trait emits ServerFnErrors so we can downcast them here
|
||||
// to create richer responses.
|
||||
@@ -775,19 +797,16 @@ mod resp {
|
||||
HeaderValue::from_static("application/json"),
|
||||
);
|
||||
*resp.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
|
||||
Err(resp)
|
||||
resp
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> MakeAxumError<StatusCode> for &&ServerFnDecoder<Result<T, StatusCode>> {
|
||||
fn make_axum_error(
|
||||
self,
|
||||
result: Result<Response, StatusCode>,
|
||||
) -> Result<Response, Response> {
|
||||
fn make_axum_error(self, result: Result<Response, StatusCode>) -> Response {
|
||||
match result {
|
||||
Ok(resp) => Ok(resp),
|
||||
Ok(resp) => resp,
|
||||
Err(status) => {
|
||||
let body = serde_json::to_string(&ErrorPayload::<()> {
|
||||
code: status.as_u16(),
|
||||
@@ -801,19 +820,16 @@ mod resp {
|
||||
HeaderValue::from_static("application/json"),
|
||||
);
|
||||
*resp.status_mut() = status;
|
||||
Err(resp)
|
||||
resp
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> MakeAxumError<HttpError> for &ServerFnDecoder<Result<T, HttpError>> {
|
||||
fn make_axum_error(
|
||||
self,
|
||||
result: Result<Response, HttpError>,
|
||||
) -> Result<Response, Response> {
|
||||
fn make_axum_error(self, result: Result<Response, HttpError>) -> Response {
|
||||
match result {
|
||||
Ok(resp) => Ok(resp),
|
||||
Ok(resp) => resp,
|
||||
Err(http_err) => {
|
||||
let body = serde_json::to_string(&ErrorPayload::<()> {
|
||||
code: http_err.status.as_u16(),
|
||||
@@ -829,7 +845,7 @@ mod resp {
|
||||
HeaderValue::from_static("application/json"),
|
||||
);
|
||||
*resp.status_mut() = http_err.status;
|
||||
Err(resp)
|
||||
resp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user