mirror of https://github.com/fafhrd91/actix-web
Merge 1ff366517d into 9822f5e120
This commit is contained in:
commit
d86a4aad76
|
|
@ -2,8 +2,11 @@
|
|||
|
||||
## Unreleased
|
||||
|
||||
- Add `TestServer::{query, squery}()` for issuing HTTP `QUERY` requests (RFC 10008). [#4140]
|
||||
- Minimum supported Rust version (MSRV) is now 1.88.
|
||||
|
||||
[#4140]: https://github.com/actix/actix-web/pull/4140
|
||||
|
||||
## 3.2.0
|
||||
|
||||
- Minimum supported Rust version (MSRV) is now 1.68 due to transitive `time` dependency.
|
||||
|
|
|
|||
|
|
@ -216,6 +216,16 @@ impl TestServer {
|
|||
self.client.patch(self.surl(path.as_ref()).as_str())
|
||||
}
|
||||
|
||||
/// Create `QUERY` request
|
||||
pub fn query<S: AsRef<str>>(&self, path: S) -> ClientRequest {
|
||||
self.client.query(self.url(path.as_ref()).as_str())
|
||||
}
|
||||
|
||||
/// Create HTTPS `QUERY` request
|
||||
pub fn squery<S: AsRef<str>>(&self, path: S) -> ClientRequest {
|
||||
self.client.query(self.surl(path.as_ref()).as_str())
|
||||
}
|
||||
|
||||
/// Create `DELETE` request
|
||||
pub fn delete<S: AsRef<str>>(&self, path: S) -> ClientRequest {
|
||||
self.client.delete(self.url(path.as_ref()).as_str())
|
||||
|
|
|
|||
|
|
@ -2,8 +2,11 @@
|
|||
|
||||
## Unreleased
|
||||
|
||||
- Add `TestServer::query()` for issuing HTTP `QUERY` requests (RFC 10008). [#4140]
|
||||
- Minimum supported Rust version (MSRV) is now 1.88.
|
||||
|
||||
[#4140]: https://github.com/actix/actix-web/pull/4140
|
||||
|
||||
## 0.1.5
|
||||
|
||||
- Add `TestServerConfig::listen_address()` method.
|
||||
|
|
|
|||
|
|
@ -697,6 +697,11 @@ impl TestServer {
|
|||
self.client.patch(self.url(path.as_ref()).as_str())
|
||||
}
|
||||
|
||||
/// Create `QUERY` request.
|
||||
pub fn query(&self, path: impl AsRef<str>) -> ClientRequest {
|
||||
self.client.query(self.url(path.as_ref()).as_str())
|
||||
}
|
||||
|
||||
/// Create `DELETE` request.
|
||||
pub fn delete(&self, path: impl AsRef<str>) -> ClientRequest {
|
||||
self.client.delete(self.url(path.as_ref()).as_str())
|
||||
|
|
|
|||
|
|
@ -2,8 +2,11 @@
|
|||
|
||||
## Unreleased
|
||||
|
||||
- Add `#[query]` macro for routing the HTTP `QUERY` method (RFC 10008). [#4140]
|
||||
- Minimum supported Rust version (MSRV) is now 1.88.
|
||||
|
||||
[#4140]: https://github.com/actix/actix-web/pull/4140
|
||||
|
||||
## 4.3.0
|
||||
|
||||
- Add `#[scope]` macro.
|
||||
|
|
|
|||
|
|
@ -21,7 +21,8 @@
|
|||
//! There is a macro to set up a handler for each of the most common HTTP methods that also define
|
||||
//! additional guards and route-specific middleware.
|
||||
//!
|
||||
//! See docs for: [GET], [POST], [PATCH], [PUT], [DELETE], [HEAD], [CONNECT], [OPTIONS], [TRACE]
|
||||
//! See docs for: [GET], [POST], [PATCH], [PUT], [DELETE], [HEAD], [CONNECT], [OPTIONS], [TRACE],
|
||||
//! [QUERY]
|
||||
//!
|
||||
//! ```
|
||||
//! # use actix_web::HttpResponse;
|
||||
|
|
@ -71,6 +72,7 @@
|
|||
//! [TRACE]: macro@trace
|
||||
//! [PATCH]: macro@patch
|
||||
//! [DELETE]: macro@delete
|
||||
//! [QUERY]: macro@query
|
||||
|
||||
#![recursion_limit = "512"]
|
||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||
|
|
@ -195,6 +197,7 @@ method_macro!(Connect, connect);
|
|||
method_macro!(Options, options);
|
||||
method_macro!(Trace, trace);
|
||||
method_macro!(Patch, patch);
|
||||
method_macro!(Query, query);
|
||||
|
||||
/// Prepends a path prefix to all handlers using routing macros inside the attached module.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -101,6 +101,7 @@ standard_method_type! {
|
|||
Options, OPTIONS, options,
|
||||
Trace, TRACE, trace,
|
||||
Patch, PATCH, patch,
|
||||
Query, QUERY, query,
|
||||
}
|
||||
|
||||
impl TryFrom<&syn::LitStr> for MethodType {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use actix_web::{
|
|||
web, App, Error, HttpRequest, HttpResponse, Responder,
|
||||
};
|
||||
use actix_web_codegen::{
|
||||
connect, delete, get, head, options, patch, post, put, route, routes, trace,
|
||||
connect, delete, get, head, options, patch, post, put, query, route, routes, trace,
|
||||
};
|
||||
use futures_core::future::LocalBoxFuture;
|
||||
|
||||
|
|
@ -36,6 +36,11 @@ async fn patch_test() -> impl Responder {
|
|||
HttpResponse::Ok()
|
||||
}
|
||||
|
||||
#[query("/test")]
|
||||
async fn query_test() -> impl Responder {
|
||||
HttpResponse::Ok()
|
||||
}
|
||||
|
||||
#[post("/test")]
|
||||
async fn post_test() -> impl Responder {
|
||||
HttpResponse::NoContent()
|
||||
|
|
@ -258,6 +263,7 @@ async fn test_body() {
|
|||
.service(options_test)
|
||||
.service(trace_test)
|
||||
.service(patch_test)
|
||||
.service(query_test)
|
||||
.service(test_handler)
|
||||
.service(route_test)
|
||||
.service(routes_overlapping_test)
|
||||
|
|
@ -290,6 +296,13 @@ async fn test_body() {
|
|||
let response = request.send().await.unwrap();
|
||||
assert!(response.status().is_success());
|
||||
|
||||
let request = srv.request(
|
||||
http::Method::from_bytes(b"QUERY").unwrap(),
|
||||
srv.url("/test"),
|
||||
);
|
||||
let response = request.send().await.unwrap();
|
||||
assert!(response.status().is_success());
|
||||
|
||||
let request = srv.request(http::Method::PUT, srv.url("/test"));
|
||||
let response = request.send().await.unwrap();
|
||||
assert!(response.status().is_success());
|
||||
|
|
|
|||
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
## Unreleased
|
||||
|
||||
- Add support for the HTTP `QUERY` method (RFC 10008): `web::query()`, `guard::Query()`, `Resource::query()`, and `TestRequest::query()`. [#4140]
|
||||
|
||||
[#4140]: https://github.com/actix/actix-web/pull/4140
|
||||
|
||||
## 4.14.0
|
||||
|
||||
- Add `HttpRequest::{cookies_raw,cookie_raw}` and `ServiceRequest::{cookies_raw,cookie_raw}` for reading request cookies without percent-decoding names and values. [#3542]
|
||||
|
|
|
|||
|
|
@ -425,6 +425,9 @@ impl Guard for MethodGuard {
|
|||
|
||||
macro_rules! method_guard {
|
||||
($method_fn:ident, $method_const:ident) => {
|
||||
method_guard!($method_fn, $method_const, HttpMethod::$method_const);
|
||||
};
|
||||
($method_fn:ident, $method_const:ident, $method:expr) => {
|
||||
#[doc = concat!("Creates a guard that matches the `", stringify!($method_const), "` request method.")]
|
||||
///
|
||||
/// # Examples
|
||||
|
|
@ -438,7 +441,7 @@ macro_rules! method_guard {
|
|||
/// ```
|
||||
#[allow(non_snake_case)]
|
||||
pub fn $method_fn() -> impl Guard {
|
||||
MethodGuard(HttpMethod::$method_const)
|
||||
MethodGuard($method)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -452,6 +455,9 @@ method_guard!(Options, OPTIONS);
|
|||
method_guard!(Connect, CONNECT);
|
||||
method_guard!(Patch, PATCH);
|
||||
method_guard!(Trace, TRACE);
|
||||
// `QUERY` (RFC 10008) is a safe, idempotent method that carries a request body.
|
||||
// TODO: replace with `HttpMethod::QUERY` once the `http` dependency is bumped (see #3384).
|
||||
method_guard!(Query, QUERY, HttpMethod::from_bytes(b"QUERY").unwrap());
|
||||
|
||||
/// Creates a guard that matches if request contains given header name and value.
|
||||
///
|
||||
|
|
@ -543,6 +549,10 @@ mod tests {
|
|||
assert!(Patch().check(&req.guard_ctx()));
|
||||
assert!(!Patch().check(&get_req.guard_ctx()));
|
||||
|
||||
let req = TestRequest::query().to_srv_request();
|
||||
assert!(Query().check(&req.guard_ctx()));
|
||||
assert!(!Query().check(&get_req.guard_ctx()));
|
||||
|
||||
let r = TestRequest::delete().to_srv_request();
|
||||
assert!(Delete().check(&r.guard_ctx()));
|
||||
assert!(!Delete().check(&get_req.guard_ctx()));
|
||||
|
|
|
|||
|
|
@ -155,6 +155,7 @@ codegen_reexport!(delete);
|
|||
codegen_reexport!(trace);
|
||||
codegen_reexport!(connect);
|
||||
codegen_reexport!(options);
|
||||
codegen_reexport!(query);
|
||||
codegen_reexport!(scope);
|
||||
|
||||
pub(crate) type BoxError = Box<dyn std::error::Error>;
|
||||
|
|
|
|||
|
|
@ -403,6 +403,7 @@ where
|
|||
route_shortcut!(delete, "DELETE");
|
||||
route_shortcut!(head, "HEAD");
|
||||
route_shortcut!(trace, "TRACE");
|
||||
route_shortcut!(query, "QUERY");
|
||||
}
|
||||
|
||||
impl<T, B> HttpServiceFactory for Resource<T>
|
||||
|
|
@ -810,6 +811,32 @@ mod tests {
|
|||
assert_eq!(resp.status(), StatusCode::NO_CONTENT);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_query_method() {
|
||||
let srv = init_service(
|
||||
App::new().service(web::resource("/test").route(web::query().to(HttpResponse::Ok))),
|
||||
)
|
||||
.await;
|
||||
|
||||
// a QUERY request is routed to the `web::query()` handler
|
||||
let req = TestRequest::with_uri("/test")
|
||||
.method(Method::from_bytes(b"QUERY").unwrap())
|
||||
.to_request();
|
||||
let resp = call_service(&srv, req).await;
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
|
||||
// any other method is rejected with `405 Method Not Allowed`
|
||||
let req = TestRequest::with_uri("/test")
|
||||
.method(Method::GET)
|
||||
.to_request();
|
||||
let resp = call_service(&srv, req).await;
|
||||
assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
|
||||
assert_eq!(
|
||||
resp.headers().get(header::ALLOW).unwrap().as_bytes(),
|
||||
b"QUERY"
|
||||
);
|
||||
}
|
||||
|
||||
// allow deprecated `{App, Resource}::data`
|
||||
#[allow(deprecated)]
|
||||
#[actix_rt::test]
|
||||
|
|
|
|||
|
|
@ -111,6 +111,12 @@ impl TestRequest {
|
|||
TestRequest::default().method(Method::PATCH)
|
||||
}
|
||||
|
||||
/// Constructs test request with QUERY method.
|
||||
// TODO: use `Method::QUERY` once the `http` dependency is bumped (see #3384).
|
||||
pub fn query() -> TestRequest {
|
||||
TestRequest::default().method(Method::from_bytes(b"QUERY").unwrap())
|
||||
}
|
||||
|
||||
/// Constructs test request with DELETE method.
|
||||
pub fn delete() -> TestRequest {
|
||||
TestRequest::default().method(Method::DELETE)
|
||||
|
|
|
|||
|
|
@ -371,6 +371,7 @@ mod tests {
|
|||
web::resource("/index.html")
|
||||
.route(web::put().to(|| HttpResponse::Ok().body("put!")))
|
||||
.route(web::patch().to(|| HttpResponse::Ok().body("patch!")))
|
||||
.route(web::query().to(|| HttpResponse::Ok().body("query!")))
|
||||
.route(web::delete().to(|| HttpResponse::Ok().body("delete!"))),
|
||||
),
|
||||
)
|
||||
|
|
@ -392,11 +393,42 @@ mod tests {
|
|||
let result = call_and_read_body(&app, patch_req).await;
|
||||
assert_eq!(result, Bytes::from_static(b"patch!"));
|
||||
|
||||
let query_req = TestRequest::query()
|
||||
.uri("/index.html")
|
||||
.insert_header((header::CONTENT_TYPE, "application/json"))
|
||||
.to_request();
|
||||
|
||||
let result = call_and_read_body(&app, query_req).await;
|
||||
assert_eq!(result, Bytes::from_static(b"query!"));
|
||||
|
||||
let delete_req = TestRequest::delete().uri("/index.html").to_request();
|
||||
let result = call_and_read_body(&app, delete_req).await;
|
||||
assert_eq!(result, Bytes::from_static(b"delete!"));
|
||||
}
|
||||
|
||||
// `QUERY` is safe and idempotent like `GET`, but (like `POST`) carries a request body;
|
||||
// this proves body extractors work for `QUERY` handlers. See RFC 10008.
|
||||
#[actix_rt::test]
|
||||
async fn test_query_carries_json_body() {
|
||||
let app =
|
||||
init_service(App::new().service(web::resource("/people").route(
|
||||
web::query().to(|person: web::Json<Person>| HttpResponse::Ok().json(person)),
|
||||
)))
|
||||
.await;
|
||||
|
||||
let payload = r#"{"id":"12345","name":"User name"}"#.as_bytes();
|
||||
|
||||
let req = TestRequest::query()
|
||||
.uri("/people")
|
||||
.insert_header((header::CONTENT_TYPE, "application/json"))
|
||||
.set_payload(payload)
|
||||
.to_request();
|
||||
|
||||
let result: Person = call_and_read_body_json(&app, req).await;
|
||||
assert_eq!(result.id, "12345");
|
||||
assert_eq!(result.name, "User name");
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Person {
|
||||
id: String,
|
||||
|
|
|
|||
|
|
@ -101,6 +101,9 @@ pub fn route() -> Route {
|
|||
|
||||
macro_rules! method_route {
|
||||
($method_fn:ident, $method_const:ident) => {
|
||||
method_route!($method_fn, $method_const, Method::$method_const);
|
||||
};
|
||||
($method_fn:ident, $method_const:ident, $method:expr) => {
|
||||
#[doc = concat!(" Creates a new route with `", stringify!($method_const), "` method guard.")]
|
||||
///
|
||||
/// # Examples
|
||||
|
|
@ -115,7 +118,7 @@ macro_rules! method_route {
|
|||
/// );
|
||||
/// ```
|
||||
pub fn $method_fn() -> Route {
|
||||
method(Method::$method_const)
|
||||
method($method)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -127,6 +130,9 @@ method_route!(patch, PATCH);
|
|||
method_route!(delete, DELETE);
|
||||
method_route!(head, HEAD);
|
||||
method_route!(trace, TRACE);
|
||||
// `QUERY` (RFC 10008) is a safe, idempotent method that carries a request body.
|
||||
// TODO: replace with `Method::QUERY` once the `http` dependency is bumped (see #3384).
|
||||
method_route!(query, QUERY, Method::from_bytes(b"QUERY").unwrap());
|
||||
|
||||
/// Creates a new route with specified method guard.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -2,11 +2,13 @@
|
|||
|
||||
## Unreleased
|
||||
|
||||
- Add `Client::query()` for constructing HTTP `QUERY` requests (RFC 10008). [#4140]
|
||||
- Add camel-case header controls to `WebsocketsRequest` via `camel_case_headers()` and `set_camel_case_headers()`. [#3953]
|
||||
- Update `hickory-resolver` dependency to `0.26.1`.
|
||||
- Update `rand` dependency to `0.10`.
|
||||
|
||||
[#3953]: https://github.com/actix/actix-web/pull/3953
|
||||
[#4140]: https://github.com/actix/actix-web/pull/4140
|
||||
|
||||
## 3.8.2
|
||||
|
||||
|
|
|
|||
|
|
@ -181,6 +181,16 @@ impl Client {
|
|||
self.request(Method::OPTIONS, url)
|
||||
}
|
||||
|
||||
/// Construct HTTP *QUERY* request.
|
||||
// TODO: use `Method::QUERY` once the `http` dependency is bumped (see #3384).
|
||||
pub fn query<U>(&self, url: U) -> ClientRequest
|
||||
where
|
||||
Uri: TryFrom<U>,
|
||||
<Uri as TryFrom<U>>::Error: Into<HttpError>,
|
||||
{
|
||||
self.request(Method::from_bytes(b"QUERY").unwrap(), url)
|
||||
}
|
||||
|
||||
/// Initialize a WebSocket connection.
|
||||
/// Returns a WebSocket connection builder.
|
||||
pub fn ws<U>(&self, url: U) -> ws::WebsocketsRequest
|
||||
|
|
@ -203,3 +213,14 @@ impl Client {
|
|||
Rc::get_mut(&mut self.0.default_headers)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn client_query_builds_query_request() {
|
||||
let req = Client::new().query("/");
|
||||
assert_eq!(req.get_method().as_str(), "QUERY");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue