Skip to content

Commit 03666dd

Browse files
committed
Auto merge of #3373 - jtgeibel:simplify-routing, r=Turbo87
Move router error handling into new middleware layer This moves handling of routing errors into middleware and adds a test for the conversion. Additionally we previously called `router.recognize()` instead of `router.call()`. By switching we can let the `conduit-router` logic handle adding `Params` to the request extensions. A potential allocation is removed from json error handling, although the compiler may be able to eliminate that allocation already. r? `@Turbo87`
2 parents a92c743 + ff1f7cc commit 03666dd

File tree

5 files changed

+62
-27
lines changed

5 files changed

+62
-27
lines changed

src/middleware.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use self::app::AppMiddleware;
77
use self::debug::*;
88
use self::ember_html::EmberHtml;
99
use self::head::Head;
10+
use self::known_error_to_json::KnownErrorToJson;
1011
use self::log_connection_pool_status::LogConnectionPoolStatus;
1112
use self::static_or_continue::StaticOrContinue;
1213

@@ -17,6 +18,7 @@ mod debug;
1718
mod ember_html;
1819
mod ensure_well_formed_500;
1920
mod head;
21+
mod known_error_to_json;
2022
mod log_connection_pool_status;
2123
pub mod log_request;
2224
mod normalize_path;
@@ -26,14 +28,14 @@ mod static_or_continue;
2628
use conduit_conditional_get::ConditionalGet;
2729
use conduit_cookie::{Middleware as Cookie, SessionMiddleware};
2830
use conduit_middleware::MiddlewareBuilder;
31+
use conduit_router::RouteBuilder;
2932

3033
use std::env;
3134
use std::sync::Arc;
3235

33-
use crate::router::R404;
3436
use crate::{App, Env};
3537

36-
pub fn build_middleware(app: Arc<App>, endpoints: R404) -> MiddlewareBuilder {
38+
pub fn build_middleware(app: Arc<App>, endpoints: RouteBuilder) -> MiddlewareBuilder {
3739
let mut m = MiddlewareBuilder::new(endpoints);
3840
let config = app.config.clone();
3941
let env = config.env;
@@ -67,6 +69,7 @@ pub fn build_middleware(app: Arc<App>, endpoints: R404) -> MiddlewareBuilder {
6769
));
6870

6971
m.add(AppMiddleware::new(app));
72+
m.add(KnownErrorToJson);
7073

7174
// Note: The following `m.around()` middleware is run from bottom to top
7275

src/middleware/ensure_well_formed_500.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
33
use super::prelude::*;
44

5-
// Can't derive debug because of Handler.
6-
#[allow(missing_debug_implementations)]
75
#[derive(Default)]
86
pub struct EnsureWellFormed500;
97

src/middleware/known_error_to_json.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//! Converts known error types into friendly JSON errors
2+
//!
3+
//! Some similar logic exists in `crate::util::errors::AppError::try_convert()`. That low-level
4+
//! handling needs to remain in place, because some endpoint logic relys on detecting those
5+
//! normalized errors. Errors produced by the router cannot be seen by endpoints, so the conversion
6+
//! can be deferred until here.
7+
8+
use super::prelude::*;
9+
use crate::util::errors::NotFound;
10+
11+
use conduit_router::RouterError;
12+
13+
#[derive(Default)]
14+
pub struct KnownErrorToJson;
15+
16+
impl Middleware for KnownErrorToJson {
17+
fn after(&self, _: &mut dyn RequestExt, res: AfterResult) -> AfterResult {
18+
res.or_else(|e| {
19+
if e.downcast_ref::<RouterError>().is_some() {
20+
return Ok(NotFound.into());
21+
}
22+
23+
Err(e)
24+
})
25+
}
26+
}
27+
28+
#[cfg(test)]
29+
mod tests {
30+
use super::KnownErrorToJson;
31+
32+
use conduit::{Body, Handler, Method, StatusCode};
33+
use conduit_middleware::MiddlewareBuilder;
34+
use conduit_router::RouteBuilder;
35+
use conduit_test::MockRequest;
36+
37+
#[test]
38+
fn router_errors_become_not_found_response() {
39+
let route_builder = RouteBuilder::new();
40+
let mut middleware = MiddlewareBuilder::new(route_builder);
41+
middleware.add(KnownErrorToJson);
42+
43+
let mut req = MockRequest::new(Method::GET, "/");
44+
let (parts, body) = middleware.call(&mut req).unwrap().into_parts();
45+
assert_eq!(parts.status, StatusCode::NOT_FOUND);
46+
assert!(matches!(
47+
body,
48+
Body::Owned(vec) if vec == br#"{"errors":[{"detail":"Not Found"}]}"#
49+
));
50+
}
51+
}

src/router.rs

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ use conduit::{Handler, HandlerResult, RequestExt};
44
use conduit_router::{RequestParams, RouteBuilder};
55

66
use crate::controllers::*;
7-
use crate::util::errors::{std_error, AppError, NotFound};
7+
use crate::util::errors::{std_error, AppError};
88
use crate::util::EndpointResult;
99
use crate::{App, Env};
1010

11-
pub fn build_router(app: &App) -> R404 {
11+
pub fn build_router(app: &App) -> RouteBuilder {
1212
let mut api_router = RouteBuilder::new();
1313

1414
// Route used by both `cargo search` and the frontend
@@ -103,7 +103,7 @@ pub fn build_router(app: &App) -> R404 {
103103
C(user::me::regenerate_token_and_send),
104104
);
105105
api_router.get("/site_metadata", C(site_metadata::show_deployed_sha));
106-
let api_router = Arc::new(R404(api_router));
106+
let api_router = Arc::new(api_router);
107107

108108
let mut router = RouteBuilder::new();
109109

@@ -133,7 +133,7 @@ pub fn build_router(app: &App) -> R404 {
133133
router.post("/git/index/*path", R(s));
134134
}
135135

136-
R404(router)
136+
router
137137
}
138138

139139
struct C(pub fn(&mut dyn RequestExt) -> EndpointResult);
@@ -166,23 +166,6 @@ impl<H: Handler> Handler for R<H> {
166166
}
167167
}
168168

169-
// Can't derive Debug because of RouteBuilder.
170-
#[allow(missing_debug_implementations)]
171-
pub struct R404(pub RouteBuilder);
172-
173-
impl Handler for R404 {
174-
fn call(&self, req: &mut dyn RequestExt) -> HandlerResult {
175-
let R404(ref router) = *self;
176-
match router.recognize(&req.method(), req.path()) {
177-
Ok(m) => {
178-
req.mut_extensions().insert(m.params().clone());
179-
m.handler().call(req)
180-
}
181-
Err(..) => Ok(NotFound.into()),
182-
}
183-
}
184-
}
185-
186169
#[cfg(test)]
187170
mod tests {
188171
use super::*;

src/util/errors/json.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ fn json_error(detail: &str, status: StatusCode) -> AppResponse {
1414
}
1515
#[derive(Serialize)]
1616
struct Bad<'a> {
17-
errors: Vec<StringError<'a>>,
17+
errors: [StringError<'a>; 1],
1818
}
1919

2020
let mut response = json_response(&Bad {
21-
errors: vec![StringError { detail }],
21+
errors: [StringError { detail }],
2222
});
2323
*response.status_mut() = status;
2424
response

0 commit comments

Comments
 (0)