Skip to main content

Response

Struct Response 

Source
pub struct Response { /* private fields */ }
Expand description

HTTP response.

Implementations§

Source§

impl Response

Source

pub fn with_status(status: StatusCode) -> Response

Create a response with the given status.

Examples found in repository?
examples/crud_api.rs (line 126)
124fn json_error(status: StatusCode, detail: &str) -> Response {
125    let body = serde_json::json!({ "detail": detail });
126    Response::with_status(status)
127        .header("content-type", b"application/json".to_vec())
128        .body(ResponseBody::Bytes(body.to_string().into_bytes()))
129}
130
131fn json_response(status: StatusCode, value: &impl Serialize) -> Response {
132    match serde_json::to_string(value) {
133        Ok(text) => Response::with_status(status)
134            .header("content-type", b"application/json".to_vec())
135            .body(ResponseBody::Bytes(text.into_bytes())),
136        Err(err) => json_error(
137            StatusCode::INTERNAL_SERVER_ERROR,
138            &format!("JSON serialization failed: {err}"),
139        ),
140    }
141}
142
143fn validate_input(input: &UserInput) -> Option<Response> {
144    if input.name.trim().is_empty() {
145        return Some(json_error(
146            StatusCode::BAD_REQUEST,
147            "name must not be empty",
148        ));
149    }
150    if !input.email.contains('@') {
151        return Some(json_error(
152            StatusCode::BAD_REQUEST,
153            "email must contain '@'",
154        ));
155    }
156    None
157}
158
159// ============================================================================
160// Handlers
161// ============================================================================
162
163fn create_user(_ctx: &RequestContext, req: &mut Request) -> std::future::Ready<Response> {
164    let input = match parse_json_body::<UserInput>(req) {
165        Ok(v) => v,
166        Err(r) => return std::future::ready(r),
167    };
168    if let Some(r) = validate_input(&input) {
169        return std::future::ready(r);
170    }
171
172    let user = with_db(|db| {
173        let id = db.next_id;
174        db.next_id += 1;
175        let user = User {
176            id,
177            name: input.name,
178            email: input.email,
179        };
180        db.users.insert(id, user.clone());
181        user
182    });
183
184    std::future::ready(json_response(StatusCode::CREATED, &user))
185}
186
187fn list_users(_ctx: &RequestContext, _req: &mut Request) -> std::future::Ready<Response> {
188    let users = with_db(|db| {
189        let mut v: Vec<User> = db.users.values().cloned().collect();
190        v.sort_by_key(|u| u.id);
191        v
192    });
193    std::future::ready(json_response(StatusCode::OK, &users))
194}
195
196fn get_user(_ctx: &RequestContext, req: &mut Request) -> std::future::Ready<Response> {
197    let id = match extract_user_id(req) {
198        Ok(id) => id,
199        Err(r) => return std::future::ready(r),
200    };
201    let result = with_db(|db| db.users.get(&id).cloned());
202    match result {
203        Some(user) => std::future::ready(json_response(StatusCode::OK, &user)),
204        None => std::future::ready(json_error(
205            StatusCode::NOT_FOUND,
206            &format!("User {id} not found"),
207        )),
208    }
209}
210
211fn update_user(_ctx: &RequestContext, req: &mut Request) -> std::future::Ready<Response> {
212    let id = match extract_user_id(req) {
213        Ok(id) => id,
214        Err(r) => return std::future::ready(r),
215    };
216    let input = match parse_json_body::<UserInput>(req) {
217        Ok(v) => v,
218        Err(r) => return std::future::ready(r),
219    };
220    if let Some(r) = validate_input(&input) {
221        return std::future::ready(r);
222    }
223
224    let result = with_db(|db| {
225        db.users.get_mut(&id).map(|user| {
226            user.name = input.name;
227            user.email = input.email;
228            user.clone()
229        })
230    });
231    match result {
232        Some(user) => std::future::ready(json_response(StatusCode::OK, &user)),
233        None => std::future::ready(json_error(
234            StatusCode::NOT_FOUND,
235            &format!("User {id} not found"),
236        )),
237    }
238}
239
240fn delete_user(_ctx: &RequestContext, req: &mut Request) -> std::future::Ready<Response> {
241    let id = match extract_user_id(req) {
242        Ok(id) => id,
243        Err(r) => return std::future::ready(r),
244    };
245    let removed = with_db(|db| db.users.remove(&id).is_some());
246    if removed {
247        std::future::ready(Response::with_status(StatusCode::NO_CONTENT))
248    } else {
249        std::future::ready(json_error(
250            StatusCode::NOT_FOUND,
251            &format!("User {id} not found"),
252        ))
253    }
254}
More examples
Hide additional examples
examples/auth_example.rs (line 121)
106fn login_handler(_ctx: &RequestContext, req: &mut Request) -> std::future::Ready<Response> {
107    // In a real app, we would parse the JSON body and validate credentials.
108    // For this demo, we just check that it's a POST with some body.
109
110    // Check Content-Type
111    let is_json = req
112        .headers()
113        .get("content-type")
114        .is_some_and(|ct| ct.starts_with(b"application/json"));
115
116    if !is_json {
117        let error = serde_json::json!({
118            "detail": "Content-Type must be application/json"
119        });
120        return std::future::ready(
121            Response::with_status(StatusCode::UNSUPPORTED_MEDIA_TYPE)
122                .header("content-type", b"application/json".to_vec())
123                .body(ResponseBody::Bytes(error.to_string().into_bytes())),
124        );
125    }
126
127    // For demo purposes, we don't validate credentials - just return the token
128    // In production, you would:
129    // 1. Parse the request body as LoginRequest
130    // 2. Verify username/password against your database
131    // 3. Generate a unique, cryptographically secure token
132    // 4. Store token -> user_id mapping (with expiration)
133
134    let response = LoginResponse {
135        access_token: DEMO_BEARER_VALUE.to_string(),
136        token_type: "bearer",
137    };
138
139    std::future::ready(
140        Response::ok()
141            .header("content-type", b"application/json".to_vec())
142            .body(ResponseBody::Bytes(json_bytes(&response))),
143    )
144}
145
146/// Handler for protected endpoint - requires valid bearer token.
147///
148/// This handler manually extracts and validates the bearer token:
149/// 1. Gets the Authorization header
150/// 2. Verifies it uses the Bearer scheme
151/// 3. Validates the token against our secret using constant-time comparison
152///
153/// Returns appropriate error responses for each failure mode.
154fn protected_handler(_ctx: &RequestContext, req: &mut Request) -> std::future::Ready<Response> {
155    // Step 1: Get the Authorization header
156    let Some(auth_header) = req.headers().get("authorization") else {
157        // Missing header -> 401 Unauthorized
158        let body = serde_json::json!({
159            "detail": "Not authenticated"
160        });
161        return std::future::ready(
162            Response::with_status(StatusCode::UNAUTHORIZED)
163                .header("www-authenticate", b"Bearer".to_vec())
164                .header("content-type", b"application/json".to_vec())
165                .body(ResponseBody::Bytes(body.to_string().into_bytes())),
166        );
167    };
168
169    // Step 2: Parse the Authorization header
170    let Ok(auth_str) = std::str::from_utf8(auth_header) else {
171        // Invalid UTF-8 -> 401 Unauthorized
172        let body = serde_json::json!({
173            "detail": "Invalid authentication credentials"
174        });
175        return std::future::ready(
176            Response::with_status(StatusCode::UNAUTHORIZED)
177                .header("www-authenticate", b"Bearer".to_vec())
178                .header("content-type", b"application/json".to_vec())
179                .body(ResponseBody::Bytes(body.to_string().into_bytes())),
180        );
181    };
182
183    // Step 3: Check for "Bearer " prefix (case-insensitive for the scheme)
184    let Some(bearer_value) = auth_str
185        .strip_prefix("Bearer ")
186        .or_else(|| auth_str.strip_prefix("bearer "))
187    else {
188        // Wrong scheme -> 401 Unauthorized
189        let body = serde_json::json!({
190            "detail": "Invalid authentication credentials"
191        });
192        return std::future::ready(
193            Response::with_status(StatusCode::UNAUTHORIZED)
194                .header("www-authenticate", b"Bearer".to_vec())
195                .header("content-type", b"application/json".to_vec())
196                .body(ResponseBody::Bytes(body.to_string().into_bytes())),
197        );
198    };
199
200    let bearer_value = bearer_value.trim();
201    if bearer_value.is_empty() {
202        // Empty token -> 401 Unauthorized
203        let body = serde_json::json!({
204            "detail": "Invalid authentication credentials"
205        });
206        return std::future::ready(
207            Response::with_status(StatusCode::UNAUTHORIZED)
208                .header("www-authenticate", b"Bearer".to_vec())
209                .header("content-type", b"application/json".to_vec())
210                .body(ResponseBody::Bytes(body.to_string().into_bytes())),
211        );
212    }
213
214    // Step 4: Validate the bearer value using constant-time comparison
215    if !bearer_value.secure_eq(DEMO_BEARER_VALUE) {
216        // Invalid token -> 403 Forbidden
217        let body = serde_json::json!({
218            "detail": "Invalid token"
219        });
220        return std::future::ready(
221            Response::with_status(StatusCode::FORBIDDEN)
222                .header("content-type", b"application/json".to_vec())
223                .body(ResponseBody::Bytes(body.to_string().into_bytes())),
224        );
225    }
226
227    // Token is valid - return protected data
228    let user_info = UserInfo {
229        username: "demo_user".to_string(),
230        message: "You have accessed a protected resource!".to_string(),
231    };
232
233    std::future::ready(
234        Response::ok()
235            .header("content-type", b"application/json".to_vec())
236            .body(ResponseBody::Bytes(json_bytes(&user_info))),
237    )
238}
Source

