-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Provide attribute macro for multiple HTTP methods #1674
Provide attribute macro for multiple HTTP methods #1674
Conversation
Codecov Report
@@ Coverage Diff @@
## master #1674 +/- ##
==========================================
- Coverage 53.51% 53.31% -0.20%
==========================================
Files 123 123
Lines 11876 11919 +43
==========================================
Hits 6355 6355
- Misses 5521 5564 +43
Continue to review full report at Codecov.
|
I think this api should be: #[route("/multi", method = "GET", method = "POST")]
async fn multi_methods() -> &'static str {
"Hello world!\r\n"
} otherwise it's not consistent with: #[get("/index2", wrap = "middleware::SayHi", wrap = "middleware::SayHi2")] |
or may be: #[route("/multi", methods = ["GET","POST"])]
async fn multi_methods() -> &'static str {
"Hello world!\r\n"
} |
Thanks for the feedback @Silentdoer My initial approach was I can reimplement the macro to have the |
I've updated the syntax to be |
9618e70
to
be2af8f
Compare
Hey @mattgathu, it's great to see you tackling this! I'd like to land #1677 first so we can test some failure cases in CI and make sure the compile error outputs make sense. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks really nice. Thanks!
@mattgathu #1677 is merged so it'd be great to get some trybuild tests added as part of this. Try to get in as many weird edge case failures as you can think of. |
e813df3
to
79c005d
Compare
@robjtede I got an issue with tests passing on rustc 1.42.0 but failing on the current stable.
note the = note: this error originates in an attribute macro... line. Do you have any suggestions on how to handle this? |
Hmm... I'll have a look. In the mean time, I feel that the macro should be called "resource" as it is more consistent with the manual routing methods. A resource can define multiple methods on a path where a route is a single method. |
I would argue that
I'm viewing |
Alright, fair argument. Would like to get more opinons from @actix/contributors then. When you read it as a verb it makes sense even though you'd have to sort of read it as |
I'm coming around to #[route(...)] for brevity sake. In other news, I think I've found a solution to the compile error difference while letting us test on stable too. Great find with that rustversion macro; using that you can do this: #[test]
fn compile_macros() {
let t = trybuild::TestCases::new();
t.pass("tests/trybuild/simple.rs");
t.compile_fail("tests/trybuild/simple-fail.rs");
t.pass("tests/trybuild/route-ok.rs");
t.compile_fail("tests/trybuild/route-duplicate-method-fail.rs");
t.compile_fail("tests/trybuild/route-unexpected-method-fail.rs");
missing_method_fail_hack(&t);
}
#[rustversion::stable(1.42)]
fn missing_method_fail_hack(t: &trybuild::TestCases) {
t.compile_fail("tests/trybuild/route-missing-method-fail-msrv.rs");
}
#[rustversion::not(stable(1.42))]
fn missing_method_fail_hack(t: &trybuild::TestCases) {
t.compile_fail("tests/trybuild/route-missing-method-fail.rs");
} Then have two versions of the test file...:
(Even cleaner, you can symlink the -msrv version to the original.) ...and two versions of the compiler output:
|
I'm not against this poll but would like to share my two cents. I feel attributes should follow the rust std style. For example: |
@robjtede regarding the trybuild tests, the Actix-web's CI matrix has 1.42, current stable and nightly tests with the compilers never guaranteed to produce similar outputs. There will be a probability that each time nightly or the current stable is updated the tests will break. Pinning the tests to 1.42 avoids this likely breakage. Having multiple tests for different compilers is great but might not be maintainable IMHO. |
I think the maintenance cost is small enough using this approach, testing for MSRV and latest stable is enough; we should not run any trybuild tests on nightly. Worst case we will need to update the stderr files every 6 weeks. But I think theres value in knowing when compiler output changes for our macro errors, anyway. |
What -- Define a new `route` attribute macro that supports defining multiple HTTP methods to routed to (handled by) a single handler. The attribute macro syntax looks like this ```rust use actix_web::route; async fn multi_methods() -> &'static str { "Hello world!\r\n" } ``` How -- This implementation extends the [`GuardType`][1] enum in actix-web-codegen to have a new `GuardType::Multi` variant that denotes when multiple method guards are used. A new `methods` attribute in the `route` attribute macro provides a comma-separated list of HTTP methods to provide guard for. The code parses the methods list, matches them to the respective `GuardType` and uses the `AnyGuard` struct to combine them together. A constructor method for [`AnyGuard`][2] is added to support this. The generated code looks like this: ```rust pub struct multi_methods; impl actix_web::dev::HttpServiceFactory for multi_methods { fn register(self, __config: &mut actix_web::dev::AppService) { ¦ async fn multi_methods() -> &'static str { ¦ ¦ "Hello world!\r\n" ¦ } ¦ let __resource = actix_web::Resource::new("/multi") ¦ ¦ .name("multi_methods") ¦ ¦ .guard(actix_web::guard::AnyGuard::new(<[_]>::into_vec(box [ ¦ ¦ ¦ Box::new(actix_web::guard::Get()), ¦ ¦ ¦ Box::new(actix_web::guard::Post()), ¦ ¦ ]))) ¦ ¦ .to(multi_methods); ¦ actix_web::dev::HttpServiceFactory::register(__resource, __config) } } ``` **NOTE: This is my first attempt that implementing this feature. Feedback and mentorship is highly welcome to improve it :-)** Why -- This fixes actix#1360 [1]: https://github.com/actix/actix-web/blob/master/actix-web-codegen/src/route.rs#L21 [2]: https://github.com/actix/actix-web/blob/master/src/guard.rs#L104s
* assume HTTP methods always specified in uppercase e.g `GET` * simplify code generation by quote!
Refactor code to detect when the same HTTP method is specified more than once. eg. `#[route("/multi", method = "GET", method = "GET")]` and return a compile time error.
Try converting String literal to GuardType
Co-authored-by: Rob Ede <robjtede@icloud.com>
@robjtede I haven't found a way to differentiate between the 1.42 stable and other stable versions using the rustversion macros :-( You have Edit: I ended up building a dummy shim and stacking the macros 🤞 |
4a72a0d
to
1d673eb
Compare
Great! I'm happy to go ahead with that test set up. Check out this commit (02e16c6) for how to do the multi guard without requiring changes to actix-web. The generated code looks like this for the example in that commit: let __resource = actix_web::Resource::new("/")
.name("index")
.guard(
actix_web::guard::Any(actix_web::guard::Get())
.or(actix_web::guard::Trace())
.or(actix_web::guard::Post())
.or(actix_web::guard::Head()),
)
.to(index); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks so much for working on this 👍 great job!
@fakeshadow RE your comment on attribute style: I don't think there's a use case for not(...) here and I think it's safe to assume the syntax is any(...) without having to explicitly write that out. This is also inkeeping with current wrap="..." style. Speak now if you still feel strongly on it. |
I'm fine with the current attributes. You guys did a good job. |
PR Type
Feature
PR Checklist
Overview
Provide attribute macro for multiple HTTP methods
Define a new
route
attribute macro that supports defining multipleHTTP methods to be routed to (handled by) a single handler.
The attribute macro syntax looks like this
This implementation extends the
GuardType
enum in actix-web-codegen to have anew
GuardType::Multi
variant that denotes when multiple method guardsare used.
A new
method
attribute in theroute
attribute macro provides an HTTP method to provide a guard for.This attribute can be used multiple times for multiple HTTP methods.
The code matches the methods to the respective
GuardType
and uses theAnyGuard
struct to combine them together.The generated code looks like this:
This Closes #1360