docs(web): Add notes for request headers (#3921)

This commit is contained in:
Yuki Okushi 2026-02-12 21:28:02 +09:00 committed by GitHub
parent d66f89b7b6
commit 7d81d7b5c8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 157 additions and 127 deletions

View File

@ -26,49 +26,49 @@ common_header! {
/// accept-ext = OWS ";" OWS token [ "=" ( token / quoted-string ) ] /// accept-ext = OWS ";" OWS token [ "=" ( token / quoted-string ) ]
/// ``` /// ```
/// ///
/// # Note
/// This is a request header. Servers should not send `Accept` in responses; to describe the
/// response body media type, use [`ContentType`](super::ContentType) / the `Content-Type`
/// header instead.
///
/// # Example Values /// # Example Values
/// * `audio/*; q=0.2, audio/basic` /// * `audio/*; q=0.2, audio/basic`
/// * `text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c` /// * `text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c`
/// ///
/// # Examples /// # Examples
/// ``` /// ```
/// use actix_web::HttpResponse; /// use actix_web::{http::header::{Accept, QualityItem}, test};
/// use actix_web::http::header::{Accept, QualityItem};
/// ///
/// let mut builder = HttpResponse::Ok(); /// let req = test::TestRequest::default()
/// builder.insert_header( /// .insert_header(Accept(vec![QualityItem::max(mime::TEXT_HTML)]))
/// Accept(vec![ /// .to_http_request();
/// QualityItem::max(mime::TEXT_HTML), /// # let _ = req;
/// ])
/// );
/// ``` /// ```
/// ///
/// ``` /// ```
/// use actix_web::HttpResponse; /// use actix_web::{http::header::{Accept, QualityItem}, test};
/// use actix_web::http::header::{Accept, QualityItem};
/// ///
/// let mut builder = HttpResponse::Ok(); /// let req = test::TestRequest::default()
/// builder.insert_header( /// .insert_header(Accept(vec![QualityItem::max(mime::APPLICATION_JSON)]))
/// Accept(vec![ /// .to_http_request();
/// QualityItem::max(mime::APPLICATION_JSON), /// # let _ = req;
/// ])
/// );
/// ``` /// ```
/// ///
/// ``` /// ```
/// use actix_web::HttpResponse; /// use actix_web::{http::header::{Accept, Header as _, QualityItem, q}, test};
/// use actix_web::http::header::{Accept, QualityItem, q};
/// ///
/// let mut builder = HttpResponse::Ok(); /// let req = test::TestRequest::default()
/// builder.insert_header( /// .insert_header(Accept(vec![
/// Accept(vec![
/// QualityItem::max(mime::TEXT_HTML), /// QualityItem::max(mime::TEXT_HTML),
/// QualityItem::max("application/xhtml+xml".parse().unwrap()), /// QualityItem::max("application/xhtml+xml".parse().unwrap()),
/// QualityItem::new(mime::TEXT_XML, q(0.9)), /// QualityItem::new(mime::TEXT_XML, q(0.9)),
/// QualityItem::max("image/webp".parse().unwrap()), /// QualityItem::max("image/webp".parse().unwrap()),
/// QualityItem::new(mime::STAR_STAR, q(0.8)), /// QualityItem::new(mime::STAR_STAR, q(0.8)),
/// ]) /// ]))
/// ); /// .to_http_request();
///
/// let accept = Accept::parse(&req).unwrap();
/// assert_eq!(accept.preference(), mime::TEXT_HTML);
/// ``` /// ```
/// ///
/// [RFC 7231 §5.3.2]: https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.2 /// [RFC 7231 §5.3.2]: https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.2

View File

@ -10,6 +10,10 @@ common_header! {
/// to an origin server that is capable of representing information in /// to an origin server that is capable of representing information in
/// those charsets. /// those charsets.
/// ///
/// # Note
/// This is a request header. Servers should not send `Accept-Charset` in responses; to
/// describe the response body's charset, set an appropriate `Content-Type` header instead.
///
/// # ABNF /// # ABNF
/// ```plain /// ```plain
/// Accept-Charset = 1#( ( charset / "*" ) [ weight ] ) /// Accept-Charset = 1#( ( charset / "*" ) [ weight ] )
@ -20,36 +24,33 @@ common_header! {
/// ///
/// # Examples /// # Examples
/// ``` /// ```
/// use actix_web::HttpResponse; /// use actix_web::{http::header::{AcceptCharset, Charset, QualityItem}, test};
/// use actix_web::http::header::{AcceptCharset, Charset, QualityItem};
/// ///
/// let mut builder = HttpResponse::Ok(); /// let req = test::TestRequest::default()
/// builder.insert_header( /// .insert_header(AcceptCharset(vec![QualityItem::max(Charset::Us_Ascii)]))
/// AcceptCharset(vec![QualityItem::max(Charset::Us_Ascii)]) /// .to_http_request();
/// ); /// # let _ = req;
/// ``` /// ```
/// ///
/// ``` /// ```
/// use actix_web::HttpResponse; /// use actix_web::{http::header::{AcceptCharset, Charset, q, QualityItem}, test};
/// use actix_web::http::header::{AcceptCharset, Charset, q, QualityItem};
/// ///
/// let mut builder = HttpResponse::Ok(); /// let req = test::TestRequest::default()
/// builder.insert_header( /// .insert_header(AcceptCharset(vec![
/// AcceptCharset(vec![
/// QualityItem::new(Charset::Us_Ascii, q(0.9)), /// QualityItem::new(Charset::Us_Ascii, q(0.9)),
/// QualityItem::new(Charset::Iso_8859_10, q(0.2)), /// QualityItem::new(Charset::Iso_8859_10, q(0.2)),
/// ]) /// ]))
/// ); /// .to_http_request();
/// # let _ = req;
/// ``` /// ```
/// ///
/// ``` /// ```
/// use actix_web::HttpResponse; /// use actix_web::{http::header::{AcceptCharset, Charset, QualityItem}, test};
/// use actix_web::http::header::{AcceptCharset, Charset, QualityItem};
/// ///
/// let mut builder = HttpResponse::Ok(); /// let req = test::TestRequest::default()
/// builder.insert_header( /// .insert_header(AcceptCharset(vec![QualityItem::max(Charset::Ext("utf-8".to_owned()))]))
/// AcceptCharset(vec![QualityItem::max(Charset::Ext("utf-8".to_owned()))]) /// .to_http_request();
/// ); /// # let _ = req;
/// ``` /// ```
/// ///
/// [RFC 7231 §5.3.3]: https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.3 /// [RFC 7231 §5.3.3]: https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.3

View File

@ -11,6 +11,11 @@ common_header! {
/// content-codings are acceptable in the response. An `identity` token is used as a synonym /// content-codings are acceptable in the response. An `identity` token is used as a synonym
/// for "no encoding" in order to communicate when no encoding is preferred. /// for "no encoding" in order to communicate when no encoding is preferred.
/// ///
/// # Note
/// This is a request header. Servers should not send `Accept-Encoding` in responses; use the
/// `Content-Encoding` header (or middleware like compression) to describe any content-coding
/// applied to the response body.
///
/// # ABNF /// # ABNF
/// ```plain /// ```plain
/// Accept-Encoding = #( codings [ weight ] ) /// Accept-Encoding = #( codings [ weight ] )
@ -26,26 +31,26 @@ common_header! {
/// ///
/// # Examples /// # Examples
/// ``` /// ```
/// use actix_web::HttpResponse; /// use actix_web::{http::header::{AcceptEncoding, Encoding, Preference, QualityItem}, test};
/// use actix_web::http::header::{AcceptEncoding, Encoding, Preference, QualityItem};
/// ///
/// let mut builder = HttpResponse::Ok(); /// let req = test::TestRequest::default()
/// builder.insert_header( /// .insert_header(AcceptEncoding(vec![
/// AcceptEncoding(vec![QualityItem::max(Preference::Specific(Encoding::gzip()))]) /// QualityItem::max(Preference::Specific(Encoding::gzip())),
/// ); /// ]))
/// .to_http_request();
/// # let _ = req;
/// ``` /// ```
/// ///
/// ``` /// ```
/// use actix_web::HttpResponse; /// use actix_web::{http::header::{AcceptEncoding, QualityItem}, test};
/// use actix_web::http::header::{AcceptEncoding, Encoding, QualityItem};
/// ///
/// let mut builder = HttpResponse::Ok(); /// let req = test::TestRequest::default()
/// builder.insert_header( /// .insert_header(AcceptEncoding(vec![
/// AcceptEncoding(vec![
/// "gzip".parse().unwrap(), /// "gzip".parse().unwrap(),
/// "br".parse().unwrap(), /// "br".parse().unwrap(),
/// ]) /// ]))
/// ); /// .to_http_request();
/// # let _ = req;
/// ``` /// ```
(AcceptEncoding, header::ACCEPT_ENCODING) => (QualityItem<Preference<Encoding>>)* (AcceptEncoding, header::ACCEPT_ENCODING) => (QualityItem<Preference<Encoding>>)*

View File

@ -14,6 +14,10 @@ common_header! {
/// [RFC 7231 §5.3.5](https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.5) using language /// [RFC 7231 §5.3.5](https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.5) using language
/// ranges defined in [RFC 4647 §2.1](https://datatracker.ietf.org/doc/html/rfc4647#section-2.1). /// ranges defined in [RFC 4647 §2.1](https://datatracker.ietf.org/doc/html/rfc4647#section-2.1).
/// ///
/// # Note
/// This is a request header. Servers should not send `Accept-Language` in responses; use
/// `Content-Language` to describe the language of the response body.
///
/// # ABNF /// # ABNF
/// ```plain /// ```plain
/// Accept-Language = 1#( language-range [ weight ] ) /// Accept-Language = 1#( language-range [ weight ] )
@ -31,29 +35,25 @@ common_header! {
/// ///
/// # Examples /// # Examples
/// ``` /// ```
/// use actix_web::HttpResponse; /// use actix_web::{http::header::{AcceptLanguage, QualityItem}, test};
/// use actix_web::http::header::{AcceptLanguage, QualityItem};
/// ///
/// let mut builder = HttpResponse::Ok(); /// let req = test::TestRequest::default()
/// builder.insert_header( /// .insert_header(AcceptLanguage(vec!["en-US".parse().unwrap()]))
/// AcceptLanguage(vec![ /// .to_http_request();
/// "en-US".parse().unwrap(), /// # let _ = req;
/// ])
/// );
/// ``` /// ```
/// ///
/// ``` /// ```
/// use actix_web::HttpResponse; /// use actix_web::{http::header::{AcceptLanguage, q, QualityItem}, test};
/// use actix_web::http::header::{AcceptLanguage, QualityItem, q};
/// ///
/// let mut builder = HttpResponse::Ok(); /// let req = test::TestRequest::default()
/// builder.insert_header( /// .insert_header(AcceptLanguage(vec![
/// AcceptLanguage(vec![
/// "da".parse().unwrap(), /// "da".parse().unwrap(),
/// "en-GB;q=0.8".parse().unwrap(), /// "en-GB;q=0.8".parse().unwrap(),
/// "en;q=0.7".parse().unwrap(), /// "en;q=0.7".parse().unwrap(),
/// ]) /// ]))
/// ); /// .to_http_request();
/// # let _ = req;
/// ``` /// ```
(AcceptLanguage, header::ACCEPT_LANGUAGE) => (QualityItem<Preference<LanguageTag>>)* (AcceptLanguage, header::ACCEPT_LANGUAGE) => (QualityItem<Preference<LanguageTag>>)*

View File

@ -16,6 +16,11 @@ common_header! {
/// intends this precondition to prevent the method from being applied if /// intends this precondition to prevent the method from being applied if
/// there have been any changes to the representation data. /// there have been any changes to the representation data.
/// ///
/// # Note
/// This is a request header used for conditional requests (typically to avoid lost updates).
/// Servers should not send `If-Match` in responses; use [`ETag`](super::ETag) to describe the
/// current representation instead.
///
/// # ABNF /// # ABNF
/// ```plain /// ```plain
/// If-Match = "*" / 1#entity-tag /// If-Match = "*" / 1#entity-tag
@ -27,25 +32,25 @@ common_header! {
/// ///
/// # Examples /// # Examples
/// ``` /// ```
/// use actix_web::HttpResponse; /// use actix_web::{http::header::IfMatch, test};
/// use actix_web::http::header::IfMatch;
/// ///
/// let mut builder = HttpResponse::Ok(); /// let req = test::TestRequest::default()
/// builder.insert_header(IfMatch::Any); /// .insert_header(IfMatch::Any)
/// .to_http_request();
/// # let _ = req;
/// ``` /// ```
/// ///
/// ``` /// ```
/// use actix_web::HttpResponse; /// use actix_web::{http::header::{EntityTag, IfMatch}, test};
/// use actix_web::http::header::{IfMatch, EntityTag};
/// ///
/// let mut builder = HttpResponse::Ok(); /// let req = test::TestRequest::default()
/// builder.insert_header( /// .insert_header(IfMatch::Items(vec![
/// IfMatch::Items(vec![
/// EntityTag::new(false, "xyzzy".to_owned()), /// EntityTag::new(false, "xyzzy".to_owned()),
/// EntityTag::new(false, "foobar".to_owned()), /// EntityTag::new(false, "foobar".to_owned()),
/// EntityTag::new(false, "bazquux".to_owned()), /// EntityTag::new(false, "bazquux".to_owned()),
/// ]) /// ]))
/// ); /// .to_http_request();
/// # let _ = req;
/// ``` /// ```
(IfMatch, IF_MATCH) => {Any / (EntityTag)+} (IfMatch, IF_MATCH) => {Any / (EntityTag)+}

View File

@ -10,9 +10,14 @@ crate::http::header::common_header! {
/// Transfer of the selected representation's data is avoided if that /// Transfer of the selected representation's data is avoided if that
/// data has not changed. /// data has not changed.
/// ///
/// # Note
/// This is a request header used for cache validation. Servers should not send
/// `If-Modified-Since` in responses; use [`LastModified`](super::LastModified) / the
/// `Last-Modified` header instead.
///
/// # ABNF /// # ABNF
/// ```plain /// ```plain
/// If-Unmodified-Since = HTTP-date /// If-Modified-Since = HTTP-date
/// ``` /// ```
/// ///
/// # Example Values /// # Example Values
@ -22,14 +27,13 @@ crate::http::header::common_header! {
/// ///
/// ``` /// ```
/// use std::time::{SystemTime, Duration}; /// use std::time::{SystemTime, Duration};
/// use actix_web::HttpResponse; /// use actix_web::{http::header::IfModifiedSince, test};
/// use actix_web::http::header::IfModifiedSince;
/// ///
/// let mut builder = HttpResponse::Ok();
/// let modified = SystemTime::now() - Duration::from_secs(60 * 60 * 24); /// let modified = SystemTime::now() - Duration::from_secs(60 * 60 * 24);
/// builder.insert_header( /// let req = test::TestRequest::default()
/// IfModifiedSince(modified.into()) /// .insert_header(IfModifiedSince(modified.into()))
/// ); /// .to_http_request();
/// # let _ = req;
/// ``` /// ```
(IfModifiedSince, IF_MODIFIED_SINCE) => [HttpDate] (IfModifiedSince, IF_MODIFIED_SINCE) => [HttpDate]

View File

@ -15,6 +15,11 @@ crate::http::header::common_header! {
/// can be used for cache validation even if there have been changes to /// can be used for cache validation even if there have been changes to
/// the representation data. /// the representation data.
/// ///
/// # Note
/// This is a request header used for cache validation (and conditional requests). Servers
/// should not send `If-None-Match` in responses; use [`ETag`](super::ETag) to describe the
/// current representation instead.
///
/// # ABNF /// # ABNF
/// ```plain /// ```plain
/// If-None-Match = "*" / 1#entity-tag /// If-None-Match = "*" / 1#entity-tag
@ -29,25 +34,25 @@ crate::http::header::common_header! {
/// ///
/// # Examples /// # Examples
/// ``` /// ```
/// use actix_web::HttpResponse; /// use actix_web::{http::header::IfNoneMatch, test};
/// use actix_web::http::header::IfNoneMatch;
/// ///
/// let mut builder = HttpResponse::Ok(); /// let req = test::TestRequest::default()
/// builder.insert_header(IfNoneMatch::Any); /// .insert_header(IfNoneMatch::Any)
/// .to_http_request();
/// # let _ = req;
/// ``` /// ```
/// ///
/// ``` /// ```
/// use actix_web::HttpResponse; /// use actix_web::{http::header::{EntityTag, IfNoneMatch}, test};
/// use actix_web::http::header::{IfNoneMatch, EntityTag};
/// ///
/// let mut builder = HttpResponse::Ok(); /// let req = test::TestRequest::default()
/// builder.insert_header( /// .insert_header(IfNoneMatch::Items(vec![
/// IfNoneMatch::Items(vec![
/// EntityTag::new(false, "xyzzy".to_owned()), /// EntityTag::new(false, "xyzzy".to_owned()),
/// EntityTag::new(false, "foobar".to_owned()), /// EntityTag::new(false, "foobar".to_owned()),
/// EntityTag::new(false, "bazquux".to_owned()), /// EntityTag::new(false, "bazquux".to_owned()),
/// ]) /// ]))
/// ); /// .to_http_request();
/// # let _ = req;
/// ``` /// ```
(IfNoneMatch, IF_NONE_MATCH) => {Any / (EntityTag)+} (IfNoneMatch, IF_NONE_MATCH) => {Any / (EntityTag)+}

View File

@ -22,6 +22,9 @@ use crate::{error::ParseError, http::header, HttpMessage};
/// representation is unchanged, send me the part(s) that I am requesting /// representation is unchanged, send me the part(s) that I am requesting
/// in Range; otherwise, send me the entire representation. /// in Range; otherwise, send me the entire representation.
/// ///
/// # Note
/// This is a request header. Servers should not send `If-Range` in responses.
///
/// # ABNF /// # ABNF
/// ```plain /// ```plain
/// If-Range = entity-tag / HTTP-date /// If-Range = entity-tag / HTTP-date
@ -34,26 +37,23 @@ use crate::{error::ParseError, http::header, HttpMessage};
/// ///
/// # Examples /// # Examples
/// ``` /// ```
/// use actix_web::HttpResponse; /// use actix_web::{http::header::{EntityTag, IfRange}, test};
/// use actix_web::http::header::{EntityTag, IfRange};
/// ///
/// let mut builder = HttpResponse::Ok(); /// let req = test::TestRequest::default()
/// builder.insert_header( /// .insert_header(IfRange::EntityTag(EntityTag::new(false, "abc".to_owned())))
/// IfRange::EntityTag( /// .to_http_request();
/// EntityTag::new(false, "abc".to_owned()) /// # let _ = req;
/// )
/// );
/// ``` /// ```
/// ///
/// ``` /// ```
/// use std::time::{Duration, SystemTime}; /// use std::time::{Duration, SystemTime};
/// use actix_web::{http::header::IfRange, HttpResponse}; /// use actix_web::{http::header::IfRange, test};
/// ///
/// let mut builder = HttpResponse::Ok();
/// let fetched = SystemTime::now() - Duration::from_secs(60 * 60 * 24); /// let fetched = SystemTime::now() - Duration::from_secs(60 * 60 * 24);
/// builder.insert_header( /// let req = test::TestRequest::default()
/// IfRange::Date(fetched.into()) /// .insert_header(IfRange::Date(fetched.into()))
/// ); /// .to_http_request();
/// # let _ = req;
/// ``` /// ```
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub enum IfRange { pub enum IfRange {

View File

@ -10,6 +10,11 @@ crate::http::header::common_header! {
/// This field accomplishes the same purpose as If-Match for cases where /// This field accomplishes the same purpose as If-Match for cases where
/// the user agent does not have an entity-tag for the representation. /// the user agent does not have an entity-tag for the representation.
/// ///
/// # Note
/// This is a request header used for conditional requests. Servers should not send
/// `If-Unmodified-Since` in responses; use [`LastModified`](super::LastModified) / the
/// `Last-Modified` header instead.
///
/// # ABNF /// # ABNF
/// ```plain /// ```plain
/// If-Unmodified-Since = HTTP-date /// If-Unmodified-Since = HTTP-date
@ -22,14 +27,13 @@ crate::http::header::common_header! {
/// ///
/// ``` /// ```
/// use std::time::{SystemTime, Duration}; /// use std::time::{SystemTime, Duration};
/// use actix_web::HttpResponse; /// use actix_web::{http::header::IfUnmodifiedSince, test};
/// use actix_web::http::header::IfUnmodifiedSince;
/// ///
/// let mut builder = HttpResponse::Ok();
/// let modified = SystemTime::now() - Duration::from_secs(60 * 60 * 24); /// let modified = SystemTime::now() - Duration::from_secs(60 * 60 * 24);
/// builder.insert_header( /// let req = test::TestRequest::default()
/// IfUnmodifiedSince(modified.into()) /// .insert_header(IfUnmodifiedSince(modified.into()))
/// ); /// .to_http_request();
/// # let _ = req;
/// ``` /// ```
(IfUnmodifiedSince, IF_UNMODIFIED_SINCE) => [HttpDate] (IfUnmodifiedSince, IF_UNMODIFIED_SINCE) => [HttpDate]

View File

@ -15,6 +15,10 @@ use super::{Header, HeaderName, HeaderValue, InvalidHeaderValue, TryIntoHeaderVa
/// only one or more sub-ranges of the selected representation data, rather than the entire selected /// only one or more sub-ranges of the selected representation data, rather than the entire selected
/// representation data. /// representation data.
/// ///
/// # Note
/// This is a request header. Servers should not send `Range` in responses; use
/// [`ContentRange`](super::ContentRange) / the `Content-Range` header for partial responses.
///
/// # ABNF /// # ABNF
/// ```plain /// ```plain
/// Range = byte-ranges-specifier / other-ranges-specifier /// Range = byte-ranges-specifier / other-ranges-specifier
@ -42,16 +46,18 @@ use super::{Header, HeaderName, HeaderValue, InvalidHeaderValue, TryIntoHeaderVa
/// ///
/// # Examples /// # Examples
/// ``` /// ```
/// use actix_web::http::header::{Range, ByteRangeSpec}; /// use actix_web::{http::header::{ByteRangeSpec, Range}, test};
/// use actix_web::HttpResponse;
/// ///
/// let mut builder = HttpResponse::Ok(); /// let req = test::TestRequest::default()
/// builder.insert_header(Range::Bytes( /// .insert_header(Range::Bytes(vec![
/// vec![ByteRangeSpec::FromTo(1, 100), ByteRangeSpec::From(200)] /// ByteRangeSpec::FromTo(1, 100),
/// )); /// ByteRangeSpec::From(200),
/// builder.insert_header(Range::Unregistered("letters".to_owned(), "a-f".to_owned())); /// ]))
/// builder.insert_header(Range::bytes(1, 100)); /// .insert_header(Range::Unregistered("letters".to_owned(), "a-f".to_owned()))
/// builder.insert_header(Range::bytes_multi(vec![(1, 100), (200, 300)])); /// .insert_header(Range::bytes(1, 100))
/// .insert_header(Range::bytes_multi(vec![(1, 100), (200, 300)]))
/// .to_http_request();
/// # let _ = req;
/// ``` /// ```
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum Range { pub enum Range {