pub fn ok() -> Response

Create a 200 OK response.

Examples found in repository?
examples/getting_started.rs (line 14)
13fn hello(_ctx: &RequestContext, _req: &mut Request) -> std::future::Ready<Response> {
14    std::future::ready(Response::ok().body(ResponseBody::Bytes(b"Hello, World!".to_vec())))
15}
16
17/// Handler for GET /health
18fn health(_ctx: &RequestContext, _req: &mut Request) -> std::future::Ready<Response> {
19    std::future::ready(
20        Response::ok().body(ResponseBody::Bytes(b"{\"status\":\"healthy\"}".to_vec())),
21    )
22}
More examples
Hide additional examples
examples/hello_world.rs (line 48)
46fn hello_handler(_ctx: &RequestContext, _req: &mut Request) -> std::future::Ready<Response> {
47    // Create a 200 OK response with a plain text body
48    std::future::ready(Response::ok().body(ResponseBody::Bytes(b"Hello, World!".to_vec())))
49}
examples/auth_example.rs (line 91)
86fn public_handler(_ctx: &RequestContext, _req: &mut Request) -> std::future::Ready<Response> {
87    let body = serde_json::json!({
88        "message": "This is a public endpoint - no authentication required!"
89    });
90    std::future::ready(
91        Response::ok()
92            .header("content-type", b"application/json".to_vec())
93            .body(ResponseBody::Bytes(body.to_string().into_bytes())),
94    )
95}
96
97/// Handler for the login endpoint.
98///
99/// In a real application, this would:
100/// 1. Validate username/password against a database
101/// 2. Generate a unique token (JWT or random)
102/// 3. Store the token with associated user info
103/// 4. Return the token to the client
104///
105/// For this demo, we accept any credentials and return a fixed token.
106fn login_handler(_ctx: &RequestContext, req: &mut Request) -> std::future::Ready<Response> {
107    // In a real app, we would parse the JSON body and validate credentials.
108    // For this demo, we just check that it's a POST with some body.
109
110    // Check Content-Type
111    let is_json = req
112        .headers()
113        .get("content-type")
114        .is_some_and(|ct| ct.starts_with(b"application/json"));
115
116    if !is_json {
117        let error = serde_json::json!({
118            "detail": "Content-Type must be application/json"
119        });
120        return std::future::ready(
121            Response::with_status(StatusCode::UNSUPPORTED_MEDIA_TYPE)
122                .header("content-type", b"application/json".to_vec())
123                .body(ResponseBody::Bytes(error.to_string().into_bytes())),
124        );
125    }
126
127    // For demo purposes, we don't validate credentials - just return the token
128    // In production, you would:
129    // 1. Parse the request body as LoginRequest
130    // 2. Verify username/password against your database
131    // 3. Generate a unique, cryptographically secure token
132    // 4. Store token -> user_id mapping (with expiration)
133
134    let response = LoginResponse {
135        access_token: DEMO_BEARER_VALUE.to_string(),
136        token_type: "bearer",
137    };
138
139    std::future::ready(
140        Response::ok()
141            .header("content-type", b"application/json".to_vec())
142            .body(ResponseBody::Bytes(json_bytes(&response))),
143    )
144}
145
146/// Handler for protected endpoint - requires valid bearer token.
147///
148/// This handler manually extracts and validates the bearer token:
149/// 1. Gets the Authorization header
150/// 2. Verifies it uses the Bearer scheme
151/// 3. Validates the token against our secret using constant-time comparison
152///
153/// Returns appropriate error responses for each failure mode.
154fn protected_handler(_ctx: &RequestContext, req: &mut Request) -> std::future::Ready<Response> {
155    // Step 1: Get the Authorization header
156    let Some(auth_header) = req.headers().get("authorization") else {
157        // Missing header -> 401 Unauthorized
158        let body = serde_json::json!({
159            "detail": "Not authenticated"
160        });
161        return std::future::ready(
162            Response::with_status(StatusCode::UNAUTHORIZED)
163                .header("www-authenticate", b"Bearer".to_vec())
164                .header("content-type", b"application/json".to_vec())
165                .body(ResponseBody::Bytes(body.to_string().into_bytes())),
166        );
167    };
168
169    // Step 2: Parse the Authorization header
170    let Ok(auth_str) = std::str::from_utf8(auth_header) else {
171        // Invalid UTF-8 -> 401 Unauthorized
172        let body = serde_json::json!({
173            "detail": "Invalid authentication credentials"
174        });
175        return std::future::ready(
176            Response::with_status(StatusCode::UNAUTHORIZED)
177                .header("www-authenticate", b"Bearer".to_vec())
178                .header("content-type", b"application/json".to_vec())
179                .body(ResponseBody::Bytes(body.to_string().into_bytes())),
180        );
181    };
182
183    // Step 3: Check for "Bearer " prefix (case-insensitive for the scheme)
184    let Some(bearer_value) = auth_str
185        .strip_prefix("Bearer ")
186        .or_else(|| auth_str.strip_prefix("bearer "))
187    else {
188        // Wrong scheme -> 401 Unauthorized
189        let body = serde_json::json!({
190            "detail": "Invalid authentication credentials"
191        });
192        return std::future::ready(
193            Response::with_status(StatusCode::UNAUTHORIZED)
194                .header("www-authenticate", b"Bearer".to_vec())
195                .header("content-type", b"application/json".to_vec())
196                .body(ResponseBody::Bytes(body.to_string().into_bytes())),
197        );
198    };
199
200    let bearer_value = bearer_value.trim();
201    if bearer_value.is_empty() {
202        // Empty token -> 401 Unauthorized
203        let body = serde_json::json!({
204            "detail": "Invalid authentication credentials"
205        });
206        return std::future::ready(
207            Response::with_status(StatusCode::UNAUTHORIZED)
208                .header("www-authenticate", b"Bearer".to_vec())
209                .header("content-type", b"application/json".to_vec())
210                .body(ResponseBody::Bytes(body.to_string().into_bytes())),
211        );
212    }
213
214    // Step 4: Validate the bearer value using constant-time comparison
215    if !bearer_value.secure_eq(DEMO_BEARER_VALUE) {
216        // Invalid token -> 403 Forbidden
217        let body = serde_json::json!({
218            "detail": "Invalid token"
219        });
220        return std::future::ready(
221            Response::with_status(StatusCode::FORBIDDEN)
222                .header("content-type", b"application/json".to_vec())
223                .body(ResponseBody::Bytes(body.to_string().into_bytes())),
224        );
225    }
226
227    // Token is valid - return protected data
228    let user_info = UserInfo {
229        username: "demo_user".to_string(),
230        message: "You have accessed a protected resource!".to_string(),
231    };
232
233    std::future::ready(
234        Response::ok()
235            .header("content-type", b"application/json".to_vec())
236            .body(ResponseBody::Bytes(json_bytes(&user_info))),
237    )
238}
Source

