pub struct RequestBuilder<'a, H> { /* private fields */ }Expand description
Builder for constructing test requests with a fluent API.
Use the methods on TestClient to create a request builder,
then chain configuration methods and call send to execute.
§Example
ⓘ
let response = client
.post("/api/items")
.header("Content-Type", "application/json")
.body(r#"{"name": "Widget"}"#)
.send();Implementations§
Source§impl<'a, H> RequestBuilder<'a, H>where
H: Handler + 'static,
impl<'a, H> RequestBuilder<'a, H>where
H: Handler + 'static,
Sourcepub fn query(self, key: &str, value: &str) -> RequestBuilder<'a, H>
pub fn query(self, key: &str, value: &str) -> RequestBuilder<'a, H>
Sourcepub fn header(
self,
name: impl Into<String>,
value: impl Into<Vec<u8>>,
) -> RequestBuilder<'a, H>
pub fn header( self, name: impl Into<String>, value: impl Into<Vec<u8>>, ) -> RequestBuilder<'a, H>
Examples found in repository?
examples/crud_api.rs (line 299)
271fn main() {
272 // Initialize the global store
273 let mut guard = STORE
274 .lock()
275 .unwrap_or_else(std::sync::PoisonError::into_inner);
276 *guard = Some(UserDb {
277 users: HashMap::new(),
278 next_id: 1,
279 });
280
281 println!("fastapi_rust CRUD API Example");
282 println!("=============================\n");
283
284 let app = App::builder()
285 .post("/users", create_user)
286 .get("/users", list_users)
287 .get("/users/{id}", get_user)
288 .put("/users/{id}", update_user)
289 .delete("/users/{id}", delete_user)
290 .build();
291
292 println!("App created with {} route(s)\n", app.route_count());
293 let client = TestClient::new(app);
294
295 // 1. Create users
296 println!("1. Create users");
297 let resp = client
298 .post("/users")
299 .header("content-type", "application/json")
300 .body(r#"{"name": "Alice", "email": "alice@example.com"}"#)
301 .send();
302 println!(
303 " POST /users -> {} {}",
304 resp.status().as_u16(),
305 resp.text()
306 );
307 if !check_eq(resp.status().as_u16(), 201, "Create user should return 201") {
308 return;
309 }
310
311 let resp = client
312 .post("/users")
313 .header("content-type", "application/json")
314 .body(r#"{"name": "Bob", "email": "bob@example.com"}"#)
315 .send();
316 println!(
317 " POST /users -> {} {}",
318 resp.status().as_u16(),
319 resp.text()
320 );
321 if !check_eq(resp.status().as_u16(), 201, "Create user should return 201") {
322 return;
323 }
324
325 // 2. List users
326 println!("\n2. List all users");
327 let resp = client.get("/users").send();
328 println!(
329 " GET /users -> {} {}",
330 resp.status().as_u16(),
331 resp.text()
332 );
333 if !check_eq(resp.status().as_u16(), 200, "List users should return 200") {
334 return;
335 }
336
337 // 3. Get user by ID
338 println!("\n3. Get user by ID");
339 let resp = client.get("/users/1").send();
340 println!(
341 " GET /users/1 -> {} {}",
342 resp.status().as_u16(),
343 resp.text()
344 );
345 if !check_eq(resp.status().as_u16(), 200, "Get user should return 200") {
346 return;
347 }
348
349 // 4. Get nonexistent user
350 println!("\n4. Get nonexistent user");
351 let resp = client.get("/users/999").send();
352 println!(
353 " GET /users/999 -> {} {}",
354 resp.status().as_u16(),
355 resp.text()
356 );
357 if !check_eq(
358 resp.status().as_u16(),
359 404,
360 "Missing user should return 404",
361 ) {
362 return;
363 }
364
365 // 5. Update user
366 println!("\n5. Update user");
367 let resp = client
368 .put("/users/1")
369 .header("content-type", "application/json")
370 .body(r#"{"name": "Alice Smith", "email": "alice.smith@example.com"}"#)
371 .send();
372 println!(
373 " PUT /users/1 -> {} {}",
374 resp.status().as_u16(),
375 resp.text()
376 );
377 if !check_eq(resp.status().as_u16(), 200, "Update user should return 200") {
378 return;
379 }
380
381 // 6. Validation: empty name
382 println!("\n6. Validation error (empty name)");
383 let resp = client
384 .post("/users")
385 .header("content-type", "application/json")
386 .body(r#"{"name": "", "email": "bad@example.com"}"#)
387 .send();
388 println!(
389 " POST /users -> {} {}",
390 resp.status().as_u16(),
391 resp.text()
392 );
393 if !check_eq(resp.status().as_u16(), 400, "Empty name should return 400") {
394 return;
395 }
396
397 // 7. Validation: invalid email
398 println!("\n7. Validation error (invalid email)");
399 let resp = client
400 .post("/users")
401 .header("content-type", "application/json")
402 .body(r#"{"name": "Charlie", "email": "not-an-email"}"#)
403 .send();
404 println!(
405 " POST /users -> {} {}",
406 resp.status().as_u16(),
407 resp.text()
408 );
409 if !check_eq(
410 resp.status().as_u16(),
411 400,
412 "Invalid email should return 400",
413 ) {
414 return;
415 }
416
417 // 8. Wrong Content-Type
418 println!("\n8. Wrong Content-Type");
419 let resp = client
420 .post("/users")
421 .header("content-type", "text/plain")
422 .body(r#"{"name": "Dan", "email": "dan@example.com"}"#)
423 .send();
424 println!(
425 " POST /users -> {} {}",
426 resp.status().as_u16(),
427 resp.text()
428 );
429 if !check_eq(
430 resp.status().as_u16(),
431 415,
432 "Wrong Content-Type should return 415",
433 ) {
434 return;
435 }
436
437 // 9. Delete user
438 println!("\n9. Delete user");
439 let resp = client.delete("/users/2").send();
440 println!(" DELETE /users/2 -> {}", resp.status().as_u16());
441 if !check_eq(resp.status().as_u16(), 204, "Delete user should return 204") {
442 return;
443 }
444
445 // 10. Verify deletion
446 println!("\n10. Verify deletion");
447 let resp = client.get("/users/2").send();
448 println!(
449 " GET /users/2 -> {} {}",
450 resp.status().as_u16(),
451 resp.text()
452 );
453 if !check_eq(
454 resp.status().as_u16(),
455 404,
456 "Deleted user should return 404",
457 ) {
458 return;
459 }
460
461 let resp = client.get("/users").send();
462 println!(
463 " GET /users -> {} {}",
464 resp.status().as_u16(),
465 resp.text()
466 );
467 if !check_eq(resp.status().as_u16(), 200, "List users should return 200") {
468 return;
469 }
470
471 println!("\nAll CRUD operations passed!");
472}More examples
examples/auth_example.rs (line 346)
267fn main() {
268 println!("fastapi_rust Authentication Example");
269 println!("====================================\n");
270
271 // Build the application with public and protected routes
272 let app = App::builder()
273 // Public endpoints - accessible to everyone
274 .get("/public", public_handler)
275 // Login endpoint - returns a token
276 .post("/login", login_handler)
277 // Protected endpoint - requires valid bearer token
278 .get("/protected", protected_handler)
279 .build();
280
281 println!("App created with {} route(s)\n", app.route_count());
282
283 // Create a test client
284 let client = TestClient::new(app);
285
286 // =========================================================================
287 // Test 1: Public endpoint - no auth required
288 // =========================================================================
289 println!("1. Public endpoint - no auth required");
290 let response = client.get("/public").send();
291 println!(
292 " GET /public -> {} {}",
293 response.status().as_u16(),
294 response.status().canonical_reason()
295 );
296 if !check_eq(
297 response.status().as_u16(),
298 200,
299 "GET /public should return 200",
300 ) {
301 return;
302 }
303 if !check(
304 response.text().contains("public endpoint"),
305 "GET /public should include the public endpoint body",
306 ) {
307 return;
308 }
309
310 // =========================================================================
311 // Test 2: Protected endpoint - without token (should get 401)
312 // =========================================================================
313 println!("\n2. Protected endpoint - without token");
314 let response = client.get("/protected").send();
315 println!(
316 " GET /protected -> {} {}",
317 response.status().as_u16(),
318 response.status().canonical_reason()
319 );
320 if !check_eq(
321 response.status().as_u16(),
322 401,
323 "Protected endpoint should return 401 without token",
324 ) {
325 return;
326 }
327
328 // Check for WWW-Authenticate header
329 let has_www_auth = response
330 .headers()
331 .iter()
332 .any(|(name, value)| name == "www-authenticate" && value == b"Bearer");
333 if !check(
334 has_www_auth,
335 "401 response should include WWW-Authenticate: Bearer header",
336 ) {
337 return;
338 }
339
340 // =========================================================================
341 // Test 3: Login endpoint - get a token
342 // =========================================================================
343 println!("\n3. Login endpoint - get a token");
344 let response = client
345 .post("/login")
346 .header("content-type", "application/json")
347 .body(r#"{"username":"test","password":"test123"}"#)
348 .send();
349 println!(
350 " POST /login -> {} {}",
351 response.status().as_u16(),
352 response.status().canonical_reason()
353 );
354 if !check_eq(
355 response.status().as_u16(),
356 200,
357 "POST /login should return 200",
358 ) {
359 return;
360 }
361
362 // Parse the response to get the token
363 let body_text = response.text();
364 let body: serde_json::Value = match serde_json::from_str(body_text) {
365 Ok(body) => body,
366 Err(err) => {
367 eprintln!("Failed to parse login response JSON: {err}");
368 return;
369 }
370 };
371 let Some(bearer_value) = body.get("access_token").and_then(|value| value.as_str()) else {
372 eprintln!("Login response missing access_token");
373 return;
374 };
375 println!(" Bearer value: {bearer_value}");
376 if !check_eq(
377 bearer_value,
378 DEMO_BEARER_VALUE,
379 "Login should return the expected bearer value",
380 ) {
381 return;
382 }
383
384 // =========================================================================
385 // Test 4: Protected endpoint - with valid token (should get 200)
386 // =========================================================================
387 println!("\n4. Protected endpoint - with valid token");
388 let response = client
389 .get("/protected")
390 .header("authorization", format!("Bearer {DEMO_BEARER_VALUE}"))
391 .send();
392 println!(
393 " GET /protected (Authorization: Bearer {}) -> {} {}",
394 DEMO_BEARER_VALUE,
395 response.status().as_u16(),
396 response.status().canonical_reason()
397 );
398 if !check_eq(
399 response.status().as_u16(),
400 200,
401 "Protected endpoint should return 200 with valid token",
402 ) {
403 return;
404 }
405 if !check(
406 response.text().contains("protected resource"),
407 "Protected endpoint should include protected resource body",
408 ) {
409 return;
410 }
411
412 // =========================================================================
413 // Test 5: Protected endpoint - with invalid token (should get 403)
414 // =========================================================================
415 println!("\n5. Protected endpoint - with invalid token");
416 let response = client
417 .get("/protected")
418 .header("authorization", "Bearer wrong_token")
419 .send();
420 println!(
421 " GET /protected (Authorization: Bearer wrong_token) -> {} {}",
422 response.status().as_u16(),
423 response.status().canonical_reason()
424 );
425 if !check_eq(
426 response.status().as_u16(),
427 403,
428 "Protected endpoint should return 403 with invalid token",
429 ) {
430 return;
431 }
432
433 // =========================================================================
434 // Test 6: Protected endpoint - with wrong auth scheme (should get 401)
435 // =========================================================================
436 println!("\n6. Protected endpoint - with wrong auth scheme");
437 let response = client
438 .get("/protected")
439 .header("authorization", "Basic dXNlcjpwYXNz")
440 .send();
441 println!(
442 " GET /protected (Authorization: Basic ...) -> {} {}",
443 response.status().as_u16(),
444 response.status().canonical_reason()
445 );
446 if !check_eq(
447 response.status().as_u16(),
448 401,
449 "Protected endpoint should return 401 with wrong auth scheme",
450 ) {
451 return;
452 }
453
454 // =========================================================================
455 // Test 7: Login with wrong Content-Type (should get 415)
456 // =========================================================================
457 println!("\n7. Login with wrong Content-Type");
458 let response = client
459 .post("/login")
460 .header("content-type", "text/plain")
461 .body("demo=true")
462 .send();
463 println!(
464 " POST /login (Content-Type: text/plain) -> {} {}",
465 response.status().as_u16(),
466 response.status().canonical_reason()
467 );
468 if !check_eq(
469 response.status().as_u16(),
470 415,
471 "Login should return 415 with wrong Content-Type",
472 ) {
473 return;
474 }
475
476 // =========================================================================
477 // Test 8: Token case sensitivity (lowercase 'bearer')
478 // =========================================================================
479 println!("\n8. Token case sensitivity (lowercase 'bearer')");
480 let response = client
481 .get("/protected")
482 .header("authorization", format!("bearer {DEMO_BEARER_VALUE}"))
483 .send();
484 println!(
485 " GET /protected (Authorization: bearer {}) -> {} {}",
486 DEMO_BEARER_VALUE,
487 response.status().as_u16(),
488 response.status().canonical_reason()
489 );
490 if !check_eq(
491 response.status().as_u16(),
492 200,
493 "Bearer scheme should be case-insensitive (lowercase accepted)",
494 ) {
495 return;
496 }
497
498 println!("\nAll authentication tests passed!");
499}Sourcepub fn header_str(
self,
name: impl Into<String>,
value: &str,
) -> RequestBuilder<'a, H>
pub fn header_str( self, name: impl Into<String>, value: &str, ) -> RequestBuilder<'a, H>
Sets a request header with a string value.
Sourcepub fn body(self, body: impl Into<Vec<u8>>) -> RequestBuilder<'a, H>
pub fn body(self, body: impl Into<Vec<u8>>) -> RequestBuilder<'a, H>
Sets the request body as raw bytes.
§Example
ⓘ
client.post("/upload").body(b"binary data".to_vec()).send()Examples found in repository?
examples/crud_api.rs (line 300)
271fn main() {
272 // Initialize the global store
273 let mut guard = STORE
274 .lock()
275 .unwrap_or_else(std::sync::PoisonError::into_inner);
276 *guard = Some(UserDb {
277 users: HashMap::new(),
278 next_id: 1,
279 });
280
281 println!("fastapi_rust CRUD API Example");
282 println!("=============================\n");
283
284 let app = App::builder()
285 .post("/users", create_user)
286 .get("/users", list_users)
287 .get("/users/{id}", get_user)
288 .put("/users/{id}", update_user)
289 .delete("/users/{id}", delete_user)
290 .build();
291
292 println!("App created with {} route(s)\n", app.route_count());
293 let client = TestClient::new(app);
294
295 // 1. Create users
296 println!("1. Create users");
297 let resp = client
298 .post("/users")
299 .header("content-type", "application/json")
300 .body(r#"{"name": "Alice", "email": "alice@example.com"}"#)
301 .send();
302 println!(
303 " POST /users -> {} {}",
304 resp.status().as_u16(),
305 resp.text()
306 );
307 if !check_eq(resp.status().as_u16(), 201, "Create user should return 201") {
308 return;
309 }
310
311 let resp = client
312 .post("/users")
313 .header("content-type", "application/json")
314 .body(r#"{"name": "Bob", "email": "bob@example.com"}"#)
315 .send();
316 println!(
317 " POST /users -> {} {}",
318 resp.status().as_u16(),
319 resp.text()
320 );
321 if !check_eq(resp.status().as_u16(), 201, "Create user should return 201") {
322 return;
323 }
324
325 // 2. List users
326 println!("\n2. List all users");
327 let resp = client.get("/users").send();
328 println!(
329 " GET /users -> {} {}",
330 resp.status().as_u16(),
331 resp.text()
332 );
333 if !check_eq(resp.status().as_u16(), 200, "List users should return 200") {
334 return;
335 }
336
337 // 3. Get user by ID
338 println!("\n3. Get user by ID");
339 let resp = client.get("/users/1").send();
340 println!(
341 " GET /users/1 -> {} {}",
342 resp.status().as_u16(),
343 resp.text()
344 );
345 if !check_eq(resp.status().as_u16(), 200, "Get user should return 200") {
346 return;
347 }
348
349 // 4. Get nonexistent user
350 println!("\n4. Get nonexistent user");
351 let resp = client.get("/users/999").send();
352 println!(
353 " GET /users/999 -> {} {}",
354 resp.status().as_u16(),
355 resp.text()
356 );
357 if !check_eq(
358 resp.status().as_u16(),
359 404,
360 "Missing user should return 404",
361 ) {
362 return;
363 }
364
365 // 5. Update user
366 println!("\n5. Update user");
367 let resp = client
368 .put("/users/1")
369 .header("content-type", "application/json")
370 .body(r#"{"name": "Alice Smith", "email": "alice.smith@example.com"}"#)
371 .send();
372 println!(
373 " PUT /users/1 -> {} {}",
374 resp.status().as_u16(),
375 resp.text()
376 );
377 if !check_eq(resp.status().as_u16(), 200, "Update user should return 200") {
378 return;
379 }
380
381 // 6. Validation: empty name
382 println!("\n6. Validation error (empty name)");
383 let resp = client
384 .post("/users")
385 .header("content-type", "application/json")
386 .body(r#"{"name": "", "email": "bad@example.com"}"#)
387 .send();
388 println!(
389 " POST /users -> {} {}",
390 resp.status().as_u16(),
391 resp.text()
392 );
393 if !check_eq(resp.status().as_u16(), 400, "Empty name should return 400") {
394 return;
395 }
396
397 // 7. Validation: invalid email
398 println!("\n7. Validation error (invalid email)");
399 let resp = client
400 .post("/users")
401 .header("content-type", "application/json")
402 .body(r#"{"name": "Charlie", "email": "not-an-email"}"#)
403 .send();
404 println!(
405 " POST /users -> {} {}",
406 resp.status().as_u16(),
407 resp.text()
408 );
409 if !check_eq(
410 resp.status().as_u16(),
411 400,
412 "Invalid email should return 400",
413 ) {
414 return;
415 }
416
417 // 8. Wrong Content-Type
418 println!("\n8. Wrong Content-Type");
419 let resp = client
420 .post("/users")
421 .header("content-type", "text/plain")
422 .body(r#"{"name": "Dan", "email": "dan@example.com"}"#)
423 .send();
424 println!(
425 " POST /users -> {} {}",
426 resp.status().as_u16(),
427 resp.text()
428 );
429 if !check_eq(
430 resp.status().as_u16(),
431 415,
432 "Wrong Content-Type should return 415",
433 ) {
434 return;
435 }
436
437 // 9. Delete user
438 println!("\n9. Delete user");
439 let resp = client.delete("/users/2").send();
440 println!(" DELETE /users/2 -> {}", resp.status().as_u16());
441 if !check_eq(resp.status().as_u16(), 204, "Delete user should return 204") {
442 return;
443 }
444
445 // 10. Verify deletion
446 println!("\n10. Verify deletion");
447 let resp = client.get("/users/2").send();
448 println!(
449 " GET /users/2 -> {} {}",
450 resp.status().as_u16(),
451 resp.text()
452 );
453 if !check_eq(
454 resp.status().as_u16(),
455 404,
456 "Deleted user should return 404",
457 ) {
458 return;
459 }
460
461 let resp = client.get("/users").send();
462 println!(
463 " GET /users -> {} {}",
464 resp.status().as_u16(),
465 resp.text()
466 );
467 if !check_eq(resp.status().as_u16(), 200, "List users should return 200") {
468 return;
469 }
470
471 println!("\nAll CRUD operations passed!");
472}More examples
examples/auth_example.rs (line 347)
267fn main() {
268 println!("fastapi_rust Authentication Example");
269 println!("====================================\n");
270
271 // Build the application with public and protected routes
272 let app = App::builder()
273 // Public endpoints - accessible to everyone
274 .get("/public", public_handler)
275 // Login endpoint - returns a token
276 .post("/login", login_handler)
277 // Protected endpoint - requires valid bearer token
278 .get("/protected", protected_handler)
279 .build();
280
281 println!("App created with {} route(s)\n", app.route_count());
282
283 // Create a test client
284 let client = TestClient::new(app);
285
286 // =========================================================================
287 // Test 1: Public endpoint - no auth required
288 // =========================================================================
289 println!("1. Public endpoint - no auth required");
290 let response = client.get("/public").send();
291 println!(
292 " GET /public -> {} {}",
293 response.status().as_u16(),
294 response.status().canonical_reason()
295 );
296 if !check_eq(
297 response.status().as_u16(),
298 200,
299 "GET /public should return 200",
300 ) {
301 return;
302 }
303 if !check(
304 response.text().contains("public endpoint"),
305 "GET /public should include the public endpoint body",
306 ) {
307 return;
308 }
309
310 // =========================================================================
311 // Test 2: Protected endpoint - without token (should get 401)
312 // =========================================================================
313 println!("\n2. Protected endpoint - without token");
314 let response = client.get("/protected").send();
315 println!(
316 " GET /protected -> {} {}",
317 response.status().as_u16(),
318 response.status().canonical_reason()
319 );
320 if !check_eq(
321 response.status().as_u16(),
322 401,
323 "Protected endpoint should return 401 without token",
324 ) {
325 return;
326 }
327
328 // Check for WWW-Authenticate header
329 let has_www_auth = response
330 .headers()
331 .iter()
332 .any(|(name, value)| name == "www-authenticate" && value == b"Bearer");
333 if !check(
334 has_www_auth,
335 "401 response should include WWW-Authenticate: Bearer header",
336 ) {
337 return;
338 }
339
340 // =========================================================================
341 // Test 3: Login endpoint - get a token
342 // =========================================================================
343 println!("\n3. Login endpoint - get a token");
344 let response = client
345 .post("/login")
346 .header("content-type", "application/json")
347 .body(r#"{"username":"test","password":"test123"}"#)
348 .send();
349 println!(
350 " POST /login -> {} {}",
351 response.status().as_u16(),
352 response.status().canonical_reason()
353 );
354 if !check_eq(
355 response.status().as_u16(),
356 200,
357 "POST /login should return 200",
358 ) {
359 return;
360 }
361
362 // Parse the response to get the token
363 let body_text = response.text();
364 let body: serde_json::Value = match serde_json::from_str(body_text) {
365 Ok(body) => body,
366 Err(err) => {
367 eprintln!("Failed to parse login response JSON: {err}");
368 return;
369 }
370 };
371 let Some(bearer_value) = body.get("access_token").and_then(|value| value.as_str()) else {
372 eprintln!("Login response missing access_token");
373 return;
374 };
375 println!(" Bearer value: {bearer_value}");
376 if !check_eq(
377 bearer_value,
378 DEMO_BEARER_VALUE,
379 "Login should return the expected bearer value",
380 ) {
381 return;
382 }
383
384 // =========================================================================
385 // Test 4: Protected endpoint - with valid token (should get 200)
386 // =========================================================================
387 println!("\n4. Protected endpoint - with valid token");
388 let response = client
389 .get("/protected")
390 .header("authorization", format!("Bearer {DEMO_BEARER_VALUE}"))
391 .send();
392 println!(
393 " GET /protected (Authorization: Bearer {}) -> {} {}",
394 DEMO_BEARER_VALUE,
395 response.status().as_u16(),
396 response.status().canonical_reason()
397 );
398 if !check_eq(
399 response.status().as_u16(),
400 200,
401 "Protected endpoint should return 200 with valid token",
402 ) {
403 return;
404 }
405 if !check(
406 response.text().contains("protected resource"),
407 "Protected endpoint should include protected resource body",
408 ) {
409 return;
410 }
411
412 // =========================================================================
413 // Test 5: Protected endpoint - with invalid token (should get 403)
414 // =========================================================================
415 println!("\n5. Protected endpoint - with invalid token");
416 let response = client
417 .get("/protected")
418 .header("authorization", "Bearer wrong_token")
419 .send();
420 println!(
421 " GET /protected (Authorization: Bearer wrong_token) -> {} {}",
422 response.status().as_u16(),
423 response.status().canonical_reason()
424 );
425 if !check_eq(
426 response.status().as_u16(),
427 403,
428 "Protected endpoint should return 403 with invalid token",
429 ) {
430 return;
431 }
432
433 // =========================================================================
434 // Test 6: Protected endpoint - with wrong auth scheme (should get 401)
435 // =========================================================================
436 println!("\n6. Protected endpoint - with wrong auth scheme");
437 let response = client
438 .get("/protected")
439 .header("authorization", "Basic dXNlcjpwYXNz")
440 .send();
441 println!(
442 " GET /protected (Authorization: Basic ...) -> {} {}",
443 response.status().as_u16(),
444 response.status().canonical_reason()
445 );
446 if !check_eq(
447 response.status().as_u16(),
448 401,
449 "Protected endpoint should return 401 with wrong auth scheme",
450 ) {
451 return;
452 }
453
454 // =========================================================================
455 // Test 7: Login with wrong Content-Type (should get 415)
456 // =========================================================================
457 println!("\n7. Login with wrong Content-Type");
458 let response = client
459 .post("/login")
460 .header("content-type", "text/plain")
461 .body("demo=true")
462 .send();
463 println!(
464 " POST /login (Content-Type: text/plain) -> {} {}",
465 response.status().as_u16(),
466 response.status().canonical_reason()
467 );
468 if !check_eq(
469 response.status().as_u16(),
470 415,
471 "Login should return 415 with wrong Content-Type",
472 ) {
473 return;
474 }
475
476 // =========================================================================
477 // Test 8: Token case sensitivity (lowercase 'bearer')
478 // =========================================================================
479 println!("\n8. Token case sensitivity (lowercase 'bearer')");
480 let response = client
481 .get("/protected")
482 .header("authorization", format!("bearer {DEMO_BEARER_VALUE}"))
483 .send();
484 println!(
485 " GET /protected (Authorization: bearer {}) -> {} {}",
486 DEMO_BEARER_VALUE,
487 response.status().as_u16(),
488 response.status().canonical_reason()
489 );
490 if !check_eq(
491 response.status().as_u16(),
492 200,
493 "Bearer scheme should be case-insensitive (lowercase accepted)",
494 ) {
495 return;
496 }
497
498 println!("\nAll authentication tests passed!");
499}Sourcepub fn body_str(self, body: &str) -> RequestBuilder<'a, H>
pub fn body_str(self, body: &str) -> RequestBuilder<'a, H>
Sets the request body as a string.
Sourcepub fn json<T>(self, value: &T) -> RequestBuilder<'a, H>where
T: Serialize,
pub fn json<T>(self, value: &T) -> RequestBuilder<'a, H>where
T: Serialize,
Sets a cookie for this request only.
This does not affect the client’s cookie jar.
Sourcepub fn send(self) -> TestResponse
pub fn send(self) -> TestResponse
Examples found in repository?
examples/hello_world.rs (line 94)
61fn main() {
62 println!("fastapi_rust Hello World Example");
63 println!("================================\n");
64
65 // Build the application
66 //
67 // App::builder() creates a new application builder that lets you:
68 // - Add routes for different HTTP methods
69 // - Configure middleware
70 // - Set application state
71 // - Define exception handlers
72 let app = App::builder()
73 // Register a GET handler for the root path "/"
74 //
75 // This is equivalent to:
76 // @app.get("/")
77 // def hello():
78 // return "Hello, World!"
79 // in Python FastAPI
80 .get("/", hello_handler)
81 // Build the final immutable App
82 .build();
83
84 println!("App created with {} route(s)\n", app.route_count());
85
86 // Create a test client to make requests to our app
87 //
88 // TestClient wraps any Handler (including App) and provides
89 // a convenient API for making HTTP requests in tests.
90 let client = TestClient::new(app);
91
92 // Make a GET request to "/"
93 println!("Making request: GET /");
94 let response = client.get("/").send();
95
96 // Check the response
97 println!(
98 "GET / -> {} {}",
99 response.status().as_u16(),
100 response.status().canonical_reason()
101 );
102 println!("Response: {}\n", response.text());
103
104 // Verify success
105 if !check_eq(response.status().as_u16(), 200, "GET / should return 200") {
106 return;
107 }
108 if !check_eq(response.text(), "Hello, World!", "GET / should return body") {
109 return;
110 }
111
112 // Try a path that doesn't exist - should get 404
113 println!("Making request: GET /not-found");
114 let response = client.get("/not-found").send();
115 println!(
116 "GET /not-found -> {} {}",
117 response.status().as_u16(),
118 response.status().canonical_reason()
119 );
120 if !check_eq(
121 response.status().as_u16(),
122 404,
123 "Unknown routes should return 404",
124 ) {
125 return;
126 }
127
128 println!("\nAll assertions passed!");
129}More examples
examples/getting_started.rs (line 47)
34fn main() {
35 println!("Getting Started Guide - Code Validation\n");
36
37 // === Basic App Example ===
38 println!("1. Basic app with two routes:");
39 let app = App::builder()
40 .get("/", hello)
41 .get("/health", health)
42 .build();
43
44 println!(" Routes: {}", app.route_count());
45 let client = TestClient::new(app);
46
47 let response = client.get("/").send();
48 println!(
49 " GET / -> {} ({})",
50 response.status().as_u16(),
51 response.text()
52 );
53 if !check_eq(response.status().as_u16(), 200, "GET / should return 200") {
54 return;
55 }
56 if !check_eq(response.text(), "Hello, World!", "GET / should return body") {
57 return;
58 }
59
60 let response = client.get("/health").send();
61 println!(
62 " GET /health -> {} ({})",
63 response.status().as_u16(),
64 response.text()
65 );
66 if !check_eq(
67 response.status().as_u16(),
68 200,
69 "GET /health should return 200",
70 ) {
71 return;
72 }
73
74 // === App with Middleware ===
75 println!("\n2. App with middleware:");
76 let app = App::builder()
77 .middleware(RequestIdMiddleware::new())
78 .middleware(SecurityHeaders::new())
79 .get("/", hello)
80 .build();
81
82 let client = TestClient::new(app);
83 let response = client.get("/").send();
84 println!(" GET / -> {}", response.status().as_u16());
85 if !check_eq(
86 response.status().as_u16(),
87 200,
88 "GET / with middleware should return 200",
89 ) {
90 return;
91 }
92
93 // === App with Configuration ===
94 println!("\n3. App with configuration:");
95 let config = AppConfig::new()
96 .name("My API")
97 .version("1.0.0")
98 .debug(true)
99 .max_body_size(10 * 1024 * 1024)
100 .request_timeout_ms(30_000);
101
102 let app = App::builder().config(config).get("/", hello).build();
103
104 println!(" App name: {}", app.config().name);
105 println!(" Version: {}", app.config().version);
106 if !check_eq(
107 app.config().name.as_str(),
108 "My API",
109 "Config name should match",
110 ) {
111 return;
112 }
113 if !check_eq(
114 app.config().version.as_str(),
115 "1.0.0",
116 "Config version should match",
117 ) {
118 return;
119 }
120
121 // === 404 for unknown routes ===
122 println!("\n4. 404 for unknown routes:");
123 let app = App::builder().get("/", hello).build();
124
125 let client = TestClient::new(app);
126 let response = client.get("/nonexistent").send();
127 println!(" GET /nonexistent -> {}", response.status().as_u16());
128 if !check_eq(
129 response.status().as_u16(),
130 404,
131 "Unknown routes should return 404",
132 ) {
133 return;
134 }
135
136 println!("\nAll getting started examples validated successfully!");
137}examples/crud_api.rs (line 301)
271fn main() {
272 // Initialize the global store
273 let mut guard = STORE
274 .lock()
275 .unwrap_or_else(std::sync::PoisonError::into_inner);
276 *guard = Some(UserDb {
277 users: HashMap::new(),
278 next_id: 1,
279 });
280
281 println!("fastapi_rust CRUD API Example");
282 println!("=============================\n");
283
284 let app = App::builder()
285 .post("/users", create_user)
286 .get("/users", list_users)
287 .get("/users/{id}", get_user)
288 .put("/users/{id}", update_user)
289 .delete("/users/{id}", delete_user)
290 .build();
291
292 println!("App created with {} route(s)\n", app.route_count());
293 let client = TestClient::new(app);
294
295 // 1. Create users
296 println!("1. Create users");
297 let resp = client
298 .post("/users")
299 .header("content-type", "application/json")
300 .body(r#"{"name": "Alice", "email": "alice@example.com"}"#)
301 .send();
302 println!(
303 " POST /users -> {} {}",
304 resp.status().as_u16(),
305 resp.text()
306 );
307 if !check_eq(resp.status().as_u16(), 201, "Create user should return 201") {
308 return;
309 }
310
311 let resp = client
312 .post("/users")
313 .header("content-type", "application/json")
314 .body(r#"{"name": "Bob", "email": "bob@example.com"}"#)
315 .send();
316 println!(
317 " POST /users -> {} {}",
318 resp.status().as_u16(),
319 resp.text()
320 );
321 if !check_eq(resp.status().as_u16(), 201, "Create user should return 201") {
322 return;
323 }
324
325 // 2. List users
326 println!("\n2. List all users");
327 let resp = client.get("/users").send();
328 println!(
329 " GET /users -> {} {}",
330 resp.status().as_u16(),
331 resp.text()
332 );
333 if !check_eq(resp.status().as_u16(), 200, "List users should return 200") {
334 return;
335 }
336
337 // 3. Get user by ID
338 println!("\n3. Get user by ID");
339 let resp = client.get("/users/1").send();
340 println!(
341 " GET /users/1 -> {} {}",
342 resp.status().as_u16(),
343 resp.text()
344 );
345 if !check_eq(resp.status().as_u16(), 200, "Get user should return 200") {
346 return;
347 }
348
349 // 4. Get nonexistent user
350 println!("\n4. Get nonexistent user");
351 let resp = client.get("/users/999").send();
352 println!(
353 " GET /users/999 -> {} {}",
354 resp.status().as_u16(),
355 resp.text()
356 );
357 if !check_eq(
358 resp.status().as_u16(),
359 404,
360 "Missing user should return 404",
361 ) {
362 return;
363 }
364
365 // 5. Update user
366 println!("\n5. Update user");
367 let resp = client
368 .put("/users/1")
369 .header("content-type", "application/json")
370 .body(r#"{"name": "Alice Smith", "email": "alice.smith@example.com"}"#)
371 .send();
372 println!(
373 " PUT /users/1 -> {} {}",
374 resp.status().as_u16(),
375 resp.text()
376 );
377 if !check_eq(resp.status().as_u16(), 200, "Update user should return 200") {
378 return;
379 }
380
381 // 6. Validation: empty name
382 println!("\n6. Validation error (empty name)");
383 let resp = client
384 .post("/users")
385 .header("content-type", "application/json")
386 .body(r#"{"name": "", "email": "bad@example.com"}"#)
387 .send();
388 println!(
389 " POST /users -> {} {}",
390 resp.status().as_u16(),
391 resp.text()
392 );
393 if !check_eq(resp.status().as_u16(), 400, "Empty name should return 400") {
394 return;
395 }
396
397 // 7. Validation: invalid email
398 println!("\n7. Validation error (invalid email)");
399 let resp = client
400 .post("/users")
401 .header("content-type", "application/json")
402 .body(r#"{"name": "Charlie", "email": "not-an-email"}"#)
403 .send();
404 println!(
405 " POST /users -> {} {}",
406 resp.status().as_u16(),
407 resp.text()
408 );
409 if !check_eq(
410 resp.status().as_u16(),
411 400,
412 "Invalid email should return 400",
413 ) {
414 return;
415 }
416
417 // 8. Wrong Content-Type
418 println!("\n8. Wrong Content-Type");
419 let resp = client
420 .post("/users")
421 .header("content-type", "text/plain")
422 .body(r#"{"name": "Dan", "email": "dan@example.com"}"#)
423 .send();
424 println!(
425 " POST /users -> {} {}",
426 resp.status().as_u16(),
427 resp.text()
428 );
429 if !check_eq(
430 resp.status().as_u16(),
431 415,
432 "Wrong Content-Type should return 415",
433 ) {
434 return;
435 }
436
437 // 9. Delete user
438 println!("\n9. Delete user");
439 let resp = client.delete("/users/2").send();
440 println!(" DELETE /users/2 -> {}", resp.status().as_u16());
441 if !check_eq(resp.status().as_u16(), 204, "Delete user should return 204") {
442 return;
443 }
444
445 // 10. Verify deletion
446 println!("\n10. Verify deletion");
447 let resp = client.get("/users/2").send();
448 println!(
449 " GET /users/2 -> {} {}",
450 resp.status().as_u16(),
451 resp.text()
452 );
453 if !check_eq(
454 resp.status().as_u16(),
455 404,
456 "Deleted user should return 404",
457 ) {
458 return;
459 }
460
461 let resp = client.get("/users").send();
462 println!(
463 " GET /users -> {} {}",
464 resp.status().as_u16(),
465 resp.text()
466 );
467 if !check_eq(resp.status().as_u16(), 200, "List users should return 200") {
468 return;
469 }
470
471 println!("\nAll CRUD operations passed!");
472}examples/auth_example.rs (line 290)
267fn main() {
268 println!("fastapi_rust Authentication Example");
269 println!("====================================\n");
270
271 // Build the application with public and protected routes
272 let app = App::builder()
273 // Public endpoints - accessible to everyone
274 .get("/public", public_handler)
275 // Login endpoint - returns a token
276 .post("/login", login_handler)
277 // Protected endpoint - requires valid bearer token
278 .get("/protected", protected_handler)
279 .build();
280
281 println!("App created with {} route(s)\n", app.route_count());
282
283 // Create a test client
284 let client = TestClient::new(app);
285
286 // =========================================================================
287 // Test 1: Public endpoint - no auth required
288 // =========================================================================
289 println!("1. Public endpoint - no auth required");
290 let response = client.get("/public").send();
291 println!(
292 " GET /public -> {} {}",
293 response.status().as_u16(),
294 response.status().canonical_reason()
295 );
296 if !check_eq(
297 response.status().as_u16(),
298 200,
299 "GET /public should return 200",
300 ) {
301 return;
302 }
303 if !check(
304 response.text().contains("public endpoint"),
305 "GET /public should include the public endpoint body",
306 ) {
307 return;
308 }
309
310 // =========================================================================
311 // Test 2: Protected endpoint - without token (should get 401)
312 // =========================================================================
313 println!("\n2. Protected endpoint - without token");
314 let response = client.get("/protected").send();
315 println!(
316 " GET /protected -> {} {}",
317 response.status().as_u16(),
318 response.status().canonical_reason()
319 );
320 if !check_eq(
321 response.status().as_u16(),
322 401,
323 "Protected endpoint should return 401 without token",
324 ) {
325 return;
326 }
327
328 // Check for WWW-Authenticate header
329 let has_www_auth = response
330 .headers()
331 .iter()
332 .any(|(name, value)| name == "www-authenticate" && value == b"Bearer");
333 if !check(
334 has_www_auth,
335 "401 response should include WWW-Authenticate: Bearer header",
336 ) {
337 return;
338 }
339
340 // =========================================================================
341 // Test 3: Login endpoint - get a token
342 // =========================================================================
343 println!("\n3. Login endpoint - get a token");
344 let response = client
345 .post("/login")
346 .header("content-type", "application/json")
347 .body(r#"{"username":"test","password":"test123"}"#)
348 .send();
349 println!(
350 " POST /login -> {} {}",
351 response.status().as_u16(),
352 response.status().canonical_reason()
353 );
354 if !check_eq(
355 response.status().as_u16(),
356 200,
357 "POST /login should return 200",
358 ) {
359 return;
360 }
361
362 // Parse the response to get the token
363 let body_text = response.text();
364 let body: serde_json::Value = match serde_json::from_str(body_text) {
365 Ok(body) => body,
366 Err(err) => {
367 eprintln!("Failed to parse login response JSON: {err}");
368 return;
369 }
370 };
371 let Some(bearer_value) = body.get("access_token").and_then(|value| value.as_str()) else {
372 eprintln!("Login response missing access_token");
373 return;
374 };
375 println!(" Bearer value: {bearer_value}");
376 if !check_eq(
377 bearer_value,
378 DEMO_BEARER_VALUE,
379 "Login should return the expected bearer value",
380 ) {
381 return;
382 }
383
384 // =========================================================================
385 // Test 4: Protected endpoint - with valid token (should get 200)
386 // =========================================================================
387 println!("\n4. Protected endpoint - with valid token");
388 let response = client
389 .get("/protected")
390 .header("authorization", format!("Bearer {DEMO_BEARER_VALUE}"))
391 .send();
392 println!(
393 " GET /protected (Authorization: Bearer {}) -> {} {}",
394 DEMO_BEARER_VALUE,
395 response.status().as_u16(),
396 response.status().canonical_reason()
397 );
398 if !check_eq(
399 response.status().as_u16(),
400 200,
401 "Protected endpoint should return 200 with valid token",
402 ) {
403 return;
404 }
405 if !check(
406 response.text().contains("protected resource"),
407 "Protected endpoint should include protected resource body",
408 ) {
409 return;
410 }
411
412 // =========================================================================
413 // Test 5: Protected endpoint - with invalid token (should get 403)
414 // =========================================================================
415 println!("\n5. Protected endpoint - with invalid token");
416 let response = client
417 .get("/protected")
418 .header("authorization", "Bearer wrong_token")
419 .send();
420 println!(
421 " GET /protected (Authorization: Bearer wrong_token) -> {} {}",
422 response.status().as_u16(),
423 response.status().canonical_reason()
424 );
425 if !check_eq(
426 response.status().as_u16(),
427 403,
428 "Protected endpoint should return 403 with invalid token",
429 ) {
430 return;
431 }
432
433 // =========================================================================
434 // Test 6: Protected endpoint - with wrong auth scheme (should get 401)
435 // =========================================================================
436 println!("\n6. Protected endpoint - with wrong auth scheme");
437 let response = client
438 .get("/protected")
439 .header("authorization", "Basic dXNlcjpwYXNz")
440 .send();
441 println!(
442 " GET /protected (Authorization: Basic ...) -> {} {}",
443 response.status().as_u16(),
444 response.status().canonical_reason()
445 );
446 if !check_eq(
447 response.status().as_u16(),
448 401,
449 "Protected endpoint should return 401 with wrong auth scheme",
450 ) {
451 return;
452 }
453
454 // =========================================================================
455 // Test 7: Login with wrong Content-Type (should get 415)
456 // =========================================================================
457 println!("\n7. Login with wrong Content-Type");
458 let response = client
459 .post("/login")
460 .header("content-type", "text/plain")
461 .body("demo=true")
462 .send();
463 println!(
464 " POST /login (Content-Type: text/plain) -> {} {}",
465 response.status().as_u16(),
466 response.status().canonical_reason()
467 );
468 if !check_eq(
469 response.status().as_u16(),
470 415,
471 "Login should return 415 with wrong Content-Type",
472 ) {
473 return;
474 }
475
476 // =========================================================================
477 // Test 8: Token case sensitivity (lowercase 'bearer')
478 // =========================================================================
479 println!("\n8. Token case sensitivity (lowercase 'bearer')");
480 let response = client
481 .get("/protected")
482 .header("authorization", format!("bearer {DEMO_BEARER_VALUE}"))
483 .send();
484 println!(
485 " GET /protected (Authorization: bearer {}) -> {} {}",
486 DEMO_BEARER_VALUE,
487 response.status().as_u16(),
488 response.status().canonical_reason()
489 );
490 if !check_eq(
491 response.status().as_u16(),
492 200,
493 "Bearer scheme should be case-insensitive (lowercase accepted)",
494 ) {
495 return;
496 }
497
498 println!("\nAll authentication tests passed!");
499}Auto Trait Implementations§
impl<'a, H> !Freeze for RequestBuilder<'a, H>
impl<'a, H> !RefUnwindSafe for RequestBuilder<'a, H>
impl<'a, H> Send for RequestBuilder<'a, H>
impl<'a, H> Sync for RequestBuilder<'a, H>
impl<'a, H> Unpin for RequestBuilder<'a, H>
impl<'a, H> !UnwindSafe for RequestBuilder<'a, H>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, _span: NoopSpan) -> Self
fn instrument(self, _span: NoopSpan) -> Self
Instruments this future with a span (no-op when disabled).
Source§fn in_current_span(self) -> Self
fn in_current_span(self) -> Self
Instruments this future with the current span (no-op when disabled).