1use std::error::Error as StdError;
2
3use actix_http::Request;
4use actix_service::IntoServiceFactory;
5use serde::de::DeserializeOwned;
6
7use crate::{
8 body::{self, MessageBody},
9 config::AppConfig,
10 dev::{Service, ServiceFactory},
11 service::ServiceResponse,
12 web::Bytes,
13 Error,
14};
15
16pub async fn init_service<R, S, B, E>(
42 app: R,
43) -> impl Service<Request, Response = ServiceResponse<B>, Error = E>
44where
45 R: IntoServiceFactory<S, Request>,
46 S: ServiceFactory<Request, Config = AppConfig, Response = ServiceResponse<B>, Error = E>,
47 S::InitError: std::fmt::Debug,
48{
49 try_init_service(app)
50 .await
51 .expect("service initialization failed")
52}
53
54pub(crate) async fn try_init_service<R, S, B, E>(
56 app: R,
57) -> Result<impl Service<Request, Response = ServiceResponse<B>, Error = E>, S::InitError>
58where
59 R: IntoServiceFactory<S, Request>,
60 S: ServiceFactory<Request, Config = AppConfig, Response = ServiceResponse<B>, Error = E>,
61 S::InitError: std::fmt::Debug,
62{
63 let srv = app.into_factory();
64 srv.new_service(AppConfig::default()).await
65}
66
67pub async fn call_service<S, R, B, E>(app: &S, req: R) -> S::Response
94where
95 S: Service<R, Response = ServiceResponse<B>, Error = E>,
96 E: std::fmt::Debug,
97{
98 app.call(req)
99 .await
100 .expect("test service call returned error")
101}
102
103pub async fn try_call_service<S, R, B, E>(app: &S, req: R) -> Result<S::Response, E>
105where
106 S: Service<R, Response = ServiceResponse<B>, Error = E>,
107 E: std::fmt::Debug,
108{
109 app.call(req).await
110}
111
112pub async fn call_and_read_body<S, B>(app: &S, req: Request) -> Bytes
144where
145 S: Service<Request, Response = ServiceResponse<B>, Error = Error>,
146 B: MessageBody,
147{
148 let res = call_service(app, req).await;
149 read_body(res).await
150}
151
152#[doc(hidden)]
153#[deprecated(since = "4.0.0", note = "Renamed to `call_and_read_body`.")]
154pub async fn read_response<S, B>(app: &S, req: Request) -> Bytes
155where
156 S: Service<Request, Response = ServiceResponse<B>, Error = Error>,
157 B: MessageBody,
158{
159 let res = call_service(app, req).await;
160 read_body(res).await
161}
162
163pub async fn read_body<B>(res: ServiceResponse<B>) -> Bytes
194where
195 B: MessageBody,
196{
197 try_read_body(res)
198 .await
199 .map_err(Into::<Box<dyn StdError>>::into)
200 .expect("error reading test response body")
201}
202
203pub async fn try_read_body<B>(res: ServiceResponse<B>) -> Result<Bytes, <B as MessageBody>::Error>
205where
206 B: MessageBody,
207{
208 let body = res.into_body();
209 body::to_bytes(body).await
210}
211
212pub async fn read_body_json<T, B>(res: ServiceResponse<B>) -> T
256where
257 B: MessageBody,
258 T: DeserializeOwned,
259{
260 try_read_body_json(res).await.unwrap_or_else(|err| {
261 panic!(
262 "could not deserialize body into a {}\nerr: {}",
263 std::any::type_name::<T>(),
264 err,
265 )
266 })
267}
268
269pub async fn try_read_body_json<T, B>(res: ServiceResponse<B>) -> Result<T, Box<dyn StdError>>
271where
272 B: MessageBody,
273 T: DeserializeOwned,
274{
275 let body = try_read_body(res)
276 .await
277 .map_err(Into::<Box<dyn StdError>>::into)?;
278 serde_json::from_slice(&body).map_err(Into::<Box<dyn StdError>>::into)
279}
280
281pub async fn call_and_read_body_json<S, B, T>(app: &S, req: Request) -> T
323where
324 S: Service<Request, Response = ServiceResponse<B>, Error = Error>,
325 B: MessageBody,
326 T: DeserializeOwned,
327{
328 try_call_and_read_body_json(app, req).await.unwrap()
329}
330
331pub async fn try_call_and_read_body_json<S, B, T>(
333 app: &S,
334 req: Request,
335) -> Result<T, Box<dyn StdError>>
336where
337 S: Service<Request, Response = ServiceResponse<B>, Error = Error>,
338 B: MessageBody,
339 T: DeserializeOwned,
340{
341 let res = try_call_service(app, req)
342 .await
343 .map_err(Into::<Box<dyn StdError>>::into)?;
344 try_read_body_json(res).await
345}
346
347#[doc(hidden)]
348#[deprecated(since = "4.0.0", note = "Renamed to `call_and_read_body_json`.")]
349pub async fn read_response_json<S, B, T>(app: &S, req: Request) -> T
350where
351 S: Service<Request, Response = ServiceResponse<B>, Error = Error>,
352 B: MessageBody,
353 T: DeserializeOwned,
354{
355 call_and_read_body_json(app, req).await
356}
357
358#[cfg(test)]
359mod tests {
360 use serde::{Deserialize, Serialize};
361
362 use super::*;
363 use crate::{
364 dev::ServiceRequest, http::header, test::TestRequest, web, App, HttpMessage, HttpResponse,
365 };
366
367 #[actix_rt::test]
368 async fn test_request_methods() {
369 let app = init_service(
370 App::new().service(
371 web::resource("/index.html")
372 .route(web::put().to(|| HttpResponse::Ok().body("put!")))
373 .route(web::patch().to(|| HttpResponse::Ok().body("patch!")))
374 .route(web::delete().to(|| HttpResponse::Ok().body("delete!"))),
375 ),
376 )
377 .await;
378
379 let put_req = TestRequest::put()
380 .uri("/index.html")
381 .insert_header((header::CONTENT_TYPE, "application/json"))
382 .to_request();
383
384 let result = call_and_read_body(&app, put_req).await;
385 assert_eq!(result, Bytes::from_static(b"put!"));
386
387 let patch_req = TestRequest::patch()
388 .uri("/index.html")
389 .insert_header((header::CONTENT_TYPE, "application/json"))
390 .to_request();
391
392 let result = call_and_read_body(&app, patch_req).await;
393 assert_eq!(result, Bytes::from_static(b"patch!"));
394
395 let delete_req = TestRequest::delete().uri("/index.html").to_request();
396 let result = call_and_read_body(&app, delete_req).await;
397 assert_eq!(result, Bytes::from_static(b"delete!"));
398 }
399
400 #[derive(Serialize, Deserialize, Debug)]
401 pub struct Person {
402 id: String,
403 name: String,
404 }
405
406 #[actix_rt::test]
407 async fn test_response_json() {
408 let app =
409 init_service(App::new().service(web::resource("/people").route(
410 web::post().to(|person: web::Json<Person>| HttpResponse::Ok().json(person)),
411 )))
412 .await;
413
414 let payload = r#"{"id":"12345","name":"User name"}"#.as_bytes();
415
416 let req = TestRequest::post()
417 .uri("/people")
418 .insert_header((header::CONTENT_TYPE, "application/json"))
419 .set_payload(payload)
420 .to_request();
421
422 let result: Person = call_and_read_body_json(&app, req).await;
423 assert_eq!(&result.id, "12345");
424 }
425
426 #[actix_rt::test]
427 async fn test_try_response_json_error() {
428 let app =
429 init_service(App::new().service(web::resource("/people").route(
430 web::post().to(|person: web::Json<Person>| HttpResponse::Ok().json(person)),
431 )))
432 .await;
433
434 let payload = r#"{"id":"12345","name":"User name"}"#.as_bytes();
435
436 let req = TestRequest::post()
437 .uri("/animals") .insert_header((header::CONTENT_TYPE, "application/json"))
439 .set_payload(payload)
440 .to_request();
441
442 let result: Result<Person, Box<dyn StdError>> =
443 try_call_and_read_body_json(&app, req).await;
444 assert!(result.is_err());
445 }
446
447 #[actix_rt::test]
448 async fn test_body_json() {
449 let app =
450 init_service(App::new().service(web::resource("/people").route(
451 web::post().to(|person: web::Json<Person>| HttpResponse::Ok().json(person)),
452 )))
453 .await;
454
455 let payload = r#"{"id":"12345","name":"User name"}"#.as_bytes();
456
457 let res = TestRequest::post()
458 .uri("/people")
459 .insert_header((header::CONTENT_TYPE, "application/json"))
460 .set_payload(payload)
461 .send_request(&app)
462 .await;
463
464 let result: Person = read_body_json(res).await;
465 assert_eq!(&result.name, "User name");
466 }
467
468 #[actix_rt::test]
469 async fn test_try_body_json_error() {
470 let app =
471 init_service(App::new().service(web::resource("/people").route(
472 web::post().to(|person: web::Json<Person>| HttpResponse::Ok().json(person)),
473 )))
474 .await;
475
476 let payload = r#"{"id":12345,"name":"User name"}"#.as_bytes();
478
479 let res = TestRequest::post()
480 .uri("/people")
481 .insert_header((header::CONTENT_TYPE, "application/json"))
482 .set_payload(payload)
483 .send_request(&app)
484 .await;
485
486 let result: Result<Person, Box<dyn StdError>> = try_read_body_json(res).await;
487 assert!(result.is_err());
488 }
489
490 #[actix_rt::test]
491 async fn test_request_response_form() {
492 let app =
493 init_service(App::new().service(web::resource("/people").route(
494 web::post().to(|person: web::Form<Person>| HttpResponse::Ok().json(person)),
495 )))
496 .await;
497
498 let payload = Person {
499 id: "12345".to_string(),
500 name: "User name".to_string(),
501 };
502
503 let req = TestRequest::post()
504 .uri("/people")
505 .set_form(&payload)
506 .to_request();
507
508 assert_eq!(req.content_type(), "application/x-www-form-urlencoded");
509
510 let result: Person = call_and_read_body_json(&app, req).await;
511 assert_eq!(&result.id, "12345");
512 assert_eq!(&result.name, "User name");
513 }
514
515 #[actix_rt::test]
516 async fn test_response() {
517 let app = init_service(
518 App::new().service(
519 web::resource("/index.html")
520 .route(web::post().to(|| HttpResponse::Ok().body("welcome!"))),
521 ),
522 )
523 .await;
524
525 let req = TestRequest::post()
526 .uri("/index.html")
527 .insert_header((header::CONTENT_TYPE, "application/json"))
528 .to_request();
529
530 let result = call_and_read_body(&app, req).await;
531 assert_eq!(result, Bytes::from_static(b"welcome!"));
532 }
533
534 #[actix_rt::test]
535 async fn test_request_response_json() {
536 let app =
537 init_service(App::new().service(web::resource("/people").route(
538 web::post().to(|person: web::Json<Person>| HttpResponse::Ok().json(person)),
539 )))
540 .await;
541
542 let payload = Person {
543 id: "12345".to_string(),
544 name: "User name".to_string(),
545 };
546
547 let req = TestRequest::post()
548 .uri("/people")
549 .set_json(&payload)
550 .to_request();
551
552 assert_eq!(req.content_type(), "application/json");
553
554 let result: Person = call_and_read_body_json(&app, req).await;
555 assert_eq!(&result.id, "12345");
556 assert_eq!(&result.name, "User name");
557 }
558
559 #[actix_rt::test]
560 #[allow(dead_code)]
561 async fn return_opaque_types() {
562 fn test_app() -> App<
563 impl ServiceFactory<
564 ServiceRequest,
565 Config = (),
566 Response = ServiceResponse<impl MessageBody>,
567 Error = crate::Error,
568 InitError = (),
569 >,
570 > {
571 App::new().service(
572 web::resource("/people").route(
573 web::post().to(|person: web::Json<Person>| HttpResponse::Ok().json(person)),
574 ),
575 )
576 }
577
578 async fn test_service(
579 ) -> impl Service<Request, Response = ServiceResponse<impl MessageBody>, Error = crate::Error>
580 {
581 init_service(test_app()).await
582 }
583
584 async fn compile_test(mut req: Vec<Request>) {
585 let svc = test_service().await;
586 call_service(&svc, req.pop().unwrap()).await;
587 call_and_read_body(&svc, req.pop().unwrap()).await;
588 read_body(call_service(&svc, req.pop().unwrap()).await).await;
589 let _: String = call_and_read_body_json(&svc, req.pop().unwrap()).await;
590 let _: String = read_body_json(call_service(&svc, req.pop().unwrap()).await).await;
591 }
592 }
593}