mirror of
https://github.com/tokio-rs/axum.git
synced 2024-12-28 07:20:12 +01:00
Add axum_extra::json!
(#2962)
This commit is contained in:
parent
64e6edac05
commit
4b4dac4d86
5 changed files with 91 additions and 2 deletions
|
@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
|
||||||
The format is based on [Keep a Changelog],
|
The format is based on [Keep a Changelog],
|
||||||
and this project adheres to [Semantic Versioning].
|
and this project adheres to [Semantic Versioning].
|
||||||
|
|
||||||
|
# Unreleased
|
||||||
|
|
||||||
|
- **added:** Add `json!` for easy construction of JSON responses ([#2962])
|
||||||
|
|
||||||
|
[#2962]: https://github.com/tokio-rs/axum/pull/2962
|
||||||
|
|
||||||
# 0.10.0
|
# 0.10.0
|
||||||
|
|
||||||
# alpha.1
|
# alpha.1
|
||||||
|
|
|
@ -20,7 +20,7 @@ cookie = ["dep:cookie"]
|
||||||
cookie-private = ["cookie", "cookie?/private"]
|
cookie-private = ["cookie", "cookie?/private"]
|
||||||
cookie-signed = ["cookie", "cookie?/signed"]
|
cookie-signed = ["cookie", "cookie?/signed"]
|
||||||
cookie-key-expansion = ["cookie", "cookie?/key-expansion"]
|
cookie-key-expansion = ["cookie", "cookie?/key-expansion"]
|
||||||
erased-json = ["dep:serde_json"]
|
erased-json = ["dep:serde_json", "dep:typed-json"]
|
||||||
form = ["dep:serde_html_form"]
|
form = ["dep:serde_html_form"]
|
||||||
json-deserializer = ["dep:serde_json", "dep:serde_path_to_error"]
|
json-deserializer = ["dep:serde_json", "dep:serde_path_to_error"]
|
||||||
json-lines = [
|
json-lines = [
|
||||||
|
@ -69,9 +69,11 @@ tokio = { version = "1.19", optional = true }
|
||||||
tokio-stream = { version = "0.1.9", optional = true }
|
tokio-stream = { version = "0.1.9", optional = true }
|
||||||
tokio-util = { version = "0.7", optional = true }
|
tokio-util = { version = "0.7", optional = true }
|
||||||
tracing = { version = "0.1.37", default-features = false, optional = true }
|
tracing = { version = "0.1.37", default-features = false, optional = true }
|
||||||
|
typed-json = { version = "0.1.1", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
axum = { path = "../axum" }
|
axum = { path = "../axum", features = ["macros"] }
|
||||||
|
axum-macros = { path = "../axum-macros", features = ["__private"] }
|
||||||
hyper = "1.0.0"
|
hyper = "1.0.0"
|
||||||
reqwest = { version = "0.12", default-features = false, features = ["json", "stream", "multipart"] }
|
reqwest = { version = "0.12", default-features = false, features = ["json", "stream", "multipart"] }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
|
|
@ -12,6 +12,15 @@ use serde::Serialize;
|
||||||
/// This allows returning a borrowing type from a handler, or returning different response
|
/// This allows returning a borrowing type from a handler, or returning different response
|
||||||
/// types as JSON from different branches inside a handler.
|
/// types as JSON from different branches inside a handler.
|
||||||
///
|
///
|
||||||
|
/// Like [`axum::Json`],
|
||||||
|
/// if the [`Serialize`] implementation fails
|
||||||
|
/// or if a map with non-string keys is used,
|
||||||
|
/// a 500 response will be issued
|
||||||
|
/// whose body is the error message in UTF-8.
|
||||||
|
///
|
||||||
|
/// This can be constructed using [`new`](ErasedJson::new)
|
||||||
|
/// or the [`json!`](crate::json) macro.
|
||||||
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
|
@ -72,3 +81,65 @@ impl IntoResponse for ErasedJson {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Construct an [`ErasedJson`] response from a JSON literal.
|
||||||
|
///
|
||||||
|
/// A `Content-Type: application/json` header is automatically added.
|
||||||
|
/// Any variable or expression implementing [`Serialize`]
|
||||||
|
/// can be interpolated as a value in the literal.
|
||||||
|
/// If the [`Serialize`] implementation fails,
|
||||||
|
/// or if a map with non-string keys is used,
|
||||||
|
/// a 500 response will be issued
|
||||||
|
/// whose body is the error message in UTF-8.
|
||||||
|
///
|
||||||
|
/// Internally,
|
||||||
|
/// this function uses the [`typed_json::json!`] macro,
|
||||||
|
/// allowing it to perform far fewer allocations
|
||||||
|
/// than a dynamic macro like [`serde_json::json!`] would –
|
||||||
|
/// it's equivalent to if you had just written
|
||||||
|
/// `derive(Serialize)` on a struct.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use axum::{
|
||||||
|
/// Router,
|
||||||
|
/// extract::Path,
|
||||||
|
/// response::Response,
|
||||||
|
/// routing::get,
|
||||||
|
/// };
|
||||||
|
/// use axum_extra::response::ErasedJson;
|
||||||
|
///
|
||||||
|
/// async fn get_user(Path(user_id) : Path<u64>) -> ErasedJson {
|
||||||
|
/// let user_name = find_user_name(user_id).await;
|
||||||
|
/// axum_extra::json!({ "name": user_name })
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// async fn find_user_name(user_id: u64) -> String {
|
||||||
|
/// // ...
|
||||||
|
/// # unimplemented!()
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// let app = Router::new().route("/users/{id}", get(get_user));
|
||||||
|
/// # let _: Router = app;
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Trailing commas are allowed in both arrays and objects.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// let response = axum_extra::json!(["trailing",]);
|
||||||
|
/// ```
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! json {
|
||||||
|
($($t:tt)*) => {
|
||||||
|
$crate::response::ErasedJson::new(
|
||||||
|
$crate::response::__private_erased_json::typed_json::json!($($t)*)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Not public API. Re-exported as `crate::response::__private_erased_json`.
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub mod private {
|
||||||
|
pub use typed_json;
|
||||||
|
}
|
||||||
|
|
|
@ -12,6 +12,11 @@ pub mod multiple;
|
||||||
#[cfg(feature = "erased-json")]
|
#[cfg(feature = "erased-json")]
|
||||||
pub use erased_json::ErasedJson;
|
pub use erased_json::ErasedJson;
|
||||||
|
|
||||||
|
/// _not_ public API
|
||||||
|
#[cfg(feature = "erased-json")]
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub use erased_json::private as __private_erased_json;
|
||||||
|
|
||||||
#[cfg(feature = "json-lines")]
|
#[cfg(feature = "json-lines")]
|
||||||
#[doc(no_inline)]
|
#[doc(no_inline)]
|
||||||
pub use crate::json_lines::JsonLines;
|
pub use crate::json_lines::JsonLines;
|
||||||
|
|
|
@ -54,6 +54,11 @@ use serde::{de::DeserializeOwned, Serialize};
|
||||||
/// When used as a response, it can serialize any type that implements [`serde::Serialize`] to
|
/// When used as a response, it can serialize any type that implements [`serde::Serialize`] to
|
||||||
/// `JSON`, and will automatically set `Content-Type: application/json` header.
|
/// `JSON`, and will automatically set `Content-Type: application/json` header.
|
||||||
///
|
///
|
||||||
|
/// If the [`Serialize`] implementation decides to fail
|
||||||
|
/// or if a map with non-string keys is used,
|
||||||
|
/// a 500 response will be issued
|
||||||
|
/// whose body is the error message in UTF-8.
|
||||||
|
///
|
||||||
/// # Response example
|
/// # Response example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
|
|
Loading…
Reference in a new issue