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

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