pub fn created() -> Response

Create a 201 Created response.

Source

pub fn no_content() -> Response

Create a 204 No Content response.

Source

pub fn internal_error() -> Response

Create a 500 Internal Server Error response.

Source

pub fn partial_content() -> Response

Create a 206 Partial Content response.

Used for range requests. You should also set the Content-Range header.

§Example
use fastapi_core::{Response, ResponseBody};

let response = Response::partial_content()
    .header("Content-Range", b"bytes 0-499/1000".to_vec())
    .header("Accept-Ranges", b"bytes".to_vec())
    .body(ResponseBody::Bytes(partial_data));
Source

pub fn range_not_satisfiable() -> Response

Create a 416 Range Not Satisfiable response.

Used when a Range header specifies a range that cannot be satisfied. You should also set the Content-Range header with the resource size.

§Example
use fastapi_core::Response;

let response = Response::range_not_satisfiable()
    .header("Content-Range", b"bytes */1000".to_vec());
Source

pub fn not_modified() -> Response

Create a 304 Not Modified response.

Used for conditional requests where the resource has not changed. The response body is empty per HTTP spec.

Source

pub fn precondition_failed() -> Response

Create a 412 Precondition Failed response.

Used when a conditional request’s precondition (e.g., If-Match) fails.

Source

