1 use std::cell::{Ref, RefMut}; 2 use std::rc::Rc; 3 use std::{fmt, net}; 4 5 use actix_http::{ 6 body::{AnyBody, MessageBody}, 7 http::{HeaderMap, Method, StatusCode, Uri, Version}, 8 Extensions, HttpMessage, Payload, PayloadStream, RequestHead, Response, ResponseHead, 9 }; 10 use actix_router::{IntoPattern, Path, Resource, ResourceDef, Url}; 11 use actix_service::{IntoServiceFactory, ServiceFactory}; 12 #[cfg(feature = "cookies")] 13 use cookie::{Cookie, ParseError as CookieParseError}; 14 15 use crate::{ 16 config::{AppConfig, AppService}, 17 dev::insert_slash, 18 guard::Guard, 19 info::ConnectionInfo, 20 rmap::ResourceMap, 21 Error, HttpRequest, HttpResponse, 22 }; 23 24 pub trait HttpServiceFactory { register(self, config: &mut AppService)25 fn register(self, config: &mut AppService); 26 } 27 28 impl<T: HttpServiceFactory> HttpServiceFactory for Vec<T> { register(self, config: &mut AppService)29 fn register(self, config: &mut AppService) { 30 self.into_iter() 31 .for_each(|factory| factory.register(config)); 32 } 33 } 34 35 pub(crate) trait AppServiceFactory { register(&mut self, config: &mut AppService)36 fn register(&mut self, config: &mut AppService); 37 } 38 39 pub(crate) struct ServiceFactoryWrapper<T> { 40 factory: Option<T>, 41 } 42 43 impl<T> ServiceFactoryWrapper<T> { new(factory: T) -> Self44 pub fn new(factory: T) -> Self { 45 Self { 46 factory: Some(factory), 47 } 48 } 49 } 50 51 impl<T> AppServiceFactory for ServiceFactoryWrapper<T> 52 where 53 T: HttpServiceFactory, 54 { register(&mut self, config: &mut AppService)55 fn register(&mut self, config: &mut AppService) { 56 if let Some(item) = self.factory.take() { 57 item.register(config) 58 } 59 } 60 } 61 62 /// An service http request 63 /// 64 /// ServiceRequest allows mutable access to request's internal structures 65 pub struct ServiceRequest { 66 req: HttpRequest, 67 payload: Payload, 68 } 69 70 impl ServiceRequest { 71 /// Construct service request new(req: HttpRequest, payload: Payload) -> Self72 pub(crate) fn new(req: HttpRequest, payload: Payload) -> Self { 73 Self { req, payload } 74 } 75 76 /// Deconstruct request into parts 77 #[inline] into_parts(self) -> (HttpRequest, Payload)78 pub fn into_parts(self) -> (HttpRequest, Payload) { 79 (self.req, self.payload) 80 } 81 82 /// Get mutable access to inner `HttpRequest` and `Payload` 83 #[inline] parts_mut(&mut self) -> (&mut HttpRequest, &mut Payload)84 pub fn parts_mut(&mut self) -> (&mut HttpRequest, &mut Payload) { 85 (&mut self.req, &mut self.payload) 86 } 87 88 /// Construct request from parts. from_parts(req: HttpRequest, payload: Payload) -> Self89 pub fn from_parts(req: HttpRequest, payload: Payload) -> Self { 90 Self { req, payload } 91 } 92 93 /// Construct request from request. 94 /// 95 /// The returned `ServiceRequest` would have no payload. from_request(req: HttpRequest) -> Self96 pub fn from_request(req: HttpRequest) -> Self { 97 ServiceRequest { 98 req, 99 payload: Payload::None, 100 } 101 } 102 103 /// Create service response 104 #[inline] into_response<B, R: Into<Response<B>>>(self, res: R) -> ServiceResponse<B>105 pub fn into_response<B, R: Into<Response<B>>>(self, res: R) -> ServiceResponse<B> { 106 let res = HttpResponse::from(res.into()); 107 ServiceResponse::new(self.req, res) 108 } 109 110 /// Create service response for error 111 #[inline] error_response<E: Into<Error>>(self, err: E) -> ServiceResponse112 pub fn error_response<E: Into<Error>>(self, err: E) -> ServiceResponse { 113 let res = HttpResponse::from_error(err.into()); 114 ServiceResponse::new(self.req, res) 115 } 116 117 /// This method returns reference to the request head 118 #[inline] head(&self) -> &RequestHead119 pub fn head(&self) -> &RequestHead { 120 &self.req.head() 121 } 122 123 /// This method returns reference to the request head 124 #[inline] head_mut(&mut self) -> &mut RequestHead125 pub fn head_mut(&mut self) -> &mut RequestHead { 126 self.req.head_mut() 127 } 128 129 /// Request's uri. 130 #[inline] uri(&self) -> &Uri131 pub fn uri(&self) -> &Uri { 132 &self.head().uri 133 } 134 135 /// Read the Request method. 136 #[inline] method(&self) -> &Method137 pub fn method(&self) -> &Method { 138 &self.head().method 139 } 140 141 /// Read the Request Version. 142 #[inline] version(&self) -> Version143 pub fn version(&self) -> Version { 144 self.head().version 145 } 146 147 #[inline] 148 /// Returns request's headers. headers(&self) -> &HeaderMap149 pub fn headers(&self) -> &HeaderMap { 150 &self.head().headers 151 } 152 153 #[inline] 154 /// Returns mutable request's headers. headers_mut(&mut self) -> &mut HeaderMap155 pub fn headers_mut(&mut self) -> &mut HeaderMap { 156 &mut self.head_mut().headers 157 } 158 159 /// The target path of this Request. 160 #[inline] path(&self) -> &str161 pub fn path(&self) -> &str { 162 self.head().uri.path() 163 } 164 165 /// The query string in the URL. 166 /// 167 /// E.g., id=10 168 #[inline] query_string(&self) -> &str169 pub fn query_string(&self) -> &str { 170 self.uri().query().unwrap_or_default() 171 } 172 173 /// Peer socket address. 174 /// 175 /// Peer address is the directly connected peer's socket address. If a proxy is used in front of 176 /// the Actix Web server, then it would be address of this proxy. 177 /// 178 /// To get client connection information `ConnectionInfo` should be used. 179 /// 180 /// Will only return None when called in unit tests. 181 #[inline] peer_addr(&self) -> Option<net::SocketAddr>182 pub fn peer_addr(&self) -> Option<net::SocketAddr> { 183 self.head().peer_addr 184 } 185 186 /// Get *ConnectionInfo* for the current request. 187 #[inline] connection_info(&self) -> Ref<'_, ConnectionInfo>188 pub fn connection_info(&self) -> Ref<'_, ConnectionInfo> { 189 ConnectionInfo::get(self.head(), &*self.app_config()) 190 } 191 192 /// Get a reference to the Path parameters. 193 /// 194 /// Params is a container for url parameters. 195 /// A variable segment is specified in the form `{identifier}`, 196 /// where the identifier can be used later in a request handler to 197 /// access the matched value for that segment. 198 #[inline] match_info(&self) -> &Path<Url>199 pub fn match_info(&self) -> &Path<Url> { 200 self.req.match_info() 201 } 202 203 /// Counterpart to [`HttpRequest::match_name`](super::HttpRequest::match_name()). 204 #[inline] match_name(&self) -> Option<&str>205 pub fn match_name(&self) -> Option<&str> { 206 self.req.match_name() 207 } 208 209 /// Counterpart to [`HttpRequest::match_pattern`](super::HttpRequest::match_pattern()). 210 #[inline] match_pattern(&self) -> Option<String>211 pub fn match_pattern(&self) -> Option<String> { 212 self.req.match_pattern() 213 } 214 215 #[inline] 216 /// Get a mutable reference to the Path parameters. match_info_mut(&mut self) -> &mut Path<Url>217 pub fn match_info_mut(&mut self) -> &mut Path<Url> { 218 self.req.match_info_mut() 219 } 220 221 #[inline] 222 /// Get a reference to a `ResourceMap` of current application. resource_map(&self) -> &ResourceMap223 pub fn resource_map(&self) -> &ResourceMap { 224 self.req.resource_map() 225 } 226 227 /// Service configuration 228 #[inline] app_config(&self) -> &AppConfig229 pub fn app_config(&self) -> &AppConfig { 230 self.req.app_config() 231 } 232 233 /// Counterpart to [`HttpRequest::app_data`](super::HttpRequest::app_data()). app_data<T: 'static>(&self) -> Option<&T>234 pub fn app_data<T: 'static>(&self) -> Option<&T> { 235 for container in self.req.inner.app_data.iter().rev() { 236 if let Some(data) = container.get::<T>() { 237 return Some(data); 238 } 239 } 240 241 None 242 } 243 244 #[cfg(feature = "cookies")] cookies(&self) -> Result<Ref<'_, Vec<Cookie<'static>>>, CookieParseError>245 pub fn cookies(&self) -> Result<Ref<'_, Vec<Cookie<'static>>>, CookieParseError> { 246 self.req.cookies() 247 } 248 249 /// Return request cookie. 250 #[cfg(feature = "cookies")] cookie(&self, name: &str) -> Option<Cookie<'static>>251 pub fn cookie(&self, name: &str) -> Option<Cookie<'static>> { 252 self.req.cookie(name) 253 } 254 255 /// Set request payload. set_payload(&mut self, payload: Payload)256 pub fn set_payload(&mut self, payload: Payload) { 257 self.payload = payload; 258 } 259 260 /// Add data container to request's resolution set. 261 /// 262 /// In middleware, prefer [`extensions_mut`](ServiceRequest::extensions_mut) for request-local 263 /// data since it is assumed that the same app data is presented for every request. add_data_container(&mut self, extensions: Rc<Extensions>)264 pub fn add_data_container(&mut self, extensions: Rc<Extensions>) { 265 Rc::get_mut(&mut (self.req).inner) 266 .unwrap() 267 .app_data 268 .push(extensions); 269 } 270 } 271 272 impl Resource<Url> for ServiceRequest { resource_path(&mut self) -> &mut Path<Url>273 fn resource_path(&mut self) -> &mut Path<Url> { 274 self.match_info_mut() 275 } 276 } 277 278 impl HttpMessage for ServiceRequest { 279 type Stream = PayloadStream; 280 281 #[inline] 282 /// Returns Request's headers. headers(&self) -> &HeaderMap283 fn headers(&self) -> &HeaderMap { 284 &self.head().headers 285 } 286 287 /// Request extensions 288 #[inline] extensions(&self) -> Ref<'_, Extensions>289 fn extensions(&self) -> Ref<'_, Extensions> { 290 self.req.extensions() 291 } 292 293 /// Mutable reference to a the request's extensions 294 #[inline] extensions_mut(&self) -> RefMut<'_, Extensions>295 fn extensions_mut(&self) -> RefMut<'_, Extensions> { 296 self.req.extensions_mut() 297 } 298 299 #[inline] take_payload(&mut self) -> Payload<Self::Stream>300 fn take_payload(&mut self) -> Payload<Self::Stream> { 301 self.payload.take() 302 } 303 } 304 305 impl fmt::Debug for ServiceRequest { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result306 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 307 writeln!( 308 f, 309 "\nServiceRequest {:?} {}:{}", 310 self.head().version, 311 self.head().method, 312 self.path() 313 )?; 314 if !self.query_string().is_empty() { 315 writeln!(f, " query: ?{:?}", self.query_string())?; 316 } 317 if !self.match_info().is_empty() { 318 writeln!(f, " params: {:?}", self.match_info())?; 319 } 320 writeln!(f, " headers:")?; 321 for (key, val) in self.headers().iter() { 322 writeln!(f, " {:?}: {:?}", key, val)?; 323 } 324 Ok(()) 325 } 326 } 327 328 pub struct ServiceResponse<B = AnyBody> { 329 request: HttpRequest, 330 response: HttpResponse<B>, 331 } 332 333 impl ServiceResponse<AnyBody> { 334 /// Create service response from the error from_err<E: Into<Error>>(err: E, request: HttpRequest) -> Self335 pub fn from_err<E: Into<Error>>(err: E, request: HttpRequest) -> Self { 336 let response = HttpResponse::from_error(err); 337 ServiceResponse { request, response } 338 } 339 } 340 341 impl<B> ServiceResponse<B> { 342 /// Create service response instance new(request: HttpRequest, response: HttpResponse<B>) -> Self343 pub fn new(request: HttpRequest, response: HttpResponse<B>) -> Self { 344 ServiceResponse { request, response } 345 } 346 347 /// Create service response for error 348 #[inline] error_response<E: Into<Error>>(self, err: E) -> ServiceResponse349 pub fn error_response<E: Into<Error>>(self, err: E) -> ServiceResponse { 350 ServiceResponse::from_err(err, self.request) 351 } 352 353 /// Create service response 354 #[inline] into_response<B1>(self, response: HttpResponse<B1>) -> ServiceResponse<B1>355 pub fn into_response<B1>(self, response: HttpResponse<B1>) -> ServiceResponse<B1> { 356 ServiceResponse::new(self.request, response) 357 } 358 359 /// Get reference to original request 360 #[inline] request(&self) -> &HttpRequest361 pub fn request(&self) -> &HttpRequest { 362 &self.request 363 } 364 365 /// Get reference to response 366 #[inline] response(&self) -> &HttpResponse<B>367 pub fn response(&self) -> &HttpResponse<B> { 368 &self.response 369 } 370 371 /// Get mutable reference to response 372 #[inline] response_mut(&mut self) -> &mut HttpResponse<B>373 pub fn response_mut(&mut self) -> &mut HttpResponse<B> { 374 &mut self.response 375 } 376 377 /// Get the response status code 378 #[inline] status(&self) -> StatusCode379 pub fn status(&self) -> StatusCode { 380 self.response.status() 381 } 382 383 #[inline] 384 /// Returns response's headers. headers(&self) -> &HeaderMap385 pub fn headers(&self) -> &HeaderMap { 386 self.response.headers() 387 } 388 389 /// Returns mutable response's headers. 390 #[inline] headers_mut(&mut self) -> &mut HeaderMap391 pub fn headers_mut(&mut self) -> &mut HeaderMap { 392 self.response.headers_mut() 393 } 394 395 /// Execute closure and in case of error convert it to response. checked_expr<F, E>(mut self, f: F) -> Result<Self, Error> where F: FnOnce(&mut Self) -> Result<(), E>, E: Into<Error>,396 pub fn checked_expr<F, E>(mut self, f: F) -> Result<Self, Error> 397 where 398 F: FnOnce(&mut Self) -> Result<(), E>, 399 E: Into<Error>, 400 { 401 f(&mut self).map_err(Into::into)?; 402 Ok(self) 403 } 404 405 /// Extract response body into_body(self) -> B406 pub fn into_body(self) -> B { 407 self.response.into_body() 408 } 409 } 410 411 impl<B> ServiceResponse<B> { 412 /// Set a new body map_body<F, B2>(self, f: F) -> ServiceResponse<B2> where F: FnOnce(&mut ResponseHead, B) -> B2,413 pub fn map_body<F, B2>(self, f: F) -> ServiceResponse<B2> 414 where 415 F: FnOnce(&mut ResponseHead, B) -> B2, 416 { 417 let response = self.response.map_body(f); 418 419 ServiceResponse { 420 response, 421 request: self.request, 422 } 423 } 424 } 425 426 impl<B> From<ServiceResponse<B>> for HttpResponse<B> { from(res: ServiceResponse<B>) -> HttpResponse<B>427 fn from(res: ServiceResponse<B>) -> HttpResponse<B> { 428 res.response 429 } 430 } 431 432 impl<B> From<ServiceResponse<B>> for Response<B> { from(res: ServiceResponse<B>) -> Response<B>433 fn from(res: ServiceResponse<B>) -> Response<B> { 434 res.response.into() 435 } 436 } 437 438 impl<B> fmt::Debug for ServiceResponse<B> 439 where 440 B: MessageBody, 441 B::Error: Into<Error>, 442 { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result443 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 444 let res = writeln!( 445 f, 446 "\nServiceResponse {:?} {}{}", 447 self.response.head().version, 448 self.response.head().status, 449 self.response.head().reason.unwrap_or(""), 450 ); 451 let _ = writeln!(f, " headers:"); 452 for (key, val) in self.response.head().headers.iter() { 453 let _ = writeln!(f, " {:?}: {:?}", key, val); 454 } 455 let _ = writeln!(f, " body: {:?}", self.response.body().size()); 456 res 457 } 458 } 459 460 pub struct WebService { 461 rdef: Vec<String>, 462 name: Option<String>, 463 guards: Vec<Box<dyn Guard>>, 464 } 465 466 impl WebService { 467 /// Create new `WebService` instance. new<T: IntoPattern>(path: T) -> Self468 pub fn new<T: IntoPattern>(path: T) -> Self { 469 WebService { 470 rdef: path.patterns(), 471 name: None, 472 guards: Vec::new(), 473 } 474 } 475 476 /// Set service name. 477 /// 478 /// Name is used for url generation. name(mut self, name: &str) -> Self479 pub fn name(mut self, name: &str) -> Self { 480 self.name = Some(name.to_string()); 481 self 482 } 483 484 /// Add match guard to a web service. 485 /// 486 /// ``` 487 /// use actix_web::{web, guard, dev, App, Error, HttpResponse}; 488 /// 489 /// async fn index(req: dev::ServiceRequest) -> Result<dev::ServiceResponse, Error> { 490 /// Ok(req.into_response(HttpResponse::Ok().finish())) 491 /// } 492 /// 493 /// fn main() { 494 /// let app = App::new() 495 /// .service( 496 /// web::service("/app") 497 /// .guard(guard::Header("content-type", "text/plain")) 498 /// .finish(index) 499 /// ); 500 /// } 501 /// ``` guard<G: Guard + 'static>(mut self, guard: G) -> Self502 pub fn guard<G: Guard + 'static>(mut self, guard: G) -> Self { 503 self.guards.push(Box::new(guard)); 504 self 505 } 506 507 /// Set a service factory implementation and generate web service. finish<T, F>(self, service: F) -> impl HttpServiceFactory where F: IntoServiceFactory<T, ServiceRequest>, T: ServiceFactory< ServiceRequest, Config = (), Response = ServiceResponse, Error = Error, InitError = (), > + 'static,508 pub fn finish<T, F>(self, service: F) -> impl HttpServiceFactory 509 where 510 F: IntoServiceFactory<T, ServiceRequest>, 511 T: ServiceFactory< 512 ServiceRequest, 513 Config = (), 514 Response = ServiceResponse, 515 Error = Error, 516 InitError = (), 517 > + 'static, 518 { 519 WebServiceImpl { 520 srv: service.into_factory(), 521 rdef: self.rdef, 522 name: self.name, 523 guards: self.guards, 524 } 525 } 526 } 527 528 struct WebServiceImpl<T> { 529 srv: T, 530 rdef: Vec<String>, 531 name: Option<String>, 532 guards: Vec<Box<dyn Guard>>, 533 } 534 535 impl<T> HttpServiceFactory for WebServiceImpl<T> 536 where 537 T: ServiceFactory< 538 ServiceRequest, 539 Config = (), 540 Response = ServiceResponse, 541 Error = Error, 542 InitError = (), 543 > + 'static, 544 { register(mut self, config: &mut AppService)545 fn register(mut self, config: &mut AppService) { 546 let guards = if self.guards.is_empty() { 547 None 548 } else { 549 Some(std::mem::take(&mut self.guards)) 550 }; 551 552 let mut rdef = if config.is_root() || !self.rdef.is_empty() { 553 ResourceDef::new(insert_slash(self.rdef)) 554 } else { 555 ResourceDef::new(self.rdef) 556 }; 557 if let Some(ref name) = self.name { 558 *rdef.name_mut() = name.clone(); 559 } 560 config.register_service(rdef, guards, self.srv, None) 561 } 562 } 563 564 /// Macro helping register different types of services at the sametime. 565 /// 566 /// The service type must be implementing [`HttpServiceFactory`](self::HttpServiceFactory) trait. 567 /// 568 /// The max number of services can be grouped together is 12. 569 /// 570 /// # Examples 571 /// 572 /// ``` 573 /// use actix_web::{services, web, App}; 574 /// 575 /// let services = services![ 576 /// web::resource("/test2").to(|| async { "test2" }), 577 /// web::scope("/test3").route("/", web::get().to(|| async { "test3" })) 578 /// ]; 579 /// 580 /// let app = App::new().service(services); 581 /// 582 /// // services macro just convert multiple services to a tuple. 583 /// // below would also work without importing the macro. 584 /// let app = App::new().service(( 585 /// web::resource("/test2").to(|| async { "test2" }), 586 /// web::scope("/test3").route("/", web::get().to(|| async { "test3" })) 587 /// )); 588 /// ``` 589 #[macro_export] 590 macro_rules! services { 591 ($($x:expr),+ $(,)?) => { 592 ($($x,)+) 593 } 594 } 595 596 /// HttpServiceFactory trait impl for tuples 597 macro_rules! service_tuple ({ $($T:ident)+ } => { 598 impl<$($T: HttpServiceFactory),+> HttpServiceFactory for ($($T,)+) { 599 #[allow(non_snake_case)] 600 fn register(self, config: &mut AppService) { 601 let ($($T,)*) = self; 602 $($T.register(config);)+ 603 } 604 } 605 }); 606 607 service_tuple! { A } 608 service_tuple! { A B } 609 service_tuple! { A B C } 610 service_tuple! { A B C D } 611 service_tuple! { A B C D E } 612 service_tuple! { A B C D E F } 613 service_tuple! { A B C D E F G } 614 service_tuple! { A B C D E F G H } 615 service_tuple! { A B C D E F G H I } 616 service_tuple! { A B C D E F G H I J } 617 service_tuple! { A B C D E F G H I J K } 618 service_tuple! { A B C D E F G H I J K L } 619 620 #[cfg(test)] 621 mod tests { 622 use super::*; 623 use crate::test::{init_service, TestRequest}; 624 use crate::{guard, http, web, App, HttpResponse}; 625 use actix_service::Service; 626 use actix_utils::future::ok; 627 628 #[actix_rt::test] test_service()629 async fn test_service() { 630 let srv = init_service( 631 App::new().service(web::service("/test").name("test").finish( 632 |req: ServiceRequest| ok(req.into_response(HttpResponse::Ok().finish())), 633 )), 634 ) 635 .await; 636 let req = TestRequest::with_uri("/test").to_request(); 637 let resp = srv.call(req).await.unwrap(); 638 assert_eq!(resp.status(), http::StatusCode::OK); 639 640 let srv = init_service( 641 App::new().service(web::service("/test").guard(guard::Get()).finish( 642 |req: ServiceRequest| ok(req.into_response(HttpResponse::Ok().finish())), 643 )), 644 ) 645 .await; 646 let req = TestRequest::with_uri("/test") 647 .method(http::Method::PUT) 648 .to_request(); 649 let resp = srv.call(req).await.unwrap(); 650 assert_eq!(resp.status(), http::StatusCode::NOT_FOUND); 651 } 652 653 // allow deprecated App::data 654 #[allow(deprecated)] 655 #[actix_rt::test] test_service_data()656 async fn test_service_data() { 657 let srv = 658 init_service( 659 App::new() 660 .data(42u32) 661 .service(web::service("/test").name("test").finish( 662 |req: ServiceRequest| { 663 assert_eq!(req.app_data::<web::Data<u32>>().unwrap().as_ref(), &42); 664 ok(req.into_response(HttpResponse::Ok().finish())) 665 }, 666 )), 667 ) 668 .await; 669 let req = TestRequest::with_uri("/test").to_request(); 670 let resp = srv.call(req).await.unwrap(); 671 assert_eq!(resp.status(), http::StatusCode::OK); 672 } 673 674 #[test] test_fmt_debug()675 fn test_fmt_debug() { 676 let req = TestRequest::get() 677 .uri("/index.html?test=1") 678 .insert_header(("x-test", "111")) 679 .to_srv_request(); 680 let s = format!("{:?}", req); 681 assert!(s.contains("ServiceRequest")); 682 assert!(s.contains("test=1")); 683 assert!(s.contains("x-test")); 684 685 let res = HttpResponse::Ok().insert_header(("x-test", "111")).finish(); 686 let res = TestRequest::post() 687 .uri("/index.html?test=1") 688 .to_srv_response(res); 689 690 let s = format!("{:?}", res); 691 assert!(s.contains("ServiceResponse")); 692 assert!(s.contains("x-test")); 693 } 694 695 #[actix_rt::test] test_services_macro()696 async fn test_services_macro() { 697 let scoped = services![ 698 web::service("/scoped_test1").name("scoped_test1").finish( 699 |req: ServiceRequest| async { 700 Ok(req.into_response(HttpResponse::Ok().finish())) 701 } 702 ), 703 web::resource("/scoped_test2").to(|| async { "test2" }), 704 ]; 705 706 let services = services![ 707 web::service("/test1") 708 .name("test") 709 .finish(|req: ServiceRequest| async { 710 Ok(req.into_response(HttpResponse::Ok().finish())) 711 }), 712 web::resource("/test2").to(|| async { "test2" }), 713 web::scope("/test3").service(scoped) 714 ]; 715 716 let srv = init_service(App::new().service(services)).await; 717 718 let req = TestRequest::with_uri("/test1").to_request(); 719 let resp = srv.call(req).await.unwrap(); 720 assert_eq!(resp.status(), http::StatusCode::OK); 721 722 let req = TestRequest::with_uri("/test2").to_request(); 723 let resp = srv.call(req).await.unwrap(); 724 assert_eq!(resp.status(), http::StatusCode::OK); 725 726 let req = TestRequest::with_uri("/test3/scoped_test1").to_request(); 727 let resp = srv.call(req).await.unwrap(); 728 assert_eq!(resp.status(), http::StatusCode::OK); 729 730 let req = TestRequest::with_uri("/test3/scoped_test2").to_request(); 731 let resp = srv.call(req).await.unwrap(); 732 assert_eq!(resp.status(), http::StatusCode::OK); 733 } 734 735 #[actix_rt::test] test_services_vec()736 async fn test_services_vec() { 737 let services = vec![ 738 web::resource("/test1").to(|| async { "test1" }), 739 web::resource("/test2").to(|| async { "test2" }), 740 ]; 741 742 let scoped = vec![ 743 web::resource("/scoped_test1").to(|| async { "test1" }), 744 web::resource("/scoped_test2").to(|| async { "test2" }), 745 ]; 746 747 let srv = init_service( 748 App::new() 749 .service(services) 750 .service(web::scope("/test3").service(scoped)), 751 ) 752 .await; 753 754 let req = TestRequest::with_uri("/test1").to_request(); 755 let resp = srv.call(req).await.unwrap(); 756 assert_eq!(resp.status(), http::StatusCode::OK); 757 758 let req = TestRequest::with_uri("/test2").to_request(); 759 let resp = srv.call(req).await.unwrap(); 760 assert_eq!(resp.status(), http::StatusCode::OK); 761 762 let req = TestRequest::with_uri("/test3/scoped_test1").to_request(); 763 let resp = srv.call(req).await.unwrap(); 764 assert_eq!(resp.status(), http::StatusCode::OK); 765 766 let req = TestRequest::with_uri("/test3/scoped_test2").to_request(); 767 let resp = srv.call(req).await.unwrap(); 768 assert_eq!(resp.status(), http::StatusCode::OK); 769 } 770 } 771