rust 50 lines · 7 steps

Custom Axum responses with per-user ETags

A generic wrapper that serializes any payload to JSON and attaches a user-specific ETag by implementing IntoResponse.

Explained by highlit
1use axum::body::Bytes;
2use axum::http::{header, HeaderValue, StatusCode};
3use axum::response::{IntoResponse, Response};
4use serde::Serialize;
5use sha2::{Digest, Sha256};
6 
7pub struct Personalized<T> {
8 user_id: i64,
9 payload: T,
10}
11 
12impl<T> Personalized<T> {
13 pub fn new(user_id: i64, payload: T) -> Self {
14 Self { user_id, payload }
15 }
16}
17 
18impl<T: Serialize> IntoResponse for Personalized<T> {
19 fn into_response(self) -> Response {
20 let body = match serde_json::to_vec(&self.payload) {
21 Ok(bytes) => Bytes::from(bytes),
22 Err(err) => {
23 return (
24 StatusCode::INTERNAL_SERVER_ERROR,
25 format!("serialization failed: {err}"),
26 )
27 .into_response();
28 }
29 };
30 
31 let mut hasher = Sha256::new();
32 hasher.update(self.user_id.to_le_bytes());
33 hasher.update(&body);
34 let digest = hasher.finalize();
35 
36 let etag = format!("\"{:x}\"", digest);
37 let etag = HeaderValue::from_str(&etag)
38 .unwrap_or_else(|_| HeaderValue::from_static("\"invalid\""));
39 
40 (
41 [
42 (header::CONTENT_TYPE, HeaderValue::from_static("application/json")),
43 (header::ETAG, etag),
44 (header::CACHE_CONTROL, HeaderValue::from_static("private, max-age=0")),
45 ],
46 body,
47 )
48 .into_response()
49 }
50}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Implementing IntoResponse lets you package serialization, headers, and status into a reusable return type your handlers can hand back directly.
  2. 2Folding the user id into the ETag hash keeps caches from serving one user's personalized body to another.
  3. 3Returning early with a status-and-message tuple gives you clean error responses that also satisfy IntoResponse.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Custom Axum responses with per-user ETags — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code