pub fn with_etag(self, etag: impl Into<String>) -> Response

Set the ETag header on this response.

§Example
let response = Response::ok()
    .with_etag("\"abc123\"")
    .body(b"content".to_vec());
Source

pub fn with_weak_etag(self, etag: impl Into<String>) -> Response

Set a weak ETag header on this response.

Automatically prefixes with W/ if not already present.

Source

pub fn header( self, name: impl Into<String>, value: impl Into<Vec<u8>>, ) -> Response

Add a header.

§Security

Header names are validated to contain only valid token characters. Header values are sanitized to prevent CRLF injection attacks. Invalid characters in names will cause the header to be silently dropped.

Examples found in repository?
examples/crud_api.rs (line 127)
124fn json_error(status: StatusCode, detail: &str) -> Response {
125    let body = serde_json::json!({ "detail": detail });
126    Response::with_status(status)
127        .header("content-type", b"application/json".to_vec())
128        .body(ResponseBody::Bytes(body.to_string().into_bytes()))
129}
130
131fn json_response(status: StatusCode, value: &impl Serialize) -> Response {
132    match serde_json::to_string(value) {
133        Ok(text) => Response::with_status(status)
134            .header("content-type", b"application/json".to_vec())
135            .body(ResponseBody::Bytes(text.into_bytes())),
136        Err(err) => json_error(
137            StatusCode::INTERNAL_SERVER_ERROR,
138            &format!("JSON serialization failed: {err}"),
139        ),
140    }
141}
More examples
Hide additional examples
examples/auth_example.rs (line 92)
86fn public_handler(_ctx: &RequestContext, _req: &mut Request) -> std::future::Ready<Response> {
87    let body = serde_json::json!({
88        "message": "This is a public endpoint - no authentication required!"
89    });
90    std::future::ready(
91        Response::ok()
92            .header("content-type", b"application/json".to_vec())
93            .body(ResponseBody::Bytes(body.to_string().into_bytes())),
94    )
95}
96
97/// Handler for the login endpoint.
98///
99/// In a real application, this would:
100/// 1. Validate username/password against a database
101/// 2. Generate a unique token (JWT or random)
102/// 3. Store the token with associated user info
103/// 4. Return the token to the client
104///
105/// For this demo, we accept any credentials and return a fixed token.
106fn login_handler(_ctx: &RequestContext, req: &mut Request) -> std::future::Ready<Response> {
107    // In a real app, we would parse the JSON body and validate credentials.
108    // For this demo, we just check that it's a POST with some body.
109
110    // Check Content-Type
111    let is_json = req
112        .headers()
113        .get("content-type")
114        .is_some_and(|ct| ct.starts_with(b"application/json"));
115
116    if !is_json {
117        let error = serde_json::json!({
118            "detail": "Content-Type must be application/json"
119        });
120        return std::future::ready(
121            Response::with_status(StatusCode::UNSUPPORTED_MEDIA_TYPE)
122                .header("content-type", b"application/json".to_vec())
123                .body(ResponseBody::Bytes(error.to_string().into_bytes())),
124        );
125    }
126
127    // For demo purposes, we don't validate credentials - just return the token
128    // In production, you would:
129    // 1. Parse the request body as LoginRequest
130    // 2. Verify username/password against your database
131    // 3. Generate a unique, cryptographically secure token
132    // 4. Store token -> user_id mapping (with expiration)
133
134    let response = LoginResponse {
135        access_token: DEMO_BEARER_VALUE.to_string(),
136        token_type: "bearer",
137    };
138
139    std::future::ready(
140        Response::ok()
141            .header("content-type", b"application/json".to_vec())
142            .body(ResponseBody::Bytes(json_bytes(&response))),
143    )
144}
145
146/// Handler for protected endpoint - requires valid bearer token.
147///
148/// This handler manually extracts and validates the bearer token:
149/// 1. Gets the Authorization header
150/// 2. Verifies it uses the Bearer scheme
151/// 3. Validates the token against our secret using constant-time comparison
152///
153/// Returns appropriate error responses for each failure mode.
154fn protected_handler(_ctx: &RequestContext, req: &mut Request) -> std::future::Ready<Response> {
155    // Step 1: Get the Authorization header
156    let Some(auth_header) = req.headers().get("authorization") else {
157        // Missing header -> 401 Unauthorized
158        let body = serde_json::json!({
159            "detail": "Not authenticated"
160        });
161        return std::future::ready(
162            Response::with_status(StatusCode::UNAUTHORIZED)
163                .header("www-authenticate", b"Bearer".to_vec())
164                .header("content-type", b"application/json".to_vec())
165                .body(ResponseBody::Bytes(body.to_string().into_bytes())),
166        );
167    };
168
169    // Step 2: Parse the Authorization header
170    let Ok(auth_str) = std::str::from_utf8(auth_header) else {
171        // Invalid UTF-8 -> 401 Unauthorized
172        let body = serde_json::json!({
173            "detail": "Invalid authentication credentials"
174        });
175        return std::future::ready(
176            Response::with_status(StatusCode::UNAUTHORIZED)
177                .header("www-authenticate", b"Bearer".to_vec())
178                .header("content-type", b"application/json".to_vec())
179                .body(ResponseBody::Bytes(body.to_string().into_bytes())),
180        );
181    };
182
183    // Step 3: Check for "Bearer " prefix (case-insensitive for the scheme)
184    let Some(bearer_value) = auth_str
185        .strip_prefix("Bearer ")
186        .or_else(|| auth_str.strip_prefix("bearer "))
187    else {
188        // Wrong scheme -> 401 Unauthorized
189        let body = serde_json::json!({
190            "detail": "Invalid authentication credentials"
191        });
192        return std::future::ready(
193            Response::with_status(StatusCode::UNAUTHORIZED)
194                .header("www-authenticate", b"Bearer".to_vec())
195                .header("content-type", b"application/json".to_vec())
196                .body(ResponseBody::Bytes(body.to_string().into_bytes())),
197        );
198    };
199
200    let bearer_value = bearer_value.trim();
201    if bearer_value.is_empty() {
202        // Empty token -> 401 Unauthorized
203        let body = serde_json::json!({
204            "detail": "Invalid authentication credentials"
205        });
206        return std::future::ready(
207            Response::with_status(StatusCode::UNAUTHORIZED)
208                .header("www-authenticate", b"Bearer".to_vec())
209                .header("content-type", b"application/json".to_vec())
210                .body(ResponseBody::Bytes(body.to_string().into_bytes())),
211        );
212    }
213
214    // Step 4: Validate the bearer value using constant-time comparison
215    if !bearer_value.secure_eq(DEMO_BEARER_VALUE) {
216        // Invalid token -> 403 Forbidden
217        let body = serde_json::json!({
218            "detail": "Invalid token"
219        });
220        return std::future::ready(
221            Response::with_status(StatusCode::FORBIDDEN)
222                .header("content-type", b"application/json".to_vec())
223                .body(ResponseBody::Bytes(body.to_string().into_bytes())),
224        );
225    }
226
227    // Token is valid - return protected data
228    let user_info = UserInfo {
229        username: "demo_user".to_string(),
230        message: "You have accessed a protected resource!".to_string(),
231    };
232
233    std::future::ready(
234        Response::ok()
235            .header("content-type", b"application/json".to_vec())
236            .body(ResponseBody::Bytes(json_bytes(&user_info))),
237    )
238}
Source

