mirror of
https://github.com/tokio-rs/axum.git
synced 2025-01-01 08:56:15 +01:00
not quite working
This commit is contained in:
parent
00737c4e0a
commit
e156bc40e1
3 changed files with 147 additions and 296 deletions
|
@ -284,6 +284,7 @@ define_rejection! {
|
||||||
pub struct MissingRouteParams(());
|
pub struct MissingRouteParams(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct UrlParamsMap(HashMap<String, String>);
|
pub struct UrlParamsMap(HashMap<String, String>);
|
||||||
|
|
||||||
impl UrlParamsMap {
|
impl UrlParamsMap {
|
||||||
|
|
243
src/routing.rs
243
src/routing.rs
|
@ -66,7 +66,7 @@ macro_rules! define_route_at_methods {
|
||||||
where
|
where
|
||||||
S: Service<Request<Body>, Response = Response<B>, Error = Infallible> + Clone,
|
S: Service<Request<Body>, Response = Response<B>, Error = Infallible> + Clone,
|
||||||
{
|
{
|
||||||
self.add_route_service(service, MethodOrPrefix::Method(Method::$method))
|
self.add_route_service(service, Method::$method)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -103,19 +103,6 @@ impl<R> RouteAt<R> {
|
||||||
define_route_at_methods!(RouteAt: connect, connect_service, CONNECT);
|
define_route_at_methods!(RouteAt: connect, connect_service, CONNECT);
|
||||||
define_route_at_methods!(RouteAt: trace, trace_service, TRACE);
|
define_route_at_methods!(RouteAt: trace, trace_service, TRACE);
|
||||||
|
|
||||||
pub fn nest<T>(
|
|
||||||
self,
|
|
||||||
other: RouteBuilder<T>,
|
|
||||||
) -> RouteBuilder<Or<StripPrefix<IntoService<T>>, R>> {
|
|
||||||
let route_spec = self.route_spec.clone();
|
|
||||||
let other = StripPrefix::new(other.into_service(), route_spec.clone());
|
|
||||||
|
|
||||||
self.add_route_service_with_spec(
|
|
||||||
other,
|
|
||||||
RouteSpec::new(MethodOrPrefix::Prefix(route_spec.clone()), route_spec),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_route<H, B, T>(
|
fn add_route<H, B, T>(
|
||||||
self,
|
self,
|
||||||
handler: H,
|
handler: H,
|
||||||
|
@ -124,16 +111,12 @@ impl<R> RouteAt<R> {
|
||||||
where
|
where
|
||||||
H: Handler<B, T>,
|
H: Handler<B, T>,
|
||||||
{
|
{
|
||||||
self.add_route_service(HandlerSvc::new(handler), MethodOrPrefix::Method(method))
|
self.add_route_service(HandlerSvc::new(handler), method)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_route_service<S>(
|
fn add_route_service<S>(self, service: S, method: Method) -> RouteBuilder<Or<S, R>> {
|
||||||
self,
|
|
||||||
service: S,
|
|
||||||
method_or_prefix: MethodOrPrefix,
|
|
||||||
) -> RouteBuilder<Or<S, R>> {
|
|
||||||
let route_spec = self.route_spec.clone();
|
let route_spec = self.route_spec.clone();
|
||||||
self.add_route_service_with_spec(service, RouteSpec::new(method_or_prefix, route_spec))
|
self.add_route_service_with_spec(service, RouteSpec::new(method, route_spec))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_route_service_with_spec<S>(
|
fn add_route_service_with_spec<S>(
|
||||||
|
@ -276,59 +259,25 @@ where
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct RouteSpec {
|
struct RouteSpec {
|
||||||
method_or_prefix: MethodOrPrefix,
|
method: Method,
|
||||||
spec: Bytes,
|
spec: Bytes,
|
||||||
length_match: LengthMatch,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
enum MethodOrPrefix {
|
|
||||||
AnyMethod,
|
|
||||||
Method(Method),
|
|
||||||
Prefix(Bytes),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
enum LengthMatch {
|
|
||||||
Exact,
|
|
||||||
UriCanBeLonger,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RouteSpec {
|
impl RouteSpec {
|
||||||
fn new(method_or_prefix: MethodOrPrefix, spec: impl Into<Bytes>) -> Self {
|
fn new(method: Method, spec: impl Into<Bytes>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
method_or_prefix,
|
method,
|
||||||
spec: spec.into(),
|
spec: spec.into(),
|
||||||
length_match: LengthMatch::Exact,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn length_match(mut self, length_match: LengthMatch) -> Self {
|
|
||||||
self.length_match = length_match;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RouteSpec {
|
impl RouteSpec {
|
||||||
fn matches<B>(&self, req: &Request<B>) -> Option<Vec<(String, String)>> {
|
fn matches<B>(&self, req: &Request<B>) -> Option<Vec<(String, String)>> {
|
||||||
// println!("route spec comparing `{:?}` and `{:?}`", self, req.uri());
|
// TODO(david): perform this matching outside
|
||||||
|
if req.method() != self.method {
|
||||||
match &self.method_or_prefix {
|
|
||||||
MethodOrPrefix::Method(method) => {
|
|
||||||
if req.method() != method {
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
MethodOrPrefix::AnyMethod => {}
|
|
||||||
MethodOrPrefix::Prefix(prefix) => {
|
|
||||||
let route_spec = RouteSpec::new(MethodOrPrefix::AnyMethod, prefix.clone())
|
|
||||||
.length_match(LengthMatch::UriCanBeLonger);
|
|
||||||
|
|
||||||
if let Some(params) = route_spec.matches(req) {
|
|
||||||
return Some(params);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let spec_parts = self.spec.split(|b| *b == b'/');
|
let spec_parts = self.spec.split(|b| *b == b'/');
|
||||||
|
|
||||||
|
@ -340,11 +289,6 @@ impl RouteSpec {
|
||||||
for pair in spec_parts.zip_longest(path_parts) {
|
for pair in spec_parts.zip_longest(path_parts) {
|
||||||
match pair {
|
match pair {
|
||||||
EitherOrBoth::Both(spec, path) => {
|
EitherOrBoth::Both(spec, path) => {
|
||||||
println!(
|
|
||||||
"both: ({:?}, {:?})",
|
|
||||||
str::from_utf8(spec).unwrap(),
|
|
||||||
str::from_utf8(path).unwrap()
|
|
||||||
);
|
|
||||||
if let Some(key) = spec.strip_prefix(b":") {
|
if let Some(key) = spec.strip_prefix(b":") {
|
||||||
let key = str::from_utf8(key).unwrap().to_string();
|
let key = str::from_utf8(key).unwrap().to_string();
|
||||||
if let Ok(value) = std::str::from_utf8(path) {
|
if let Ok(value) = std::str::from_utf8(path) {
|
||||||
|
@ -356,21 +300,9 @@ impl RouteSpec {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EitherOrBoth::Left(spec) => {
|
EitherOrBoth::Left(_) | EitherOrBoth::Right(_) => {
|
||||||
println!("left: {:?}", str::from_utf8(spec).unwrap());
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
EitherOrBoth::Right(path) => {
|
|
||||||
println!("right: {:?}", str::from_utf8(path).unwrap());
|
|
||||||
match self.length_match {
|
|
||||||
LengthMatch::Exact => {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
LengthMatch::UriCanBeLonger => {
|
|
||||||
return Some(params);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -419,7 +351,7 @@ where
|
||||||
|
|
||||||
self.handler_ready = false;
|
self.handler_ready = false;
|
||||||
|
|
||||||
req.extensions_mut().insert(Some(UrlParams(params)));
|
insert_url_params(&mut req, params);
|
||||||
|
|
||||||
future::Either::Left(BoxResponseBody(self.service.call(req)))
|
future::Either::Left(BoxResponseBody(self.service.call(req)))
|
||||||
} else {
|
} else {
|
||||||
|
@ -436,6 +368,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub(crate) struct UrlParams(pub(crate) Vec<(String, String)>);
|
pub(crate) struct UrlParams(pub(crate) Vec<(String, String)>);
|
||||||
|
|
||||||
#[pin_project]
|
#[pin_project]
|
||||||
|
@ -583,92 +516,13 @@ where
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
fn insert_url_params<B>(req: &mut Request<B>, params: Vec<(String, String)>) {
|
||||||
pub struct StripPrefix<S> {
|
if let Some(current) = req.extensions_mut().get_mut::<Option<UrlParams>>() {
|
||||||
inner: S,
|
let mut current = current.take().unwrap();
|
||||||
prefix: Bytes,
|
current.0.extend(params);
|
||||||
}
|
req.extensions_mut().insert(Some(current));
|
||||||
|
|
||||||
impl<S> StripPrefix<S> {
|
|
||||||
fn new(inner: S, prefix: impl Into<Bytes>) -> Self {
|
|
||||||
Self {
|
|
||||||
inner,
|
|
||||||
prefix: prefix.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S, B> Service<Request<B>> for StripPrefix<S>
|
|
||||||
where
|
|
||||||
S: Service<Request<B>>,
|
|
||||||
{
|
|
||||||
type Response = S::Response;
|
|
||||||
type Error = S::Error;
|
|
||||||
type Future = S::Future;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
|
||||||
self.inner.poll_ready(cx)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn call(&mut self, req: Request<B>) -> Self::Future {
|
|
||||||
use http::uri::{PathAndQuery, Uri};
|
|
||||||
use std::convert::TryFrom;
|
|
||||||
|
|
||||||
println!("strip prefix {:?} of {:?}", self.prefix, req.uri().path());
|
|
||||||
|
|
||||||
let (mut request_parts, body) = req.into_parts();
|
|
||||||
let mut uri_parts = request_parts.uri.into_parts();
|
|
||||||
|
|
||||||
enum Control<T> {
|
|
||||||
Continue(T),
|
|
||||||
Break,
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(path_and_query) = &uri_parts.path_and_query {
|
|
||||||
let path = path_and_query.path();
|
|
||||||
|
|
||||||
let prefix = str::from_utf8(&self.prefix).unwrap();
|
|
||||||
|
|
||||||
let iter = path
|
|
||||||
.split('/')
|
|
||||||
.zip_longest(prefix.split('/'))
|
|
||||||
.map(|pair| match pair {
|
|
||||||
EitherOrBoth::Both(path, prefix) => {
|
|
||||||
if prefix.starts_with(':') || path == prefix {
|
|
||||||
Control::Continue(path)
|
|
||||||
} else {
|
} else {
|
||||||
Control::Break
|
req.extensions_mut().insert(Some(UrlParams(params)));
|
||||||
}
|
|
||||||
}
|
|
||||||
EitherOrBoth::Left(_) | EitherOrBoth::Right(_) => Control::Break,
|
|
||||||
})
|
|
||||||
.take_while(|item| matches!(item, Control::Continue(_)))
|
|
||||||
.map(|item| {
|
|
||||||
if let Control::Continue(item) = item {
|
|
||||||
item
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
let prefix_with_captures_updated =
|
|
||||||
Itertools::intersperse(iter, "/").collect::<String>();
|
|
||||||
|
|
||||||
if let Some(path_without_prefix) = path.strip_prefix(&prefix_with_captures_updated) {
|
|
||||||
let new = if let Some(query) = path_and_query.query() {
|
|
||||||
PathAndQuery::try_from(format!("{}?{}", &path_without_prefix, query)).unwrap()
|
|
||||||
} else {
|
|
||||||
PathAndQuery::try_from(path_without_prefix).unwrap()
|
|
||||||
};
|
|
||||||
uri_parts.path_and_query = Some(new);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
request_parts.uri = Uri::from_parts(uri_parts).unwrap();
|
|
||||||
|
|
||||||
let req = Request::from_parts(request_parts, body);
|
|
||||||
|
|
||||||
self.inner.call(req)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -709,7 +563,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assert_match(route_spec: (Method, &'static str), req_spec: (Method, &'static str)) {
|
fn assert_match(route_spec: (Method, &'static str), req_spec: (Method, &'static str)) {
|
||||||
let route = RouteSpec::new(MethodOrPrefix::Method(route_spec.0.clone()), route_spec.1);
|
let route = RouteSpec::new(route_spec.0.clone(), route_spec.1);
|
||||||
let req = Request::builder()
|
let req = Request::builder()
|
||||||
.method(req_spec.0.clone())
|
.method(req_spec.0.clone())
|
||||||
.uri(req_spec.1)
|
.uri(req_spec.1)
|
||||||
|
@ -721,13 +575,13 @@ mod tests {
|
||||||
"`{} {}` doesn't match `{:?} {}`",
|
"`{} {}` doesn't match `{:?} {}`",
|
||||||
req.method(),
|
req.method(),
|
||||||
req.uri().path(),
|
req.uri().path(),
|
||||||
route.method_or_prefix,
|
route.method,
|
||||||
str::from_utf8(&route.spec).unwrap(),
|
str::from_utf8(&route.spec).unwrap(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn refute_match(route_spec: (Method, &'static str), req_spec: (Method, &'static str)) {
|
fn refute_match(route_spec: (Method, &'static str), req_spec: (Method, &'static str)) {
|
||||||
let route = RouteSpec::new(MethodOrPrefix::Method(route_spec.0.clone()), route_spec.1);
|
let route = RouteSpec::new(route_spec.0.clone(), route_spec.1);
|
||||||
let req = Request::builder()
|
let req = Request::builder()
|
||||||
.method(req_spec.0.clone())
|
.method(req_spec.0.clone())
|
||||||
.uri(req_spec.1)
|
.uri(req_spec.1)
|
||||||
|
@ -739,61 +593,8 @@ mod tests {
|
||||||
"`{} {}` shouldn't match `{:?} {}`",
|
"`{} {}` shouldn't match `{:?} {}`",
|
||||||
req.method(),
|
req.method(),
|
||||||
req.uri().path(),
|
req.uri().path(),
|
||||||
route.method_or_prefix,
|
route.method,
|
||||||
str::from_utf8(&route.spec).unwrap(),
|
str::from_utf8(&route.spec).unwrap(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn strip_prefix() {
|
|
||||||
let mut svc = StripPrefix::new(
|
|
||||||
tower::service_fn(
|
|
||||||
|req: Request<()>| async move { Ok::<_, Infallible>(req.uri().clone()) },
|
|
||||||
),
|
|
||||||
"/foo",
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
svc.call(Request::builder().uri("/foo/bar").body(()).unwrap())
|
|
||||||
.await
|
|
||||||
.unwrap(),
|
|
||||||
"/bar"
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
svc.call(Request::builder().uri("/foo").body(()).unwrap())
|
|
||||||
.await
|
|
||||||
.unwrap(),
|
|
||||||
""
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
svc.call(
|
|
||||||
Request::builder()
|
|
||||||
.uri("http://example.com/foo/bar?key=value")
|
|
||||||
.body(())
|
|
||||||
.unwrap()
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap(),
|
|
||||||
"http://example.com/bar?key=value"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn strip_prefix_with_capture() {
|
|
||||||
let mut svc = StripPrefix::new(
|
|
||||||
tower::service_fn(
|
|
||||||
|req: Request<()>| async move { Ok::<_, Infallible>(req.uri().clone()) },
|
|
||||||
),
|
|
||||||
"/:version/api",
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
svc.call(Request::builder().uri("/v0/api/foo").body(()).unwrap())
|
|
||||||
.await
|
|
||||||
.unwrap(),
|
|
||||||
"/foo"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
195
src/tests.rs
195
src/tests.rs
|
@ -397,92 +397,141 @@ async fn layer_on_whole_router() {
|
||||||
assert_eq!(res.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
assert_eq!(res.status(), StatusCode::INTERNAL_SERVER_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
// #[tokio::test]
|
||||||
async fn nesting() {
|
// async fn nesting() {
|
||||||
let api = app()
|
// let api = app()
|
||||||
.at("/users")
|
// .at("/users")
|
||||||
.get(|_: Request<Body>| async { "users#index" })
|
// .get(|_: Request<Body>| async { "users#index" })
|
||||||
.post(|_: Request<Body>| async { "users#create" })
|
// .post(|_: Request<Body>| async { "users#create" })
|
||||||
.at("/users/:id")
|
// .at("/users/:id")
|
||||||
.get(
|
// .get(
|
||||||
|_: Request<Body>, params: extract::UrlParams<(i32,)>| async move {
|
// |_: Request<Body>, params: extract::UrlParams<(i32,)>| async move {
|
||||||
let (id,) = params.0;
|
// let (id,) = params.0;
|
||||||
format!("users#show {}", id)
|
// format!("users#show {}", id)
|
||||||
},
|
// },
|
||||||
);
|
// );
|
||||||
|
|
||||||
let app = app()
|
// let app = app()
|
||||||
.at("/foo")
|
// .at("/foo")
|
||||||
.get(|_: Request<Body>| async { "foo" })
|
// .get(|_: Request<Body>| async { "foo" })
|
||||||
.at("/api")
|
// .at("/api")
|
||||||
.nest(api)
|
// .nest(api)
|
||||||
.at("/bar")
|
// .at("/bar")
|
||||||
.get(|_: Request<Body>| async { "bar" })
|
// .get(|_: Request<Body>| async { "bar" })
|
||||||
.into_service();
|
// .into_service();
|
||||||
|
|
||||||
let addr = run_in_background(app).await;
|
// let addr = run_in_background(app).await;
|
||||||
|
|
||||||
let client = reqwest::Client::new();
|
// let client = reqwest::Client::new();
|
||||||
|
|
||||||
let res = client
|
// let res = client
|
||||||
.get(format!("http://{}/api/users", addr))
|
// .get(format!("http://{}/api/users", addr))
|
||||||
.send()
|
// .send()
|
||||||
.await
|
// .await
|
||||||
.unwrap();
|
// .unwrap();
|
||||||
assert_eq!(res.text().await.unwrap(), "users#index");
|
// assert_eq!(res.text().await.unwrap(), "users#index");
|
||||||
|
|
||||||
let res = client
|
// let res = client
|
||||||
.post(format!("http://{}/api/users", addr))
|
// .post(format!("http://{}/api/users", addr))
|
||||||
.send()
|
// .send()
|
||||||
.await
|
// .await
|
||||||
.unwrap();
|
// .unwrap();
|
||||||
assert_eq!(res.text().await.unwrap(), "users#create");
|
// assert_eq!(res.text().await.unwrap(), "users#create");
|
||||||
|
|
||||||
let res = client
|
// let res = client
|
||||||
.get(format!("http://{}/api/users/42", addr))
|
// .get(format!("http://{}/api/users/42", addr))
|
||||||
.send()
|
// .send()
|
||||||
.await
|
// .await
|
||||||
.unwrap();
|
// .unwrap();
|
||||||
assert_eq!(res.text().await.unwrap(), "users#show 42");
|
// assert_eq!(res.text().await.unwrap(), "users#show 42");
|
||||||
|
|
||||||
let res = client
|
// let res = client
|
||||||
.get(format!("http://{}/foo", addr))
|
// .get(format!("http://{}/foo", addr))
|
||||||
.send()
|
// .send()
|
||||||
.await
|
// .await
|
||||||
.unwrap();
|
// .unwrap();
|
||||||
assert_eq!(res.text().await.unwrap(), "foo");
|
// assert_eq!(res.text().await.unwrap(), "foo");
|
||||||
|
|
||||||
let res = client
|
// let res = client
|
||||||
.get(format!("http://{}/bar", addr))
|
// .get(format!("http://{}/bar", addr))
|
||||||
.send()
|
// .send()
|
||||||
.await
|
// .await
|
||||||
.unwrap();
|
// .unwrap();
|
||||||
assert_eq!(res.text().await.unwrap(), "bar");
|
// assert_eq!(res.text().await.unwrap(), "bar");
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[tokio::test]
|
// #[tokio::test]
|
||||||
async fn nesting_with_dynamic_part() {
|
// async fn nesting_with_dynamic_part() {
|
||||||
let api = app().at("/users/:id").get(
|
// let api = app().at("/users/:id").get(
|
||||||
|_: Request<Body>, params: extract::UrlParams<(String, i32)>| async move {
|
// |_: Request<Body>, params: extract::UrlParamsMap| async move {
|
||||||
let (version, id) = params.0;
|
// // let (version, id) = params.0;
|
||||||
format!("users#show {} {}", version, id)
|
// dbg!(¶ms);
|
||||||
},
|
// let version = params.get("version").unwrap();
|
||||||
);
|
// let id = params.get("id").unwrap();
|
||||||
|
// format!("users#show {} {}", version, id)
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
|
||||||
let app = app().at("/:version/api").nest(api).into_service();
|
// let app = app().at("/:version/api").nest(api).into_service();
|
||||||
|
|
||||||
let addr = run_in_background(app).await;
|
// let addr = run_in_background(app).await;
|
||||||
|
|
||||||
let client = reqwest::Client::new();
|
// let client = reqwest::Client::new();
|
||||||
|
|
||||||
let res = client
|
// let res = client
|
||||||
.get(format!("http://{}/v0/api/users/123", addr))
|
// .get(format!("http://{}/v0/api/users/123", addr))
|
||||||
.send()
|
// .send()
|
||||||
.await
|
// .await
|
||||||
.unwrap();
|
// .unwrap();
|
||||||
assert_eq!(res.status(), StatusCode::OK);
|
// let status = res.status();
|
||||||
assert_eq!(res.text().await.unwrap(), "users#show v0 123");
|
// assert_eq!(res.text().await.unwrap(), "users#show v0 123");
|
||||||
}
|
// assert_eq!(status, StatusCode::OK);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[tokio::test]
|
||||||
|
// async fn nesting_more_deeply() {
|
||||||
|
// let users_api = app()
|
||||||
|
// .at("/:id")
|
||||||
|
// .get(|req: Request<Body>| async move {
|
||||||
|
// dbg!(&req.uri().path());
|
||||||
|
// "users#show"
|
||||||
|
// });
|
||||||
|
|
||||||
|
// let games_api = app()
|
||||||
|
// .at("/")
|
||||||
|
// .post(|req: Request<Body>| async move {
|
||||||
|
// dbg!(&req.uri().path());
|
||||||
|
// "games#create"
|
||||||
|
// });
|
||||||
|
|
||||||
|
// let api = app()
|
||||||
|
// .at("/users")
|
||||||
|
// .nest(users_api)
|
||||||
|
// .at("/games")
|
||||||
|
// .nest(games_api);
|
||||||
|
|
||||||
|
// let app = app().at("/:version/api").nest(api).into_service();
|
||||||
|
|
||||||
|
// let addr = run_in_background(app).await;
|
||||||
|
|
||||||
|
// let client = reqwest::Client::new();
|
||||||
|
|
||||||
|
// // let res = client
|
||||||
|
// // .get(format!("http://{}/v0/api/users/123", addr))
|
||||||
|
// // .send()
|
||||||
|
// // .await
|
||||||
|
// // .unwrap();
|
||||||
|
// // assert_eq!(res.status(), StatusCode::OK);
|
||||||
|
|
||||||
|
// println!("============================");
|
||||||
|
|
||||||
|
// let res = client
|
||||||
|
// .post(format!("http://{}/v0/api/games", addr))
|
||||||
|
// .send()
|
||||||
|
// .await
|
||||||
|
// .unwrap();
|
||||||
|
// assert_eq!(res.status(), StatusCode::OK);
|
||||||
|
// }
|
||||||
|
|
||||||
// TODO(david): nesting more deeply
|
// TODO(david): nesting more deeply
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue