Skip to content

Conversation

@Tuntii
Copy link
Owner

@Tuntii Tuntii commented Jan 15, 2026

This pull request refactors the request routing and middleware handling in the core server logic to improve middleware flexibility and correctness. The main change is deferring route matching until after middleware has executed, allowing middleware (such as CORS) to intercept requests before routes are resolved. Additionally, path parameters are now set on the request object only after a route is matched.

Routing and middleware execution changes:

  • Route matching is now performed inside the middleware chain, allowing middleware to intercept requests before route matching occurs. This improves support for features like CORS and authentication. (crates/rustapi-core/src/server.rs, [1] [2]
  • The request is initially constructed with empty path parameters; these parameters are set on the request only after a route is matched. (crates/rustapi-core/src/request.rs, [1]; crates/rustapi-core/src/server.rs, [2]
  • The routing handler sets path parameters on the request object after a successful route match, ensuring correct extraction for downstream handlers. (crates/rustapi-core/src/server.rs, crates/rustapi-core/src/server.rsL91-R120)
  • The middleware stack now executes before route matching, and the routing handler is passed through the middleware chain instead of the final handler. (crates/rustapi-core/src/server.rs, crates/rustapi-core/src/server.rsL108-R143)

Error handling and response logic:

  • Error responses for not found and method not allowed routes are now generated inside the routing handler, keeping all routing logic within the middleware chain. (crates/rustapi-core/src/server.rs, [1] [2]

Moved route matching logic into the middleware chain so that middleware (such as CORS) can intercept requests before route matching. Added a set_path_params method to Request for updating path parameters after route matching.
Copilot AI review requested due to automatic review settings January 15, 2026 22:13
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request refactors the request handling flow to defer route matching until after middleware execution, enabling middleware (such as CORS) to intercept requests before routes are resolved. Previously, route matching occurred before middleware, preventing certain middleware from functioning correctly on unmatched routes.

Changes:

  • Route matching moved inside the middleware chain via a routing handler closure
  • Path parameters are now set on the Request object after route matching instead of at construction
  • Error responses for NotFound and MethodNotAllowed are generated within the routing handler

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
crates/rustapi-core/src/server.rs Restructured handle_request to create a routing handler that performs route matching inside the middleware chain; path parameters are set after a successful match
crates/rustapi-core/src/request.rs Added set_path_params method to allow setting path parameters after route matching

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +105 to +127
let path_clone = path.clone();
let method_clone = method.clone();
let routing_handler: BoxedNext = Arc::new(move |mut req: Request| {
let router = router_clone.clone();
let path = path_clone.clone();
let method = method_clone.clone();
Box::pin(async move {
match router.match_route(&path, &method) {
RouteMatch::Found { handler, params } => {
// Set path params on the request
req.set_path_params(params);
handler(req).await
}
RouteMatch::NotFound => {
ApiError::not_found(format!("No route found for {} {}", method, path))
.into_response()
}
RouteMatch::MethodNotAllowed { allowed } => {
let allowed_str: Vec<&str> = allowed.iter().map(|m| m.as_str()).collect();
let mut response = ApiError::new(
StatusCode::METHOD_NOT_ALLOWED,
"method_not_allowed",
format!("Method {} not allowed for {}", method, path),
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multiple clones are performed on every request inside the closure. The router_clone.clone() on line 108 is cloning an Arc<Router> which is inexpensive, but path_clone.clone() and method_clone.clone() create additional allocations for each request. Consider moving these into captured variables in the outer scope to avoid repeated cloning per request.

Suggested change
let path_clone = path.clone();
let method_clone = method.clone();
let routing_handler: BoxedNext = Arc::new(move |mut req: Request| {
let router = router_clone.clone();
let path = path_clone.clone();
let method = method_clone.clone();
Box::pin(async move {
match router.match_route(&path, &method) {
RouteMatch::Found { handler, params } => {
// Set path params on the request
req.set_path_params(params);
handler(req).await
}
RouteMatch::NotFound => {
ApiError::not_found(format!("No route found for {} {}", method, path))
.into_response()
}
RouteMatch::MethodNotAllowed { allowed } => {
let allowed_str: Vec<&str> = allowed.iter().map(|m| m.as_str()).collect();
let mut response = ApiError::new(
StatusCode::METHOD_NOT_ALLOWED,
"method_not_allowed",
format!("Method {} not allowed for {}", method, path),
let path_arc = std::sync::Arc::new(path.clone());
let method_arc = std::sync::Arc::new(method.clone());
let routing_handler: BoxedNext = Arc::new(move |mut req: Request| {
let router = router_clone.clone();
let path = std::sync::Arc::clone(&path_arc);
let method = std::sync::Arc::clone(&method_arc);
Box::pin(async move {
match router.match_route(&*path, &*method) {
RouteMatch::Found { handler, params } => {
// Set path params on the request
req.set_path_params(params);
handler(req).await
}
RouteMatch::NotFound => {
ApiError::not_found(format!(
"No route found for {} {}",
method.as_ref(),
path.as_ref()
))
.into_response()
}
RouteMatch::MethodNotAllowed { allowed } => {
let allowed_str: Vec<&str> = allowed.iter().map(|m| m.as_str()).collect();
let mut response = ApiError::new(
StatusCode::METHOD_NOT_ALLOWED,
"method_not_allowed",
format!(
"Method {} not allowed for {}",
method.as_ref(),
path.as_ref()
),

Copilot uses AI. Check for mistakes.
Comment on lines +130 to +133
response
.headers_mut()
.insert(header::ALLOW, allowed_str.join(", ").parse().unwrap());
response
Copy link

Copilot AI Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using .unwrap() on the header value parse could panic if the allowed methods string is malformed. While this is unlikely, consider using .expect() with a descriptive message or handling the error gracefully to prevent potential panics in production.

Suggested change
response
.headers_mut()
.insert(header::ALLOW, allowed_str.join(", ").parse().unwrap());
response
let allowed_header_value = allowed_str.join(", ");
if let Ok(header_value) = allowed_header_value.parse() {
response
.headers_mut()
.insert(header::ALLOW, header_value);
} else {
error!(
allowed_methods = %allowed_header_value,
"Failed to parse Allow header value; skipping header insertion"
);
}
response

Copilot uses AI. Check for mistakes.
Bump the minimum supported Rust version to 1.78 in Cargo.toml and remove the MSRV check job from the CI workflow. This streamlines CI and aligns the project with the new Rust version requirement.
@Tuntii Tuntii merged commit 5620ff3 into main Jan 15, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] 405 OPTIONS Method Not Allowed for /pins Endpoint When Called from Angular Frontend (CORS Question)

2 participants