pub fn body(self, body: ResponseBody) -> Response

Set the body.

Examples found in repository?
examples/getting_started.rs (line 14)
13fn hello(_ctx: &RequestContext, _req: &mut Request) -> std::future::Ready<Response> {
14    std::future::ready(Response::ok().body(ResponseBody::Bytes(b"Hello, World!".to_vec())))
15}
16
17/// Handler for GET /health
18fn health(_ctx: &RequestContext, _req: &mut Request) -> std::future::Ready<Response> {
19    std::future::ready(
20        Response::ok().body(ResponseBody::Bytes(b"{\"status\":\"healthy\"}".to_vec())),
21    )
22}
More examples
Hide additional examples
examples/hello_world.rs (line 48)
46fn hello_handler(_ctx: &RequestContext, _req: &mut Request) -> std::future::Ready<Response> {
47    // Create a 200 OK response with a plain text body
48    std::future::ready(Response::ok().body(ResponseBody::Bytes(b"Hello, World!".to_vec())))
49}
examples/crud_api.rs (line 128)
124fn json_error(status: StatusCode, detail: &str) -> Response {
125    let body = serde_json::json!({ "detail": detail });
126    Response::with_status(status)
127        .header("content-type", b"application/json".to_vec())
128        .body(ResponseBody::Bytes(body.to_string().into_bytes()))
129}
130
131fn json_response(status: StatusCode, value: &impl Serialize) -> Response {
132    match serde_json::to_string(value) {
133        Ok(text) => Response::with_status(status)
134            .header("content-type", b"application/json".to_vec())
135            .body(ResponseBody::Bytes(text.into_bytes())),
136        Err(err) => json_error(
137            StatusCode::INTERNAL_SERVER_ERROR,
138            &format!("JSON serialization failed: {err}"),
139        ),
140    }
141}
examples/auth_example.rs (line 93)
86fn public_handler(_ctx: &RequestContext, _req: &mut Request) -> std::future::Ready<Response> {
87    let body = serde_json::json!({
88        "message": "This is a public endpoint - no authentication required!"
89    });
90    std::future::ready(
91        Response::ok()
92            .header("content-type", b"application/json".to_vec())
93            .body(ResponseBody::Bytes(body.to_string().into_bytes())),
94    )
95}
96
97/// Handler for the login endpoint.
98///
99/// In a real application, this would:
100/// 1. Validate username/password against a database
101/// 2. Generate a unique token (JWT or random)
102/// 3. Store the token with associated user info
103/// 4. Return the token to the client
104///
105/// For this demo, we accept any credentials and return a fixed token.
106fn login_handler(_ctx: &RequestContext, req: &mut Request) -> std::future::Ready<Response> {
107    // In a real app, we would parse the JSON body and validate credentials.
108    // For this demo, we just check that it's a POST with some body.
109
110    // Check Content-Type
111    let is_json = req
112        .headers()
113        .get("content-type")
114        .is_some_and(|ct| ct.starts_with(b"application/json"));
115
116    if !is_json {
117        let error = serde_json::json!({
118            "detail": "Content-Type must be application/json"
119        });
120        return std::future::ready(
121            Response::with_status(StatusCode::UNSUPPORTED_MEDIA_TYPE)
122                .header("content-type", b"application/json".to_vec())
123                .body(ResponseBody::Bytes(error.to_string().into_bytes())),
124        );
125    }
126
127    // For demo purposes, we don't validate credentials - just return the token
128    // In production, you would:
129    // 1. Parse the request body as LoginRequest
130    // 2. Verify username/password against your database
131    // 3. Generate a unique, cryptographically secure token
132    // 4. Store token -> user_id mapping (with expiration)
133
134    let response = LoginResponse {
135        access_token: DEMO_BEARER_VALUE.to_string(),
136        token_type: "bearer",
137    };
138
139    std::future::ready(
140        Response::ok()
141            .header("content-type", b"application/json".to_vec())
142            .body(ResponseBody::Bytes(json_bytes(&response))),
143    )
144}
145
146/// Handler for protected endpoint - requires valid bearer token.
147///
148/// This handler manually extracts and validates the bearer token:
149/// 1. Gets the Authorization header
150/// 2. Verifies it uses the Bearer scheme
151/// 3. Validates the token against our secret using constant-time comparison
152///
153/// Returns appropriate error responses for each failure mode.
154fn protected_handler(_ctx: &RequestContext, req: &mut Request) -> std::future::Ready<Response> {
155    // Step 1: Get the Authorization header
156    let Some(auth_header) = req.headers().get("authorization") else {
157        // Missing header -> 401 Unauthorized
158        let body = serde_json::json!({
159            "detail": "Not authenticated"
160        });
161        return std::future::ready(
162            Response::with_status(StatusCode::UNAUTHORIZED)
163                .header("www-authenticate", b"Bearer".to_vec())
164                .header("content-type", b"application/json".to_vec())
165                .body(ResponseBody::Bytes(body.to_string().into_bytes())),
166        );
167    };
168
169    // Step 2: Parse the Authorization header
170    let Ok(auth_str) = std::str::from_utf8(auth_header) else {
171        // Invalid UTF-8 -> 401 Unauthorized
172        let body = serde_json::json!({
173            "detail": "Invalid authentication credentials"
174        });
175        return std::future::ready(
176            Response::with_status(StatusCode::UNAUTHORIZED)
177                .header("www-authenticate", b"Bearer".to_vec())
178                .header("content-type", b"application/json".to_vec())
179                .body(ResponseBody::Bytes(body.to_string().into_bytes())),
180        );
181    };
182
183    // Step 3: Check for "Bearer " prefix (case-insensitive for the scheme)
184    let Some(bearer_value) = auth_str
185        .strip_prefix("Bearer ")
186        .or_else(|| auth_str.strip_prefix("bearer "))
187    else {
188        // Wrong scheme -> 401 Unauthorized
189        let body = serde_json::json!({
190            "detail": "Invalid authentication credentials"
191        });
192        return std::future::ready(
193            Response::with_status(StatusCode::UNAUTHORIZED)
194                .header("www-authenticate", b"Bearer".to_vec())
195                .header("content-type", b"application/json".to_vec())
196                .body(ResponseBody::Bytes(body.to_string().into_bytes())),
197        );
198    };
199
200    let bearer_value = bearer_value.trim();
201    if bearer_value.is_empty() {
202        // Empty token -> 401 Unauthorized
203        let body = serde_json::json!({
204            "detail": "Invalid authentication credentials"
205        });
206        return std::future::ready(
207            Response::with_status(StatusCode::UNAUTHORIZED)
208                .header("www-authenticate", b"Bearer".to_vec())
209                .header("content-type", b"application/json".to_vec())
210                .body(ResponseBody::Bytes(body.to_string().into_bytes())),
211        );
212    }
213
214    // Step 4: Validate the bearer value using constant-time comparison
215    if !bearer_value.secure_eq(DEMO_BEARER_VALUE) {
216        // Invalid token -> 403 Forbidden
217        let body = serde_json::json!({
218            "detail": "Invalid token"
219        });
220        return std::future::ready(
221            Response::with_status(StatusCode::FORBIDDEN)
222                .header("content-type", b"application/json".to_vec())
223                .body(ResponseBody::Bytes(body.to_string().into_bytes())),
224        );
225    }
226
227    // Token is valid - return protected data
228    let user_info = UserInfo {
229        username: "demo_user".to_string(),
230        message: "You have accessed a protected resource!".to_string(),
231    };
232
233    std::future::ready(
234        Response::ok()
235            .header("content-type", b"application/json".to_vec())
236            .body(ResponseBody::Bytes(json_bytes(&user_info))),
237    )
238}

Set a cookie on the response.

Adds a Set-Cookie header with the serialized cookie value. Multiple cookies can be set by calling this method multiple times.

§Example
use fastapi_core::{Response, Cookie, SameSite};

let response = Response::ok()
    .set_cookie(Cookie::new("session", "abc123").http_only(true))
    .set_cookie(Cookie::new("prefs", "dark").same_site(SameSite::Lax));

Delete a cookie by setting it to expire immediately.

This sets the cookie with an empty value and Max-Age=0, which tells the browser to remove the cookie.

§Example
use fastapi_core::Response;

let response = Response::ok()
    .delete_cookie("session");
Source

pub fn json<T>(value: &T) -> Result<Response, Error>
where T: Serialize,

Create a JSON response.

§Errors

Returns an error if serialization fails.

Source

pub fn status(&self) -> StatusCode

Get the status code.

Source

pub fn headers(&self) -> &[(String, Vec<u8>)]

Get the headers.

Source

pub fn body_ref(&self) -> &ResponseBody

Get the body.

Source

pub fn into_parts(self) -> (StatusCode, Vec<(String, Vec<u8>)>, ResponseBody)

Decompose this response into its parts.

Source

pub fn rebuild_with_headers(self, headers: Vec<(String, Vec<u8>)>) -> Response

Rebuilds this response with the given headers, preserving status and body.

This is useful for middleware that needs to modify the response but preserve original headers.

§Example
let (status, headers, body) = response.into_parts();
// ... modify headers ...
let new_response = Response::with_status(status)
    .body(body)
    .rebuild_with_headers(headers);

Trait Implementations§

Source§

impl Debug for Response

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl IntoResponse for Response

Source§

fn into_response(self) -> Response

Convert into a response.
Source§

impl StreamingResponseExt for Response

Source§

fn stream_file<P>( path: P, cx: Cx, content_type: &[u8], ) -> Result<Response, Error>
where P: AsRef<Path>,

Create a streaming response from a file. Read more
Source§

fn stream_file_with_config<P>( path: P, cx: Cx, content_type: &[u8], config: StreamConfig, ) -> Result<Response, Error>
where P: AsRef<Path>,

Create a streaming response from a file with custom config. Read more
Source§

fn stream_file_range<P>( path: P, range: ByteRange, total_size: u64, cx: Cx, content_type: &[u8], ) -> Result<Response, Error>
where P: AsRef<Path>,

Create a 206 Partial Content response for a byte range of a file. Read more
Source§

fn stream_file_range_with_config<P>( path: P, range: ByteRange, total_size: u64, cx: Cx, content_type: &[u8], config: StreamConfig, ) -> Result<Response, Error>
where P: AsRef<Path>,

Create a 206 Partial Content response with custom streaming config. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, _span: NoopSpan) -> Self

Instruments this future with a span (no-op when disabled).
Source§

fn in_current_span(self) -> Self

Instruments this future with the current span (no-op when disabled).
Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

impl<T> ResponseProduces<T> for T