1## Unreleased 2 3* The default `NormalizePath` behavior now strips trailing slashes by default. This was 4 previously documented to be the case in v3 but the behavior now matches. The effect is that 5 routes defined with trailing slashes will become inaccessible when 6 using `NormalizePath::default()`. 7 8 Before: `#[get("/test/")` 9 After: `#[get("/test")` 10 11 Alternatively, explicitly require trailing slashes: `NormalizePath::new(TrailingSlash::Always)`. 12 13* Feature flag `compress` has been split into its supported algorithm (brotli, gzip, zstd). 14 By default all compression algorithms are enabled. 15 To select algorithm you want to include with `middleware::Compress` use following flags: 16 - `compress-brotli` 17 - `compress-gzip` 18 - `compress-zstd` 19 If you have set in your `Cargo.toml` dedicated `actix-web` features and you still want 20 to have compression enabled. Please change features selection like bellow: 21 22 Before: `"compress"` 23 After: `"compress-brotli", "compress-gzip", "compress-zstd"` 24 25 26## 3.0.0 27 28* The return type for `ServiceRequest::app_data::<T>()` was changed from returning a `Data<T>` to 29 simply a `T`. To access a `Data<T>` use `ServiceRequest::app_data::<Data<T>>()`. 30 31* Cookie handling has been offloaded to the `cookie` crate: 32 * `USERINFO_ENCODE_SET` is no longer exposed. Percent-encoding is still supported; check docs. 33 * Some types now require lifetime parameters. 34 35* The time crate was updated to `v0.2`, a major breaking change to the time crate, which affects 36 any `actix-web` method previously expecting a time v0.1 input. 37 38* Setting a cookie's SameSite property, explicitly, to `SameSite::None` will now 39 result in `SameSite=None` being sent with the response Set-Cookie header. 40 To create a cookie without a SameSite attribute, remove any calls setting same_site. 41 42* actix-http support for Actors messages was moved to actix-http crate and is enabled 43 with feature `actors` 44 45* content_length function is removed from actix-http. 46 You can set Content-Length by normally setting the response body or calling no_chunking function. 47 48* `BodySize::Sized64` variant has been removed. `BodySize::Sized` now receives a 49 `u64` instead of a `usize`. 50 51* Code that was using `path.<index>` to access a `web::Path<(A, B, C)>`s elements now needs to use 52 destructuring or `.into_inner()`. For example: 53 54 ```rust 55 // Previously: 56 async fn some_route(path: web::Path<(String, String)>) -> String { 57 format!("Hello, {} {}", path.0, path.1) 58 } 59 60 // Now (this also worked before): 61 async fn some_route(path: web::Path<(String, String)>) -> String { 62 let (first_name, last_name) = path.into_inner(); 63 format!("Hello, {} {}", first_name, last_name) 64 } 65 // Or (this wasn't previously supported): 66 async fn some_route(web::Path((first_name, last_name)): web::Path<(String, String)>) -> String { 67 format!("Hello, {} {}", first_name, last_name) 68 } 69 ``` 70 71* `middleware::NormalizePath` can now also be configured to trim trailing slashes instead of always keeping one. 72 It will need `middleware::normalize::TrailingSlash` when being constructed with `NormalizePath::new(...)`, 73 or for an easier migration you can replace `wrap(middleware::NormalizePath)` with `wrap(middleware::NormalizePath::new(TrailingSlash::MergeOnly))`. 74 75* `HttpServer::maxconn` is renamed to the more expressive `HttpServer::max_connections`. 76 77* `HttpServer::maxconnrate` is renamed to the more expressive `HttpServer::max_connection_rate`. 78 79 80## 2.0.0 81 82* `HttpServer::start()` renamed to `HttpServer::run()`. It also possible to 83 `.await` on `run` method result, in that case it awaits server exit. 84 85* `App::register_data()` renamed to `App::app_data()` and accepts any type `T: 'static`. 86 Stored data is available via `HttpRequest::app_data()` method at runtime. 87 88* Extractor configuration must be registered with `App::app_data()` instead of `App::data()` 89 90* Sync handlers has been removed. `.to_async()` method has been renamed to `.to()` 91 replace `fn` with `async fn` to convert sync handler to async 92 93* `actix_http_test::TestServer` moved to `actix_web::test` module. To start 94 test server use `test::start()` or `test_start_with_config()` methods 95 96* `ResponseError` trait has been reafctored. `ResponseError::error_response()` renders 97 http response. 98 99* Feature `rust-tls` renamed to `rustls` 100 101 instead of 102 103 ```rust 104 actix-web = { version = "2.0.0", features = ["rust-tls"] } 105 ``` 106 107 use 108 109 ```rust 110 actix-web = { version = "2.0.0", features = ["rustls"] } 111 ``` 112 113* Feature `ssl` renamed to `openssl` 114 115 instead of 116 117 ```rust 118 actix-web = { version = "2.0.0", features = ["ssl"] } 119 ``` 120 121 use 122 123 ```rust 124 actix-web = { version = "2.0.0", features = ["openssl"] } 125 ``` 126* `Cors` builder now requires that you call `.finish()` to construct the middleware 127 128## 1.0.1 129 130* Cors middleware has been moved to `actix-cors` crate 131 132 instead of 133 134 ```rust 135 use actix_web::middleware::cors::Cors; 136 ``` 137 138 use 139 140 ```rust 141 use actix_cors::Cors; 142 ``` 143 144* Identity middleware has been moved to `actix-identity` crate 145 146 instead of 147 148 ```rust 149 use actix_web::middleware::identity::{Identity, CookieIdentityPolicy, IdentityService}; 150 ``` 151 152 use 153 154 ```rust 155 use actix_identity::{Identity, CookieIdentityPolicy, IdentityService}; 156 ``` 157 158 159## 1.0.0 160 161* Extractor configuration. In version 1.0 this is handled with the new `Data` mechanism for both setting and retrieving the configuration 162 163 instead of 164 165 ```rust 166 167 #[derive(Default)] 168 struct ExtractorConfig { 169 config: String, 170 } 171 172 impl FromRequest for YourExtractor { 173 type Config = ExtractorConfig; 174 type Result = Result<YourExtractor, Error>; 175 176 fn from_request(req: &HttpRequest, cfg: &Self::Config) -> Self::Result { 177 println!("use the config: {:?}", cfg.config); 178 ... 179 } 180 } 181 182 App::new().resource("/route_with_config", |r| { 183 r.post().with_config(handler_fn, |cfg| { 184 cfg.0.config = "test".to_string(); 185 }) 186 }) 187 188 ``` 189 190 use the HttpRequest to get the configuration like any other `Data` with `req.app_data::<C>()` and set it with the `data()` method on the `resource` 191 192 ```rust 193 #[derive(Default)] 194 struct ExtractorConfig { 195 config: String, 196 } 197 198 impl FromRequest for YourExtractor { 199 type Error = Error; 200 type Future = Result<Self, Self::Error>; 201 type Config = ExtractorConfig; 202 203 fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future { 204 let cfg = req.app_data::<ExtractorConfig>(); 205 println!("config data?: {:?}", cfg.unwrap().role); 206 ... 207 } 208 } 209 210 App::new().service( 211 resource("/route_with_config") 212 .data(ExtractorConfig { 213 config: "test".to_string(), 214 }) 215 .route(post().to(handler_fn)), 216 ) 217 ``` 218 219* Resource registration. 1.0 version uses generalized resource 220 registration via `.service()` method. 221 222 instead of 223 224 ```rust 225 App.new().resource("/welcome", |r| r.f(welcome)) 226 ``` 227 228 use App's or Scope's `.service()` method. `.service()` method accepts 229 object that implements `HttpServiceFactory` trait. By default 230 actix-web provides `Resource` and `Scope` services. 231 232 ```rust 233 App.new().service( 234 web::resource("/welcome") 235 .route(web::get().to(welcome)) 236 .route(web::post().to(post_handler)) 237 ``` 238 239* Scope registration. 240 241 instead of 242 243 ```rust 244 let app = App::new().scope("/{project_id}", |scope| { 245 scope 246 .resource("/path1", |r| r.f(|_| HttpResponse::Ok())) 247 .resource("/path2", |r| r.f(|_| HttpResponse::Ok())) 248 .resource("/path3", |r| r.f(|_| HttpResponse::MethodNotAllowed())) 249 }); 250 ``` 251 252 use `.service()` for registration and `web::scope()` as scope object factory. 253 254 ```rust 255 let app = App::new().service( 256 web::scope("/{project_id}") 257 .service(web::resource("/path1").to(|| HttpResponse::Ok())) 258 .service(web::resource("/path2").to(|| HttpResponse::Ok())) 259 .service(web::resource("/path3").to(|| HttpResponse::MethodNotAllowed())) 260 ); 261 ``` 262 263* `.with()`, `.with_async()` registration methods have been renamed to `.to()` and `.to_async()`. 264 265 instead of 266 267 ```rust 268 App.new().resource("/welcome", |r| r.with(welcome)) 269 ``` 270 271 use `.to()` or `.to_async()` methods 272 273 ```rust 274 App.new().service(web::resource("/welcome").to(welcome)) 275 ``` 276 277* Passing arguments to handler with extractors, multiple arguments are allowed 278 279 instead of 280 281 ```rust 282 fn welcome((body, req): (Bytes, HttpRequest)) -> ... { 283 ... 284 } 285 ``` 286 287 use multiple arguments 288 289 ```rust 290 fn welcome(body: Bytes, req: HttpRequest) -> ... { 291 ... 292 } 293 ``` 294 295* `.f()`, `.a()` and `.h()` handler registration methods have been removed. 296 Use `.to()` for handlers and `.to_async()` for async handlers. Handler function 297 must use extractors. 298 299 instead of 300 301 ```rust 302 App.new().resource("/welcome", |r| r.f(welcome)) 303 ``` 304 305 use App's `to()` or `to_async()` methods 306 307 ```rust 308 App.new().service(web::resource("/welcome").to(welcome)) 309 ``` 310 311* `HttpRequest` does not provide access to request's payload stream. 312 313 instead of 314 315 ```rust 316 fn index(req: &HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> { 317 req 318 .payload() 319 .from_err() 320 .fold((), |_, chunk| { 321 ... 322 }) 323 .map(|_| HttpResponse::Ok().finish()) 324 .responder() 325 } 326 ``` 327 328 use `Payload` extractor 329 330 ```rust 331 fn index(stream: web::Payload) -> impl Future<Item=HttpResponse, Error=Error> { 332 stream 333 .from_err() 334 .fold((), |_, chunk| { 335 ... 336 }) 337 .map(|_| HttpResponse::Ok().finish()) 338 } 339 ``` 340 341* `State` is now `Data`. You register Data during the App initialization process 342 and then access it from handlers either using a Data extractor or using 343 HttpRequest's api. 344 345 instead of 346 347 ```rust 348 App.with_state(T) 349 ``` 350 351 use App's `data` method 352 353 ```rust 354 App.new() 355 .data(T) 356 ``` 357 358 and either use the Data extractor within your handler 359 360 ```rust 361 use actix_web::web::Data; 362 363 fn endpoint_handler(Data<T>)){ 364 ... 365 } 366 ``` 367 368 .. or access your Data element from the HttpRequest 369 370 ```rust 371 fn endpoint_handler(req: HttpRequest) { 372 let data: Option<Data<T>> = req.app_data::<T>(); 373 } 374 ``` 375 376 377* AsyncResponder is removed, use `.to_async()` registration method and `impl Future<>` as result type. 378 379 instead of 380 381 ```rust 382 use actix_web::AsyncResponder; 383 384 fn endpoint_handler(...) -> impl Future<Item=HttpResponse, Error=Error>{ 385 ... 386 .responder() 387 } 388 ``` 389 390 .. simply omit AsyncResponder and the corresponding responder() finish method 391 392 393* Middleware 394 395 instead of 396 397 ```rust 398 let app = App::new() 399 .middleware(middleware::Logger::default()) 400 ``` 401 402 use `.wrap()` method 403 404 ```rust 405 let app = App::new() 406 .wrap(middleware::Logger::default()) 407 .route("/index.html", web::get().to(index)); 408 ``` 409 410* `HttpRequest::body()`, `HttpRequest::urlencoded()`, `HttpRequest::json()`, `HttpRequest::multipart()` 411 method have been removed. Use `Bytes`, `String`, `Form`, `Json`, `Multipart` extractors instead. 412 413 instead of 414 415 ```rust 416 fn index(req: &HttpRequest) -> Responder { 417 req.body() 418 .and_then(|body| { 419 ... 420 }) 421 } 422 ``` 423 424 use 425 426 ```rust 427 fn index(body: Bytes) -> Responder { 428 ... 429 } 430 ``` 431 432* `actix_web::server` module has been removed. To start http server use `actix_web::HttpServer` type 433 434* StaticFiles and NamedFile have been moved to a separate crate. 435 436 instead of `use actix_web::fs::StaticFile` 437 438 use `use actix_files::Files` 439 440 instead of `use actix_web::fs::Namedfile` 441 442 use `use actix_files::NamedFile` 443 444* Multipart has been moved to a separate crate. 445 446 instead of `use actix_web::multipart::Multipart` 447 448 use `use actix_multipart::Multipart` 449 450* Response compression is not enabled by default. 451 To enable, use `Compress` middleware, `App::new().wrap(Compress::default())`. 452 453* Session middleware moved to actix-session crate 454 455* Actors support have been moved to `actix-web-actors` crate 456 457* Custom Error 458 459 Instead of error_response method alone, ResponseError now provides two methods: error_response and render_response respectively. Where, error_response creates the error response and render_response returns the error response to the caller. 460 461 Simplest migration from 0.7 to 1.0 shall include below method to the custom implementation of ResponseError: 462 463 ```rust 464 fn render_response(&self) -> HttpResponse { 465 self.error_response() 466 } 467 ``` 468 469## 0.7.15 470 471* The `' '` character is not percent decoded anymore before matching routes. If you need to use it in 472 your routes, you should use `%20`. 473 474 instead of 475 476 ```rust 477 fn main() { 478 let app = App::new().resource("/my index", |r| { 479 r.method(http::Method::GET) 480 .with(index); 481 }); 482 } 483 ``` 484 485 use 486 487 ```rust 488 fn main() { 489 let app = App::new().resource("/my%20index", |r| { 490 r.method(http::Method::GET) 491 .with(index); 492 }); 493 } 494 ``` 495 496* If you used `AsyncResult::async` you need to replace it with `AsyncResult::future` 497 498 499## 0.7.4 500 501* `Route::with_config()`/`Route::with_async_config()` always passes configuration objects as tuple 502 even for handler with one parameter. 503 504 505## 0.7 506 507* `HttpRequest` does not implement `Stream` anymore. If you need to read request payload 508 use `HttpMessage::payload()` method. 509 510 instead of 511 512 ```rust 513 fn index(req: HttpRequest) -> impl Responder { 514 req 515 .from_err() 516 .fold(...) 517 .... 518 } 519 ``` 520 521 use `.payload()` 522 523 ```rust 524 fn index(req: HttpRequest) -> impl Responder { 525 req 526 .payload() // <- get request payload stream 527 .from_err() 528 .fold(...) 529 .... 530 } 531 ``` 532 533* [Middleware](https://actix.rs/actix-web/actix_web/middleware/trait.Middleware.html) 534 trait uses `&HttpRequest` instead of `&mut HttpRequest`. 535 536* Removed `Route::with2()` and `Route::with3()` use tuple of extractors instead. 537 538 instead of 539 540 ```rust 541 fn index(query: Query<..>, info: Json<MyStruct) -> impl Responder {} 542 ``` 543 544 use tuple of extractors and use `.with()` for registration: 545 546 ```rust 547 fn index((query, json): (Query<..>, Json<MyStruct)) -> impl Responder {} 548 ``` 549 550* `Handler::handle()` uses `&self` instead of `&mut self` 551 552* `Handler::handle()` accepts reference to `HttpRequest<_>` instead of value 553 554* Removed deprecated `HttpServer::threads()`, use 555 [HttpServer::workers()](https://actix.rs/actix-web/actix_web/server/struct.HttpServer.html#method.workers) instead. 556 557* Renamed `client::ClientConnectorError::Connector` to 558 `client::ClientConnectorError::Resolver` 559 560* `Route::with()` does not return `ExtractorConfig`, to configure 561 extractor use `Route::with_config()` 562 563 instead of 564 565 ```rust 566 fn main() { 567 let app = App::new().resource("/index.html", |r| { 568 r.method(http::Method::GET) 569 .with(index) 570 .limit(4096); // <- limit size of the payload 571 }); 572 } 573 ``` 574 575 use 576 577 ```rust 578 579 fn main() { 580 let app = App::new().resource("/index.html", |r| { 581 r.method(http::Method::GET) 582 .with_config(index, |cfg| { // <- register handler 583 cfg.limit(4096); // <- limit size of the payload 584 }) 585 }); 586 } 587 ``` 588 589* `Route::with_async()` does not return `ExtractorConfig`, to configure 590 extractor use `Route::with_async_config()` 591 592 593## 0.6 594 595* `Path<T>` extractor return `ErrorNotFound` on failure instead of `ErrorBadRequest` 596 597* `ws::Message::Close` now includes optional close reason. 598 `ws::CloseCode::Status` and `ws::CloseCode::Empty` have been removed. 599 600* `HttpServer::threads()` renamed to `HttpServer::workers()`. 601 602* `HttpServer::start_ssl()` and `HttpServer::start_tls()` deprecated. 603 Use `HttpServer::bind_ssl()` and `HttpServer::bind_tls()` instead. 604 605* `HttpRequest::extensions()` returns read only reference to the request's Extension 606 `HttpRequest::extensions_mut()` returns mutable reference. 607 608* Instead of 609 610 `use actix_web::middleware::{ 611 CookieSessionBackend, CookieSessionError, RequestSession, 612 Session, SessionBackend, SessionImpl, SessionStorage};` 613 614 use `actix_web::middleware::session` 615 616 `use actix_web::middleware::session{CookieSessionBackend, CookieSessionError, 617 RequestSession, Session, SessionBackend, SessionImpl, SessionStorage};` 618 619* `FromRequest::from_request()` accepts mutable reference to a request 620 621* `FromRequest::Result` has to implement `Into<Reply<Self>>` 622 623* [`Responder::respond_to()`]( 624 https://actix.rs/actix-web/actix_web/trait.Responder.html#tymethod.respond_to) 625 is generic over `S` 626 627* Use `Query` extractor instead of HttpRequest::query()`. 628 629 ```rust 630 fn index(q: Query<HashMap<String, String>>) -> Result<..> { 631 ... 632 } 633 ``` 634 635 or 636 637 ```rust 638 let q = Query::<HashMap<String, String>>::extract(req); 639 ``` 640 641* Websocket operations are implemented as `WsWriter` trait. 642 you need to use `use actix_web::ws::WsWriter` 643 644 645## 0.5 646 647* `HttpResponseBuilder::body()`, `.finish()`, `.json()` 648 methods return `HttpResponse` instead of `Result<HttpResponse>` 649 650* `actix_web::Method`, `actix_web::StatusCode`, `actix_web::Version` 651 moved to `actix_web::http` module 652 653* `actix_web::header` moved to `actix_web::http::header` 654 655* `NormalizePath` moved to `actix_web::http` module 656 657* `HttpServer` moved to `actix_web::server`, added new `actix_web::server::new()` function, 658 shortcut for `actix_web::server::HttpServer::new()` 659 660* `DefaultHeaders` middleware does not use separate builder, all builder methods moved to type itself 661 662* `StaticFiles::new()`'s show_index parameter removed, use `show_files_listing()` method instead. 663 664* `CookieSessionBackendBuilder` removed, all methods moved to `CookieSessionBackend` type 665 666* `actix_web::httpcodes` module is deprecated, `HttpResponse::Ok()`, `HttpResponse::Found()` and other `HttpResponse::XXX()` 667 functions should be used instead 668 669* `ClientRequestBuilder::body()` returns `Result<_, actix_web::Error>` 670 instead of `Result<_, http::Error>` 671 672* `Application` renamed to a `App` 673 674* `actix_web::Reply`, `actix_web::Resource` moved to `actix_web::dev` 675