diff --git a/.gitignore b/.gitignore index 465343d50..442399918 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .project .sass-cache/ output/ +includes/ .project .idea .metals diff --git a/BUILD.adoc b/BUILD.adoc index 8bbc37dec..d153c825e 100644 --- a/BUILD.adoc +++ b/BUILD.adoc @@ -27,6 +27,15 @@ to the `all`-target. **Note:** We have currently not tested the capability of `markdownlint` (or `make format`) to fix linter violations. +== Find next Rule ID + +If you want to create a new rule, you need to define a new rule identifier. Use +the following command to find the next unused rule identifier. + +[source,bash] +---- +make next-rule-id +---- == Generate Custom CSS diff --git a/Makefile b/Makefile index 9f084667a..44268b11b 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,7 @@ DIRMOUNTS := /documents DIRCONTENTS := chapters DIRSCRIPTS := scripts DIRBUILDS := output +DIRINCLUDES := includes DIRWORK := $(shell pwd -P) .PHONY: all clean install lint format pull assets rules html pdf epub force @@ -12,7 +13,7 @@ DIRWORK := $(shell pwd -P) all: clean html rules clean: - rm -rf $(DIRBUILDS); + rm -rf $(DIRBUILDS) $(DIRINCLUDES); install: $(NVM_BIN)/markdownlint $(NVM_BIN)/markdownlint: @@ -58,12 +59,15 @@ rules: check-rules $(DIRSCRIPTS)/generate-rules-json.sh | \ jq -s '{rules: . | sort}' | tee $(DIRBUILDS)/rules >$(DIRBUILDS)/rules.json; -html: check assets pull +$(DIRINCLUDES): models/headers-1.0.0.yaml $(DIRSCRIPTS)/generate-includes.sh + mkdir -p $(DIRINCLUDES); $(DIRSCRIPTS)/generate-includes.sh "$(DIRINCLUDES)"; + +html: $(DIRINCLUDES) check assets pull docker run -v $(DIRWORK):$(DIRMOUNTS)/ ${DOCKER} asciidoctor \ -D $(DIRMOUNTS)/$(DIRBUILDS) index.adoc; # Not used any longer. -pdf: check pull +pdf: $(DIRINCLUDES) check pull docker run -v $(DIRWORK):$(DIRMOUNTS)/ ${DOCKER} asciidoctor-pdf -v \ -a pdf-fontsdir=$(DIRMOUNTS)/resources/fonts \ -a pdf-theme=$(DIRMOUNTS)/resources/themes/pdf-theme.yml \ @@ -71,7 +75,7 @@ pdf: check pull mv -f $(DIRBUILDS)/index.pdf $(DIRBUILDS)/zalando-guidelines.pdf; # Not used any longer. -epub: check pull +epub: $(DIRINCLUDES) check pull docker run -v $(DIRWORK):$(DIRMOUNTS)/ ${DOCKER} asciidoctor-epub3 \ -D $(DIRMOUNTS)/$(DIRBUILDS) index.adoc; mv -f $(DIRBUILDS)/index.epub $(DIRBUILDS)/zalando-guidelines.epub; diff --git a/README.adoc b/README.adoc index f333e9664..f4d0a725d 100644 --- a/README.adoc +++ b/README.adoc @@ -4,8 +4,6 @@ https://github.com/zalando/restful-api-guidelines/actions/[image:https://github.com/zalando/restful-api-guidelines/actions/workflows/build.yml/badge.svg[Build status]] Latest published version: http://zalando.github.io/restful-api-guidelines/[*HTML*], -http://zalando.github.io/restful-api-guidelines/zalando-guidelines.pdf[*PDF*], -http://zalando.github.io/restful-api-guidelines/zalando-guidelines.epub[*EPUB3*] == Purpose diff --git a/chapters/compatibility.adoc b/chapters/compatibility.adoc index 78a4ff532..11b047dc8 100644 --- a/chapters/compatibility.adoc +++ b/chapters/compatibility.adoc @@ -87,7 +87,7 @@ and be explicit in what is supported: * Ignoring unknown input fields is actually not an option for {PUT}, since it becomes asymmetric with subsequent {GET} response and HTTP is clear about the {PUT} _replace_ semantics and default roundtrip expectations (see - {RFC-7231}#section-4.3.4[RFC 7231 Section 4.3.4]). Note, accepting (i.e. not + {RFC-9110}#section-9.3.4[RFC 9110 Section 9.3.4]). Note, accepting (i.e. not ignoring) unknown input fields and returning it in subsequent {GET} responses is a different situation and compliant to {PUT} semantics. * Certain client errors cannot be recognized by servers, e.g. attribute name @@ -126,7 +126,7 @@ Service clients should apply the robustness principle: ** Be prepared to handle HTTP status codes not explicitly specified in endpoint definitions. Note also, that status codes are extensible. Default handling is how you would treat the corresponding {x00} code (see - {RFC-7231}#section-6[RFC 7231 Section 6]). + {RFC-9110}#section-15[RFC 9110 Section 15]). ** Follow the redirect when the server returns HTTP status code {301} (Moved Permanently). diff --git a/chapters/deprecation.adoc b/chapters/deprecation.adoc index 16bb51ff3..b6fe37ca3 100644 --- a/chapters/deprecation.adoc +++ b/chapters/deprecation.adoc @@ -62,8 +62,8 @@ breaking effects on ongoing consumers. See also <<193>>. During the deprecation phase, the producer should add a `Deprecation: ` (see https://tools.ietf.org/html/draft-ietf-httpapi-deprecation-header[draft: RFC -Deprecation HTTP Header]) and - if also planned - a `Sunset: ` (see -{RFC-8594}#section-3[RFC 8594]) header on each response affected by a +Deprecation HTTP Header Field]) and - if also planned - a `Sunset: ` +(see {RFC-8594}#section-3[RFC 8594]) header on each response affected by a deprecated element (see <<187>>). The {Deprecation} header can either be set to `true` - if a feature is retired @@ -81,17 +81,35 @@ Sunset: Wed, 31 Dec 2025 23:59:59 GMT If multiple elements are deprecated the {Deprecation} and {Sunset} headers are expected to be set to the earliest time stamp to reflect the shortest interval -consumers are expected to get active. +at which consumers are expected to get active. The {Deprecation} and {Sunset} +headers can be defined as follows in the API specification (see also the +default definition below): + +[source,yaml] +---- +components: + parameters|headers: + Deprecation: + $ref: 'https://opensource.zalando.com/restful-api-guidelines/models/headers-1.0.0.yaml#/Deprecation' + Sunset: + $ref: 'https://opensource.zalando.com/restful-api-guidelines/models/headers-1.0.0.yaml#/Sunset' +---- + +[source,yaml] +---- +include::../includes/deprecation.yaml[] +include::../includes/sunset.yaml[lines=3..-1] +---- *Note:* adding the {Deprecation} and {Sunset} header is not sufficient to gain client consent to shut down an API or feature. -*Hint:* In earlier guideline versions, we used the `Warning` header to provide -the deprecation info to clients. However, `Warning` header has a less specific +*Hint:* In earlier guideline versions, we used the {Warning} header to provide +the deprecation info to clients. However, {Warning} header has a less specific semantics, will be obsolete with https://tools.ietf.org/html/draft-ietf-httpbis-cache-06[draft: RFC HTTP -Caching], and our syntax was not compliant with {RFC-7234}#page-29[RFC 7234 --- Warning header]. +Caching], and our syntax was not compliant with {RFC-9111}#section-5.5[RFC 9111 + Section 5.5 "Warning"]. [#190] @@ -105,6 +123,7 @@ ensure alignment with service owners on required migration task. *Hint:* In earlier guideline versions, we used the `Warning` header to provide the deprecation info (see hint in <<189>>). + [#191] == {MUST} not start using deprecated APIs diff --git a/chapters/http-headers.adoc b/chapters/http-headers.adoc index 813225bcb..c83a1be36 100644 --- a/chapters/http-headers.adoc +++ b/chapters/http-headers.adoc @@ -2,29 +2,70 @@ = REST Basics - HTTP headers [[standard-headers]] -// hint: legacy anchor positioned here - acsciidoc does not support two anchors in sequence -We describe a handful of standard HTTP headers, which we found raising the most questions in -our daily usage, or which are useful in particular circumstances but not widely known. +// hint: legacy anchor positioned here +// acsciidoc does not support two anchors in sequence -Though we generally discourage usage of proprietary headers, they are useful to pass generic, -service independent, overarching information relevant for our specific application architecture. -We consistently define these proprietary headers in this section below. Whether services support -these concerns or not is optional. Therefore, the OpenAPI API specification is the right place -to make this explicitly visible -- use the parameter definitions of the resource HTTP methods. +In this section we explain a handful of (standard) HTTP headers, which we found +most useful in certain situations and that require additional explanation to be +used effectively. + +*Note:* we generally discourage the usage of proprietary headers. However, +they are sometimes useful to pass generic, service independent, overarching +information relevant for our specific application architecture. We consistently +define these proprietary headers below. + +Whether a service supports a certain header is part of the service contract. +Therefore APIs should use the `parameters` and `headers` definition of the API +endpoint and response to clarify what is supported in a certain context. + +[[using--headers]] +== Using Standard Header definitions + +Usually, you can the standard HTTP request and response header definition +provided by the guideline to simplify API by using well recognized patterns. +The best practice importing headers providing the highest readability is as +follows: + +[source,yaml] +---- +path: + '/resource' + get: + parameters: + - $ref: '#/components/parameters/ETag + +components: + parameters|headers: + ETag: + $ref: 'https://opensource.zalando.com/restful-api-guidelines/models/headers-1.0.0.yaml#/ETag' + + responses: + Default: + headers: + ETag: + $ref: '#/components/(parameters|headers)/ETag +---- + +*Note:* It is a question of taste whether headers for responses are defined in +`\#/components/headers` or `#/components/parameters`. Unfortunately, headers +in the first section cannot be referenced from a `parameters`-list, while it is +possible to reference the second also from a `headers`-list. [#133] == {MAY} use standard headers -Use http://en.wikipedia.org/wiki/List_of_HTTP_header_fields[this list] and -explicitly mention its support in your OpenAPI definition. +APIs may make use of HTTP headers defined by non-obsolete RFCs (see following +list of http://en.wikipedia.org/wiki/List_of_HTTP_header_fields[standard HTTP +headers]). The supported headers must be explicitly mentioned in the API +specification. [#132] == {SHOULD} use kebab-case with uppercase separate words for HTTP headers -This convention is followed by (most of) the standard headers e.g. as defined in -{RFC-2616}[RFC 2616] and {RFC-4229}[RFC 4229]. Examples: +This convention is followed by (most of) the standard headers e.g. as defined +in {RFC-2616}[RFC 2616], {RFC-4229}[RFC 4229], {RFC-9110}[RFC 9110]. Examples: [source,http] ---- @@ -34,9 +75,11 @@ Content-ID Language ---- -Note, HTTP standard defines headers as case-insensitive ({RFC-7230}#page-22[RFC 7230, p.22]). -However, for sake of readability and consistency you should follow the convention when -using standard or proprietary headers. Exceptions are common abbreviations like `ID`. +*Note:* The HTTP standard defines headers (now called HTTP fields) to be +case-insensitive (see {RFC-9110}#section-5.1[RFC 9110 Section 5.1]). However, +for the sake of readability and consistency, you should follow the convention +when defining standard or proprietary headers. Exceptions are common +abbreviations like `ID`. [#178] @@ -64,27 +107,28 @@ limited to: [#180] == {SHOULD} use `Location` header instead of `Content-Location` header -As the correct usage of {Content-Location} response header (see below) with respect -to caching and its method specific semantics is difficult, we _discourage_ the use -of {Content-Location}. -In most cases it is sufficient to inform clients about the resource location -in create or re-direct responses by using the {Location} header while avoiding -the {Content-Location} specific ambiguities and complexities. +As a correct usage of the {Content-Location} response header (see <<179>>) +with respect to caching and its method specific semantics is difficult, we +*discourage* the use of {Content-Location}. In most cases it is sufficient to +inform clients about the resource location in create or re-direct responses by +using the {Location} header while avoiding the {Content-Location} specific +ambiguities and complexities. -More details in RFC 7231 {RFC-7231}#section-7.1.2[7.1.2 Location], -{RFC-7231}#section-3.1.4.2[3.1.4.2 Content-Location] +For more details see {RFC-9110}#section-10.2.2[RFC 9110 Section 10.2.2 +Location], {RFC-9110}#section-8.7[RFC 9110 Section 8.2 Content-Location] [#179] == {MAY} use `Content-Location` header -{Content-Location} is an _optional_ response header that can be used in successful write -operations ({PUT}, {POST}, or {PATCH}) or read operations ({GET}, {HEAD}) to -guide caching and signal a receiver the actual location of the resource -transmitted in the response body. This allows clients to identify the resource -and to update their local copy when receiving a response with this header. +The {Content-Location} response header is an _optional_ header that can be used +in successful write operations ({PUT}, {POST}, or {PATCH}) and read operations +({GET}, {HEAD}) to guide caching and signal a receiver the actual location of +the resource transmitted in the response body. This allows clients to identify +the resource and to update their local copy when receiving a response with this +header. -The Content-Location header can be used to support the following use cases: +The {Content-Location} header can be used to support the following use cases: * For reading operations {GET} and {HEAD}, a different location than the requested URL can be used to indicate that the returned resource is subject @@ -97,7 +141,7 @@ The Content-Location header can be used to support the following use cases: indicate that the body contains a status report resource in response to the requested action, which is available at provided location. -*Note*: When using the {Content-Location} header, the {Content-Type} header +*Note:* When using the {Content-Location} header, the {Content-Type} header has to be set as well. For example: [source,http] @@ -109,6 +153,8 @@ Content-Type: image/png Content-Location: /products/123/images?format=raw ---- +See also <<227>>. + [#181] == {MAY} consider to support `Prefer` header to handle processing preferences @@ -120,39 +166,14 @@ is entirely optional and at the discretion of API designers, but as an existing Internet Standard, is recommended over defining proprietary "X-" headers for processing directives. -The {Prefer} header can defined like this in an API definition: +The {Prefer} header can be defined in the API specification as follows: [source,yaml] ---- -components: - headers: - - Prefer: - description: > - The RFC7240 Prefer header indicates that a particular server behavior - is preferred by the client but is not required for successful completion - of the request (see [RFC 7240](https://tools.ietf.org/html/rfc7240). - The following behaviors are supported by this API: - - # (indicate the preferences supported by the API or API endpoint) - * **respond-async** is used to suggest the server to respond as fast as - possible asynchronously using 202 - accepted - instead of waiting for - the result. - * **return=** is used to suggest the server to - return using 204 without resource (minimal) or using 200 or 201 with - resource (representation) in the response body on success. - * **wait=** is used to suggest a maximum time the server - has time to process the request synchronously. - * **handling=** is used to suggest the server to be - strict and report error conditions or lenient, i.e. robust and try to - continue, if possible. - - type: array - items: - type: string - required: false +include::../includes/prefer.yaml[] ---- -*Note:* Please copy only the behaviors into your {Prefer} header specification +*Note:* Please, copy only the behaviors into your {Prefer} header specification that are supported by your API endpoint. If necessary, specify different {Prefer} headers for each supported use case. @@ -164,12 +185,13 @@ Supporting APIs may return the {Preference-Applied} header also defined in == {MAY} consider to support `ETag` together with `If-Match`/`If-None-Match` header When creating or updating resources it may be necessary to expose conflicts -and to prevent the 'lost update' or 'initially created' problem. Following -{RFC-7232}[RFC 7232 "HTTP: Conditional Requests"] this can be best accomplished -by supporting the {ETag} header together with the {If-Match} or {If-None-Match} -conditional header. The contents of an `ETag: ` header is either -(a) a hash of the response body, (b) a hash of the last modified field of the -entity, or (c) a version number or identifier of the entity version. +and to prevent the _lost update_ or _initially created_ problem. Following +{RFC-9110}[RFC 9110 Section 13 "Conditional Requests"] this can be best +accomplished by supporting the {ETag} header together with the {If-Match} or +{If-None-Match} conditional header. The contents of an `ETag: ` +header is either (a) a hash of the response body, (b) a hash of the last +modified field of the entity, or (c) a version number or identifier of the +entity version. To expose conflicts between concurrent update operations via {PUT}, {POST}, or {PATCH}, the `If-Match: ` header can be used to force the server to @@ -183,52 +205,29 @@ operation is supposed a to respond with status code {412} - precondition failed. The {ETag}, {If-Match}, and {If-None-Match} headers can be defined as follows -in the API definition: +in the API specification (see also the default definition below): [source,yaml] ---- components: - headers: - - ETag: - description: | - The RFC 7232 ETag header field in a response provides the entity-tag of - a selected resource. The entity-tag is an opaque identifier for versions - and representations of the same resource over time, regardless whether - multiple versions are valid at the same time. An entity-tag consists of - an opaque quoted string, possibly prefixed by a weakness indicator (see - [RFC 7232 Section 2.3](https://tools.ietf.org/html/rfc7232#section-2.3). - - type: string - required: false - example: W/"xy", "5", "5db68c06-1a68-11e9-8341-68f728c1ba70" - - - If-Match: - description: | - The RFC7232 If-Match header field in a request requires the server to - only operate on the resource that matches at least one of the provided - entity-tags. This allows clients express a precondition that prevent - the method from being applied if there have been any changes to the - resource (see [RFC 7232 Section - 3.1](https://tools.ietf.org/html/rfc7232#section-3.1). - - type: string - required: false - example: "5", "7da7a728-f910-11e6-942a-68f728c1ba70" - - - If-None-Match: - description: | - The RFC7232 If-None-Match header field in a request requires the server - to only operate on the resource if it does not match any of the provided - entity-tags. If the provided entity-tag is `*`, it is required that the - resource does not exist at all (see [RFC 7232 Section - 3.2](https://tools.ietf.org/html/rfc7232#section-3.2). - - type: string - required: false - example: "7da7a728-f910-11e6-942a-68f728c1ba70", * + parameters|headers: + ETag: + $ref: 'https://opensource.zalando.com/restful-api-guidelines/models/headers-1.0.0.yaml#/ETag' + If-Match: + $ref: 'https://opensource.zalando.com/restful-api-guidelines/models/headers-1.0.0.yaml#/If-Match' + If-None-Match: + $ref: 'https://opensource.zalando.com/restful-api-guidelines/models/headers-1.0.0.yaml#/If-None-Match' +---- + +[source,yaml] +---- +include::../includes/etag.yaml[] +include::../includes/if-match.yaml[lines=3..-1] +include::../includes/if-none-match.yaml[lines=3..-1] ---- -Please see <> for a detailed discussion and options. +Please see <> for a detailed discussion as well as +<> for additional use cases. [#230] @@ -265,29 +264,20 @@ exceeding the local context very hard. The {Idempotency-Key} header must be defined as follows, but you are free to choose your expiration time: +[source,yaml] +---- +include::../includes/idempotency-key.yaml[] +---- + +If you do not want to change the expiration time, you can also use the standard +definition provided by the guideline: + [source,yaml] ---- components: - headers: - - Idempotency-Key: - description: | - The idempotency key is a free identifier created by the client to - identify a request. It is used by the service to identify subsequent - retries of the same request and ensure idempotent behavior by sending - the same response without executing the request a second time. - - Clients should be careful as any subsequent requests with the same key - may return the same response without further check. Therefore, it is - recommended to use a UUID version 4 (random) or any other random - string with enough entropy to avoid collisions. - - Idempotency keys expire after 24 hours. Clients are responsible to stay - within this limit, if they require idempotent behavior. - - type: string - format: uuid - required: false - example: "7da7a728-f910-11e6-942a-68f728c1ba70" + parameters|headers: + Idempotency-Key: + $ref: 'https://opensource.zalando.com/restful-api-guidelines/models/headers-1.0.0.yaml#/Idempotency-Key' ---- *Hint:* The key cache is not intended as request log, and therefore should @@ -306,30 +296,33 @@ Zalando landscape specific proprietary headers. [#183] == {SHOULD} use only the specified proprietary Zalando headers -As a general rule, proprietary HTTP headers should be avoided. -From a conceptual point of view, the business semantics and intent of an -operation should always be expressed via the URLs path and query parameters, -the method, and the content, but not via proprietary headers. -Headers are typically used to implement protocol processing aspects, such as flow control, -content negotiation, and authentication, and represent business agnostic -request modifiers that provide generic context information ({RFC-7231}#section-5[RFC 7231]). - -However, the exceptional usage of proprietary headers is still helpful -when domain-specific generic context information... - -. needs to be passed end to end along the service call chain (even if -not all called services use it as input for steering service behavior -e.g. {X-Sales-Channel} header) and/or... -. is provided by specific gateway components, for instance, our -Fashion Shop API or Merchant API gateway. - -Below, we explicitly define the list of proprietary header exceptions usable for -all services for passing through generic context information of our fashion domain (use case 1). - -Per convention, non standardized, proprietary header names are prefixed with `X-` -and use the dash (`-`) as separator (dash-case). -(Due to backward compatibility, we do not follow the Internet Engineering Task Force’s -recommendation in {RFC-6648}[RFC 6648] to deprecate usage of `X-` headers.) +As a general rule, proprietary HTTP headers should be avoided. In addition +from a conceptual point of view, the business semantics and intent of an +operation should always be expressed via the path and query parameters, the +method, and the content, but not via proprietary headers. + +Headers are typically used to implement protocol processing aspects, such as +flow control, content negotiation, and authentication, and represent business +agnostic request modifiers that provide generic context information +({RFC-9110}#section-10[RFC 9110 Section 10 "Message Context"]). + +However, the exceptional usage of proprietary headers is still helpful when +domain-specific generic context information + +1. needs to be passed _end-to-end_ along the service call chain (even if not + all called services use it as input for steering service behavior, e.g. + {X-Sales-Channel} header), and/or +2. is provided by specific gateway components, for instance, our Fashion Shop + API or Merchant API gateway. + +Below, we explicitly define the list of proprietary headers usable for all +services for passing through generic context information of our fashion domain +(use case 1). + +Per convention, non standardized, proprietary header names are prefixed with +`X-` and use the dash (`-`) as separator (dash-case). (Due to backward +compatibility, we do not follow the recommendation of Internet Engineering +Task Force in {RFC-6648}[RFC 6648] to deprecate the usage of `X-`headers.) Remember that HTTP header field names are not case-sensitive: [cols="15%,10%,60%,15%",options="header",] @@ -350,7 +343,8 @@ a request from a Business Partner hits the Zalando Platform. |[[x-sales-channel]]{X-Sales-Channel}|String| Sales channels are owned by retailers and represent a specific consumer segment being addressed with a specific product assortment that is offered via CFA -retailer catalogs to consumers (see {glossary}[fashion platform glossary (internal_link)]). +retailer catalogs to consumers (see {glossary}[fashion platform glossary +(internal link)]). |52b96501-0f8d-43e7-82aa-8a96fab134d7 |[[x-frontend-type]]{X-Frontend-Type}|String| @@ -380,10 +374,10 @@ https://developer.apple.com/documentation/adsupport/asidentifiermanager[IDFA] https://support.google.com/googleplay/android-developer/answer/6048248[GAID] (Google mobile Advertising Identifier) for Android. It is a unique, customer-resettable identifier provided by mobile device’s operating system -to facilitate personalized advertising, and usually passed by mobile apps via http header -when calling backend services. Called services should be ready to pass this -parameter through when calling other services. It is not sent if the customer -disables it in the settings for respective mobile platform. +to facilitate personalized advertising, and usually passed by mobile apps via +HTTP header when calling backend services. Called services should be ready to +pass this parameter through when calling other services. It is not sent if the +customer disables it in the settings for respective mobile platform. |b89fadce-1f42-46aa-9c83-b7bc49e76e1f |======================================================================= @@ -391,40 +385,28 @@ disables it in the settings for respective mobile platform. *Exception:* The only exception to this guideline are the conventional hop-by-hop `X-RateLimit-` headers which can be used as defined in <<153>>. -As part of the guidelines we sourced the OpenAPI definition of all proprietary headers; -you can simply reference it when defining the API endpoint requests e.g. -[source,yaml] ----- -parameters: -- $ref: "https://opensource.zalando.com/restful-api-guidelines/models/request-headers-1.0.0.yaml#/X-Flow-ID" -- $ref: "https://opensource.zalando.com/restful-api-guidelines/models/request-headers-1.0.0.yaml#/X-Tenant-ID" ----- +As part of the guidelines we provide the default definition of all proprietary +headers, so you can simply reference them when defining the API endpoint. For +details see <>. -Response headers can be referenced in the API endpoint e.g. -[source,yaml] ----- -parameters: -- $ref: "https://opensource.zalando.com/restful-api-guidelines/models/response-headers-1.0.0.yaml#/ETag" -- $ref: "https://opensource.zalando.com/restful-api-guidelines/models/response-headers-1.0.0.yaml#/Cache-Control" ----- +*Hint:* This guideline does not standardize proprietary headers for our +specific gateway components (2. use case above). This include, for instance, +non pass-through headers `X-Zalando-Customer`, `X-Zalando-Client-ID`, +`X-Zalando-Request-Host`, `X-Zalando-Request-URI` defined by Fashion Shop API +(RKeep), or `X-Consumer`, `X-Consumer-Signature`, `X-Consumer-Key-ID` defined +by Merchant API gateway. All these proprietary headers are allowlisted in the +API Linter (Zally) checking this rule. -*Hint:* This guideline does not standardize proprietary headers for -our specific gateway components (2. use case above). -This include, for instance, non pass-through headers `X-Zalando-Customer`, `X-Zalando-Client-ID`, `X-Zalando-Request-Host`, -`X-Zalando-Request-URI` defined by Fashion Shop API (RKeep), or `X-Consumer`, `X-Consumer-Signature`, -`X-Consumer-Key-ID` defined by Merchant API gateway. -All these proprietary headers are allowlisted in the API Linter (Zally) checking this rule. [#184] == {MUST} propagate proprietary headers All Zalando's proprietary headers defined in <<183>> are end-to-end headers -footnote:header-types[HTTP/1.1 standard ({RFC-7230}#section-6.1[RFC 7230]) -defines two types of headers: end-to-end and hop-by-hop headers. End-to-end -headers must be transmitted to the ultimate recipient of a request or response. -Hop-by-hop headers, on the contrary, are meaningful for a single connection -only.] -and must be propagated to the services down the call +footnote:header-types[HTTP/1.1 standard ({RFC-9110}#section-7.6.1[RFC 9110 +Section 7.6.1]) defines two types of headers: _end-to-end_ and _hop-by-hop_ +headers. End-to-end headers must be transmitted to the ultimate recipient of a +request or response. Hop-by-hop headers, on the contrary, are meaningful for a +single connection only.] and must be propagated to the services down the call chain. The header names and values must remain unchanged. For example, the values of the custom headers like {X-Device-Type} can affect @@ -464,19 +446,22 @@ The following formats are allowed: * `UUID` ({RFC-4122}[RFC-4122]) * `base64` ({RFC-4648}[RFC-4648]) * `base64url` ({RFC-4648}#section-5[RFC-4648 Section 5]) -* Random unique string restricted to the character set `[a-zA-Z0-9/+_-=]` maximal of 128 characters. +* Random unique string restricted to the character set `[a-zA-Z0-9/+_-=]` + maximal of 128 characters. *Note:* If a legacy subsystem can only process `Flow-IDs` with a specific format or length, it must define this restriction in its API specification, and be generous and remove invalid characters or cut the length to the supported limit. -*Hint:* In case distributed tracing is supported by -{SRE-open-tracing}[OpenTracing (internal_link)] -you should ensure that created _spans_ are tagged using `flow_id` — see -{SRE-open-tracing}/blob/master/wg-semantic-conventions/best-practices/flowid.md[How to Connect Log Output with OpenTracing Using Flow-IDs (internal_link)] -or -{SRE-open-tracing}/blob/master/wg-semantic-conventions/best-practices.md[Best practises (internal_link)]. +*Hint:* In case distributed tracing is supported by +{SRE-open-tracing}[OpenTracing (internal link)] you should ensure that created +_spans_ are tagged using `flow_id` — see +{SRE-open-tracing}/blob/master/wg-semantic-conventions/best-practices/flowid.md[How +to Connect Log Output with OpenTracing Using Flow-IDs (internal link)] or +{SRE-open-tracing}/blob/master/wg-semantic-conventions/best-practices.md[Best +practices (internal link)]. + === Service Guidance @@ -484,7 +469,7 @@ or ** RESTful API endpoints *must* support {X-Flow-ID} header in requests ** Event listeners *must* support the metadata `flow-id` from events. -+ + *Note:* API-Clients *must* provide {Flow-ID} when calling a service or producing events. If no {Flow-ID} is provided in a request or event, the service must create a new {Flow-ID}. diff --git a/chapters/http-requests.adoc b/chapters/http-requests.adoc index e5351b379..fba2f78e5 100644 --- a/chapters/http-requests.adoc +++ b/chapters/http-requests.adoc @@ -5,9 +5,8 @@ [#148] == {MUST} use HTTP methods correctly -Be compliant with the standardized HTTP method semantics -(see HTTP/1 {RFC-7230}[RFC-7230] and {RFC-7231}[RFC-7230] updates from 2014) -summarized as follows: +Be compliant with the standardized HTTP semantics (see {RFC-9110}[RFC-9110 +"HTTP semantics"]) as summarized here. [[get]] @@ -247,12 +246,13 @@ details). === DELETE with body payload In rare cases {DELETE} may require additional information, that cannot be -classified as filter parameters and thus should be transported via request body payload, to -perform the operation. Since {RFC-7231}#section-4.3.5[RFC-7231] states, that -{DELETE} has an undefined semantic for payloads, we recommend to utilize {POST}. -In this case the POST endpoint must be documented with the hint {DELETE-with-Body} -analog to how it is defined for {GET-with-Body}. The response status code of -{DELETE-with-Body} requests should be similar to usual {DELETE} requests. +classified as filter parameters and thus should be transported via request body +payload, to perform the operation. Since {RFC-9110}#section-9.3.5[RFC-9110 +Section 9.3.5] states, that {DELETE} has an undefined semantic for payloads, we +recommend to utilize {POST}. In this case the POST endpoint must be documented +with the hint {DELETE-with-Body} analog to how it is defined for +{GET-with-Body}. The response status code of {DELETE-with-Body} requests should +be similar to usual {DELETE} requests. [[head]] @@ -305,7 +305,7 @@ fetching, etc. However, these actual effects and state changes, must not be intended by the operation so that it can be held accountable. Method implementations must fulfill the following basic properties according -to {RFC-7231}[RFC 7231]: +to {RFC-9110}#section-9.2[RFC 9110 Section 9.2]: [cols="15%,15%,35%,35%",options="header",] |==================================================== @@ -418,7 +418,7 @@ multiple times with different values as follows: |========================================================================= | Parameter Type | Comma-separated Values | Multiple Parameters | Standard | Header | `Header: value1,value2` | `Header: value1, Header: value2` -| {RFC-7230}#section-3.2.2[RFC 7230 Section 3.2.2] +| {RFC-9110}#section-5.3[RFC 9110 Section 5.3] | Query | `?param=value1,value2` | `?param=value1¶m=value2` | {RFC-6570}#section-3.2.8[RFC 6570 Section 3.2.8] @@ -431,7 +431,7 @@ explicitly define the collection format to guide consumers as follows: |=============================================================== | Parameter Type | Comma-separated Values | Multiple Parameters | Header | `style: simple, explode: false` | not allowed (see - {RFC-7230}#section-3.2.2[RFC 7230 Section 3.2.2]) + {RFC-9110}#section-5.3[RFC 9110 Section 5.3]) | Query | `style: form, explode: false` | `style: form, explode: true` |=============================================================== diff --git a/chapters/performance.adoc b/chapters/performance.adoc index 0486a5637..e69539538 100644 --- a/chapters/performance.adoc +++ b/chapters/performance.adoc @@ -31,16 +31,46 @@ Each of these items is described in greater detail below. [#156] == {SHOULD} use `gzip` compression -Compress the payload of your API’s responses with gzip, unless there’s a good -reason not to — for example, you are serving so many requests that the time to -compress becomes a bottleneck. This helps to transport data faster over the -network (fewer bytes) and makes frontends respond faster. +A servers and clients should support `gzip` content encoding to reduce the data +transported over the network and thereby speed up response times, unless there +is a good reason against this. Good reasons against compression are: + +1. The content is already compressed, or +2. The server has not enough resources to support compression. + +While `gzip` content encoding should be the default, servers must also support +unencoded content for testing. This is ensured by content negotiation using the +{Accept-Encoding} request header (see {RFC-9110}#section-12.5.3[RFC 9110 +Section 12.5.3]). Successful compression is signaled via the {Content-Encoding} +response header (see {RFC-9110}#section-8.4[RFC 9110 Section 8.4]). Clients and +servers may support other compression algorithms as `compress`, `deflate`, or +`br`. + +To signal server support for compression the API specification should define +both, the {Accept-Encoding} request header and the {Content-Encoding} response +header. This can be done by simply referencing the standard header definitions +as follows (see also the default definition below): -Though gzip compression might be the default choice for server payload, the -server should also support payload without compression and its client control -via {Accept-Encoding} request header -- see also {RFC-7231}#section-5.3.4[RFC -7231 Section 5.3.4]. The server should indicate used gzip compression via the -{Content-Encoding} header. +[source,yaml] +---- +components: + parameters|headers: + Accept-Encoding: + $ref: 'https://opensource.zalando.com/restful-api-guidelines/models/headers-1.0.0.yaml#/Accept-Encoding' + Content-Encoding: + $ref: 'https://opensource.zalando.com/restful-api-guidelines/models/headers-1.0.0.yaml#/Content-Encoding' +---- + +[source,yaml] +---- +include::../includes/accept-encoding.yaml[] +include::../includes/content-encoding.yaml[line=3..-1] +---- + +*Note:* since most server and client frameworks still **do not** support +`gzip`-compression *out-of-the-box* you need to manually activate it, e.g. +https://www.callicoder.com/configuring-spring-boot-application/#enabling-gzip-compression-in-spring-boot[Spring Boot], +or add a dedicated middleware, e.g. https://github.com/gin-contrib/gzip#usage[gin-gionic/gin]. [#157] @@ -128,7 +158,7 @@ https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form[BNF] grammar. ::= "!" ---- -**Note:** Following the +*Note:* Following the https://en.wikipedia.org/wiki/Principle_of_least_astonishment[principle of least astonishment], you should not define the {fields} query parameter using a default value, as the result is counter-intuitive and very likely not @@ -182,23 +212,24 @@ GET /order/123?embed=(items) HTTP/1.1 Caching has to take many aspects into account, e.g. general <> of response information, our guideline to protect endpoints using SSL and <<104, OAuth authorization>>, resource update and invalidation -rules, existence of multiple consumer instances. As a consequence, caching is -in best case complex, e.g. with respect to consistency, in worst case -inefficient. +rules, existence of multiple consumer instances. Caching is in best case +complex, e.g. with respect to consistency, in worst case inefficient. As a consequence, client side as well as transparent web caching should be avoided, unless the service supports and requires it to protect itself, e.g. in case of a heavily used and therefore rate limited master data service, i.e. data items that rarely or not at all change after creation. -As default, API providers and consumers should always set the {Cache-Control} -header set to {Cache-Control-no-store} and assume the same setting, if no -{Cache-Control} header is provided. +As default, servers and clients should always set the {Cache-Control} header +to {Cache-Control-no-store} and assume the same setting, if no {Cache-Control} +header is provided. -**Note:** There is no need to document this default setting. However, please -make sure that your framework is attaching this header value by default, or -ensure this manually, e.g. using the best practice of Spring Security as shown -below. Any setup deviating from this default must be sufficiently documented. +*Note:* There is no need to document this default setting. However, please make +sure that your framework is attaching these header values by default, or ensure +this manually, e.g. using the best practice of +https://www.baeldung.com/spring-security-cache-control-headers[Spring Security] +as shown below. Any setup deviating from this default must be sufficiently +documented. [source,http] ---- @@ -217,63 +248,49 @@ following rules: caching boundaries, i.e. time-to-live and cache constraints, by providing sensible values for {Cache-Control} and {Vary} in your service. We will explain best practices below. -* [[cache-support-patterns]]Provide efficient methods to warm up and update - caches, e.g. as follows: -** In general, you should support <<182, `ETag` Together With `If-Match`/ - `If-None-Match` Header>> on all <> endpoints. -** For larger data items support {HEAD} requests or more efficient {GET} - requests with {If-None-Match} header to check for updates. -** For small data sets provide full collection {GET} requests supporting - {ETag}, as well as {HEAD} requests or {GET} requests with {If-None-Match} - to check for updates. -** For medium sized data sets provide full collection {GET} requests supporting - {ETag} together with <> and {entity-tag} filtering {GET} requests - for limiting the response to changes since the provided {entity-tag}. *Note:* - this is not supported by generic client and proxy caches on HTTP layer. +* Provide efficient methods to warm up and update caches (see ). -*Hint:* For proper cache support, you must return {304} without content on a -failed {HEAD} or {GET} request with <<182, `If-None-Match: `>> instead -of {412}. +Usually, you can reuse the standard {Cache-Control}, {Vary}, and {ETag} +response header definitions provided by the guideline as follows: [source,yaml] ---- components: - headers: - - Cache-Control: - description: | - The RFC 7234 Cache-Control header field is providing directives to - control how proxies and clients are allowed to cache responses results - for performance. Clients and proxies are free to not support caching of - results, however if they do, they must obey all directives mentioned in - [RFC-7234 Section 5.2.2](https://tools.ietf.org/html/rfc7234) to the - word. - - In case of caching, the directive provides the scope of the cache - entry, i.e. only for the original user (private) or shared between all - users (public), the lifetime of the cache entry in seconds (max-age), - and the strategy how to handle a stale cache entry (must-revalidate). - Please note, that the lifetime and validation directives for shared - caches are different (s-maxage, proxy-revalidate). - - type: string - required: false - example: "private, must-revalidate, max-age=300" - - - Vary: - description: | - The RFC 7231 Vary header field in a response defines which parts of - a request message, aside the target URL and HTTP method, might have - influenced the response. A client or proxy cache must respect this - information, to ensure that it delivers the correct cache entry (see - [RFC-7231 Section - 7.1.4](https://tools.ietf.org/html/rfc7231#section-7.1.4)). - - type: string - required: false - example: "accept-encoding, accept-language" + parameters|headers: + Accept-Encoding: + $ref: 'https://opensource.zalando.com/restful-api-guidelines/models/headers-1.0.0.yaml#/Cache-Control' + Content-Encoding: + $ref: 'https://opensource.zalando.com/restful-api-guidelines/models/headers-1.0.0.yaml#/Vary' + ETag: + $ref: 'https://opensource.zalando.com/restful-api-guidelines/models/headers-1.0.0.yaml#/ETag' ---- -*Hint:* For {ETag} source see <<182>>. +See also <<182>>. + +[[cache-support-patterns]] +=== Cache Support Patterns + +To make best use of caching in micro service environments you need to provide +efficient methods to warm up and update caches, e.g. as follows: + +* In general, you should support <<182, `ETag` Together With `If-Match`/ + `If-None-Match` Header>> on all <> endpoints. +* For larger data items you should support {HEAD} requests or more a bit more + efficient {GET} requests with {If-None-Match} header to check for updates. +* For small data sets you should provide a *get full collection* {GET} endpoint + that supports an {ETag} for the collection in combination with a {HEAD} or + {GET} requests with {If-None-Match} to check for updates. +* For medium sized data sets provide a *get full collection* {GET} endpoint + that supports an {ETag} for the collection in combination with <> + and {entity-tag} filtering {GET} requests for limiting the response to + changes since the provided {entity-tag}. *Note:* this is not supported by + generic client and proxy caches on HTTP layer. + +*Hint:* For proper cache support, you must return {304} without content on a +failed {HEAD} or {GET} request with <<182, `If-None-Match: `>> +instead of {412}. For {ETag} see also <<182>>. + +==== Default Header Values The default setting for {Cache-Control} should contain the `private` directive for endpoints with standard <<104, OAuth authorization>>, as well as the @@ -287,11 +304,10 @@ rate of your master data and your requirements to keep clients consistent. Cache-Control: private, must-revalidate, max-age=300 ---- -The default setting for {Vary} is harder to determine correctly. It highly -depends on the API endpoint, e.g. whether it supports compression, accepts -different media types, or requires other request specific headers. To support -correct caching you have to carefully choose the value. However, a good first -default may be: +The default setting for {Vary} is harder to determine correctly. It depends on +the API endpoint, e.g. whether it supports compression, accepts different media +types, or requires other request specific headers. To support correct caching +you have to carefully choose the value. However, a good first default may be: [source,http] ---- @@ -301,13 +317,15 @@ Vary: accept, accept-encoding Anyhow, this is only relevant, if you encourage clients to install generic HTTP layer client and proxy caches. -*Note:* generic client and proxy caching on HTTP level is hard to configure. -Therefore, we strongly recommend to attach the (possibly distributed) cache -directly to the service (or gateway) layer of your application. This relieves -from interpreting the {vary} header and greatly simplifies interpreting the -{Cache-Control} and {ETag} headers. Moreover, is highly efficient with respect -to caching performance and overhead, and allows to support more +==== Caching strategy + +Generic client and proxy caching on HTTP level is hard to configure. Therefore, +we strongly recommend to attach the (possibly distributed) cache directly to +the service (or gateway) layer of your application. This relieves the service +from interpreting the {vary} header and greatly simplifies the usage patterns +of the {Cache-Control} and {ETag} headers. Moreover, is highly efficient with +respect to cache performance and overhead, and allows to support more <>. -Anyhow, please carefully read {RFC-7234}[RFC 7234] before adding any client or +Anyhow, please carefully read {RFC-9111}[RFC 9111] before adding any client or proxy cache. diff --git a/chapters/references.adoc b/chapters/references.adoc index 8edb2aeb3..dc3ca3564 100644 --- a/chapters/references.adoc +++ b/chapters/references.adoc @@ -18,19 +18,16 @@ This section collects links to documents to which we refer, and base our guideli * *{RFC-3339}[RFC 3339]:* Date and Time on the Internet: Timestamps * *{RFC-4122}[RFC 4122]:* A Universally Unique IDentifier (UUID) URN Namespace * *{RFC-4627}[RFC 4627]:* The application/json Media Type for JavaScript Object Notation (JSON) -* *{RFC-8288}[RFC 8288]:* Web Linking +* *{RFC-4648}[RFC 4648]:* The Base16, Base32, and Base64 Data Encodings * *{RFC-6585}[RFC 6585]:* Additional HTTP Status Codes * *{RFC-6902}[RFC 6902]:* JavaScript Object Notation (JSON) Patch * *{RFC-7159}[RFC 7159]:* The JavaScript Object Notation (JSON) Data Interchange Format -* *{RFC-7230}[RFC 7230]:* Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing -* *{RFC-7231}[RFC 7231]:* Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content -* *{RFC-7232}[RFC 7232]:* Hypertext Transfer Protocol (HTTP/1.1): Conditional Requests -* *{RFC-7233}[RFC 7233]:* Hypertext Transfer Protocol (HTTP/1.1): Range Requests -* *{RFC-7234}[RFC 7234]:* Hypertext Transfer Protocol (HTTP/1.1): Caching * *{RFC-7240}[RFC 7240]:* Prefer Header for HTTP * *{RFC-7396}[RFC 7396]:* JSON Merge Patch * *{RFC-7807}[RFC 7807]:* Problem Details for HTTP APIs -* *{RFC-4648}[RFC 4648]:* The Base16, Base32, and Base64 Data Encodings +* *{RFC-8288}[RFC 8288]:* Web Linking +* *{RFC-9110}[RFC 9110]:* HTTP Semantics +* *{RFC-9111}[RFC 9111]:* HTTP Caching * *{ISO-8601}[ISO 8601]:* Date and time format * *{ISO-3166-1-alpha-2}[ISO 3166-1 alpha-2]:* Two letter country codes diff --git a/index.adoc b/index.adoc index 00c078210..3f7f89ecb 100644 --- a/index.adoc +++ b/index.adoc @@ -40,18 +40,20 @@ :RFC-6901: https://tools.ietf.org/html/rfc6901 :RFC-6902: https://tools.ietf.org/html/rfc6902 :RFC-7159: https://tools.ietf.org/html/rfc7159 -:RFC-7230: https://tools.ietf.org/html/rfc7230 -:RFC-7231: https://tools.ietf.org/html/rfc7231 -:RFC-7232: https://tools.ietf.org/html/rfc7232 -:RFC-7233: https://tools.ietf.org/html/rfc7233 +:RFC-7230: https://tools.ietf.org/html/rfc7230 // obsoleted by rfc-9110 +:RFC-7231: https://tools.ietf.org/html/rfc7231 // obsoleted by rfc-9110 +:RFC-7232: https://tools.ietf.org/html/rfc7232 // obsoleted by rfc-9110 +:RFC-7233: https://tools.ietf.org/html/rfc7233 // obsoleted by rfc-9110 :RFC-7234: https://tools.ietf.org/html/rfc7234 -:RFC-7235: https://tools.ietf.org/html/rfc7235 +:RFC-7235: https://tools.ietf.org/html/rfc7235 // obsoleted by rfc-9110 :RFC-7240: https://tools.ietf.org/html/rfc7240 :RFC-7396: https://tools.ietf.org/html/rfc7396 :RFC-7493: https://tools.ietf.org/html/rfc7493 :RFC-7807: https://tools.ietf.org/html/rfc7807 :RFC-8288: https://tools.ietf.org/html/rfc8288 :RFC-8594: https://tools.ietf.org/html/rfc8594 +:RFC-9110: https://tools.ietf.org/html/rfc9110 +:RFC-9111: https://tools.ietf.org/html/rfc9111 :iana-link-relations: http://www.iana.org/assignments/link-relations :iana-media-types: https://www.iana.org/assignments/media-types/media-types.xhtml @@ -186,48 +188,50 @@ // Attributes to improve design and linking of common headers. -:ETag: pass:[ETag] -:entity-tag: pass:[<entity-tag>] -:If-Match: pass:[If-Match] -:If-None-Match: pass:[If-None-Match] - -:Last-Modified: pass:[Last-Modified] -:If-Modified-Since: pass:[If-Modified-Since] -:If-Unmodified-Since: pass:[If-Unmodified-Since] -:If-Range: pass:[If-Range] - - -:Accept: pass:[Accept] -:Accept-Charset: pass:[Accept-Charset] -:Accept-Encoding: pass:[Accept-Encoding] -:Accept-Language: pass:[Accept-Language] - -:Content-Type: pass:[Content-Type] -:Content-Range: pass:[Content-Range] -:Content-Encoding: pass:[Content-Encoding] -:Content-Language: pass:[Content-Language] -:Content-Length: pass:[Content-Length] +:entity-tag: pass:[<entity-tag>] +:ETag: pass:[ETag] +:If-Match: pass:[If-Match] +:If-None-Match: pass:[If-None-Match] + +:Last-Modified: pass:[Last-Modified] +:If-Modified-Since: pass:[If-Modified-Since] +:If-Unmodified-Since: pass:[If-Unmodified-Since] +:If-Range: pass:[If-Range] + +:Accept: pass:[Accept] +:Accept-Charset: pass:[Accept-Charset] +:Accept-Encoding: pass:[Accept-Encoding] +:Accept-Language: pass:[Accept-Language] + +:Content-Type: pass:[Content-Type] +:Content-Encoding: pass:[Content-Encoding] +:Content-Language: pass:[Content-Language] +:Content-Length: pass:[Content-Length] +:Content-Location: pass:[Content-Location] +:Content-Range: pass:[Content-Range] :Content-Disposition: pass:[Content-Disposition] -:Content-Location: pass:[Content-Location] -:Location: pass:[Location] + +:Location: pass:[Location] +:Retry-After: pass:[Retry-After] +:Vary: pass:[Vary] + +:Cache-Control: pass:[Cache-Control] +:Cache-Control-no-store: pass:[Cache-Control: no-store] +:Expires: pass:[Expires] :Prefer: pass:[Prefer] :Preference-Applied: pass:[Preference-Applied] :Sunset: pass:[Sunset] :Deprecation: pass:[Deprecation] +:Warning: pass:[Warning] -:Retry-After: pass:[Retry-After] -:Vary: pass:[Vary] -:Cache-Control: pass:[Cache-Control] -:Cache-Control-no-store: pass:[Cache-Control: no-store] -:Expires: pass:[Expires] :Flow-ID: pass:[Flow-ID] // Attributes to improve design and linking of RFC key words. -:RFC-safe: pass:[safe] -:RFC-idempotent: pass:[idempotent] -:RFC-cacheable: pass:[cacheable] +:RFC-safe: pass:[safe] +:RFC-idempotent: pass:[idempotent] +:RFC-cacheable: pass:[cacheable] = Zalando RESTful API and Event Guidelines diff --git a/models/headers-1.0.0.yaml b/models/headers-1.0.0.yaml new file mode 100644 index 000000000..c1c57847d --- /dev/null +++ b/models/headers-1.0.0.yaml @@ -0,0 +1,475 @@ +# Standard request headers + +Accept-Encoding: + name: Accept-Encoding + in: header + required: false + description: |- + The **Accept-Encoding** indicates the content encoding (usually a + compression algorithm) the client understands. The server selects one of + the proposed encodings and returns his choice in the **Content-Encoding** + response header. + + Supported compression algorithms of the server are [**gzip**][gzip] and + **identity**, but other encodings from list below may be support too: + + * **gzip** - a compression format that uses [Lempel-Ziv coding][gzip] + (LZ77) with a 32-bit CRC. + * **compress** - a format using the [Lempel-Ziv-Welch][lzw] (LZW) + algorithm. + * **deflate** - A compression format that uses the [zlib][zlib] structure + with the [deflate][deflate] compression algorithm. + * **br** - a compression format that uses the [Brotli][brotli] algorithm. + * **identity** - the identity function without modification or compression + used to indicate unchanged responses. This value is always considered as + acceptable, even if omitted. + + The server may choose not to compress the body of the response, if the + identity value is acceptable, e.g. if the content is already compressed + (e.g. for JPEG content), or if the server is missing resources to perform + the compression operation. + + **Note:** A client is allowed to explicitly forbid the identity value by + setting **identity;q=0** or **\*;q=0**, however, the server in return may + respond with a **406** (Not Acceptable) error. + + See [RFC 9110 Section 12.5.3][rfc-9110-12.5.3] as well as [API Guideline + Rule #156][api-156] for further details. + + [rfc-9110-12.5.3]: + [api-156]: + [gzip]: + [lzw]: + [zlib]: + [deflate]: + [brotli]: + schema: + type: array + items: + type: string + style: simple + explode: false + example: "gzip;q=0.8", "identity;q=0" + + +If-Match: + name: If-Match + in: header + required: false + description: |- + The **If-Match** header field is used to declare a list of identifiers that + are required to match the current resource version identifier in at least + one position as a pre-condition for executing the request on the server + side. This behavior is used to validate and reject optimistic updates, by + checking if the resource version a consumer has based his changes on is + outdated on arrival of the change request to prevent lost updates. + + If the pre-condition fails the server will respond with status code **412** + (Precondition Failed). See [RFC 9110 Section 13.1.1][rfc-9110-13.1.1] as + well as [API Guideline Rule #182][api-182] for further details. + + [rfc-9110-13.1.1]: + [api-182]: + schema: + type: array + items: + type: string + style: simple + explode: false + example: W/"xy", "5", "5db68c06-1a68-11e9-8341-68f728c1ba70" + + +If-None-Match: + name: If-None-Match + in: header + required: false + description: |- + The **If-None-Match header** field is used to declare a list of identifiers + that are required to fail matching all the current resource version + identifiers as a pre-condition for executing the request on the server + side. This is especially used in conjunction with an **\*** (asterix) that + is matching all possible resource identifiers to ensure the initial + creation of a resource. Other use cases are possible but rare. + + If the pre-condition fails the server will respond with status code **412** + (Precondition Failed). See [RFC 9110 Section 13.1.2][rfc-9110-13.1.2] as + well as [API Guideline Rule #182][api-182] for further details. + + [rfc-9110-13.1.2]: + [api-182]: + schema: + type: array + items: + type: string + style: simple + explode: false + example: W/"xy", "5", "5db68c06-1a68-11e9-8341-68f728c1ba70" + + +Prefer: + name: Prefer + in: header + required: false + description: |- + The **Prefer** header indicates that a particular server behavior is + preferred by the client, but is not required for successful completion of + the request (see [RFC 7240][rfc-7240]. The following behaviors are + supported by this API: + + * **respond-async** is used to suggest the server to respond as fast as + possible asynchronously using **202** (Accepted) instead of waiting for + the result. + * **return=** is used to suggest the server to + return using **204** (No Content) without resource (minimal) or using + **200** or **201** with resource (representation) in the response body on + success. + * **wait=** is used to suggest a maximum time the server has + time to process the request synchronously. + * **handling=** is used to suggest the server to be strict + and report error conditions or lenient, i.e. robust and try to continue, + if possible. + + See [RFC 7340][rfc-7240] as well as [API Guideline Rule #181][api-181] + for further details. + + [rfc-7240]: + [api-181]: + schema: + type: array + items: + type: string + style: simple + explode: false + example: "respond-async", "return=minimal", "wait=20", "handling=strict" + + +Idempotency-Key: + name: Idempotency-Key + in: header + required: false + description: |- + The **Idempotency-Key** is a free identifier created by the client to + identify a request. It is used by the service to identify repeated request + to ensure idempotent behavior by sending the same (or a similar) response + without executing the request a second time. + + Clients should be careful as any subsequent requests with the same key may + return the same response without further check. Thus, it is recommended to + use a UUID version 4 (random) or any other random string with enough + entropy to avoid collisions. + + Keys expire after 24 hours. Clients are responsible to stay within this + limit, if they require idempotent behavior. + + See [API Guideline Rule #181][api-230] for further details. + + [api-230]: + schema: + type: string + format: uuid + example: "7da7a728-f910-11e6-942a-68f728c1ba70" + + +# Standard response headers + + +Content-Encoding: + name: Content-Encoding + in: header + required: false + description: |- + The **Content-Encoding** header lists any encoding (usually a compression + algorithm) applied to the message payload - in execution order. This allows + the recipient to decode the content in order to obtain the original payload + format. + + Supported compression algorithms of the server are [**gzip**][gzip] and + **identity**, but other encoding from list below may be support too: + + * **gzip** - a compression format that uses [Lempel-Ziv coding][gzip] + (LZ77) with a 32-bit CRC. + * **compress** - a format using the [Lempel-Ziv-Welch][lzw] (LZW) + algorithm. + * **deflate** - A compression format that uses the [zlib][zlib] structure + with the [deflate][deflate] compression algorithm. + * **br** - a compression format that uses the [Brotli][brotli] algorithm. + * **identity** - the identity function without modification or compression + used to indicate unchanged responses. This value is always considered as + acceptable, even if omitted. + + **Note:** the original media/content type is specified in the + **Content-Type**-header, while the **Content-Encoding** applies to the + representation of the data. If the original media is encoded in some way, + e.g. as a zip file, then this information is not be included in the + **Content-Encoding** header. + + See [RFC 9110 Section 8.4][rfc-9110-8.4] as well as [API Guideline Rule + #156][api-156] for further details. + + [rfc-9110-8.4]: + [api-156]: + [gzip]: + [lzw]: + [zlib]: + [deflate]: + [brotli]: + schema: + type: array + items: + type: string + style: simple + explode: false + example: "gzip", "deflate" + + +ETag: + name: ETag + in: header + required: false + description: |- + The **ETag** header field in a response provides an opaque quoted string + identifying the distinct delivered resource. The same selected resource + depending on version and representation may be identified by multiple + identifiers. The **ETag** value is guaranteed to change whenever the + resource changes, and thereby enabling optimistic updates. + + An identifier consists of an opaque quoted string, possibly prefixed by + a weakness indicator. See [RFC 9110 Section 8.8.3][rfc-9110-8.8.3] as + well as [API Guideline Rule #182][api-182] for further details. + + [rfc-9110-8.8.3]: + [api-182]: + schema: + type: array + items: + type: string + style: simple + explode: false + example: W/"xy", "5", "5db68c06-1a68-11e9-8341-68f728c1ba70" + + +Cache-Control: + name: Cache-Control + in: header + required: false + description: |- + The **Cache-Control** header field is providing directives to control how + proxies and clients are allowed to cache responses results for performance. + Clients and proxies are free to not support caching of results, however if + they do, they must obey all directives mentioned in [RFC-9111 Section + 5.2][rfc-9111-5.2] to the word. + + In case of caching, the directive provides the scope of the cache entry, + i.e. only for the original user (private) or shared between all users + (public), the lifetime of the cache entry in seconds (max-age), and the + strategy how to handle a stale cache entry (must-revalidate). Please note, + that the lifetime and validation directives for shared caches are different + (s-maxage, proxy-revalidate). + + See [RFC 9111 Section 5.2][rfc-9111-5.2] as well as [API Guideline Rule + #227][api-227] for further details. + + [rfc-9111-5.2]: + [api-227]: + schema: + type: array + items: + type: string + style: simple + explode: false + example: "private, must-revalidate, max-age=3600" + + +Vary: + name: Vary + in: header + required: false + description: |- + The **Vary** header field in the response describes which parts of the + request message, aside from the method, the **Host** header field, and the + request target path, might have influence the server in selecting the + presented response. A client or proxy that caches the response must respect + this information to ensure that it delivers the correct cache entry (see + [RFC-9110 Section 12.5.5][rfc-9110-12.5.5]). + + The value consists of either a single **\*** (asterisk ) or a list of + case-insensitive header field names. + + See [RFC 9110 Section 12.5.5][rfc-9110-12.5.5] as well as [API Guideline + Rule #227][api-227] for further details. + + [rfc-9110-12.5.5]: + [api-227]: + schema: + type: array + items: + type: string + style: simple + explode: false + example: "accept-encoding, accept-language" + + +Deprecation: + name: Deprecation + in: header + required: false + description: |- + The **Deprecation** response header (see [Draft "The Deprecation HTTP + Header Field"][draft]) announces an upcoming deprecation of a feature or + resource. The deprecation value is either a timestamp as defined in [RFC + 9110 Section 5.6.7][rfc-9110-5.6.7] or `true` - if the feature is already + deprecated. + + The scope of the distinct deprecated feature or resource must be derived + from the API spec. If the timestamp points to the past, the API spec also + contains a migration advise. + + Clients should monitor the usage of **Deprecation** headers and notify + about them in time, so that migration measures can be planned and executed + timely (see also [API Guideline - Deprecation][api]). + + [draft]: + [rfc-9110-5.6.7]: + [api]: + schema: + type: string + format: date-time + example: "Tue, 31 Dec 2024 23:59:59 GMT" + + +Sunset: + name: Sunset + in: header + required: false + description: |- + The **Sunset** response header (see [RFC 8594][rfc-8594]) communicates the + point in time at which the feature or resource becomes unresponsive. The + sunset value is providing a timestamp as defined in [RFC 9110 Section + 5.6.7][rfc-9110-5.6.7] usually pointing to the future. If a sunset value + points to the past, the feature or resource must be expected to become + unavailable at any time. + + Clients should monitor the usage of **Sunset** headers and warn/alert about + them before the sunset time has come to take counter measures, e.g. prepare + a client shutdown or migration (see also [API Guideline - + Deprecation][api-deprecation]). + + [rfc-8594]: + [rfc-9110-5.6.7]: + [api-deprecation]: + schema: + type: string + format: date-time + example: "Wed, 31 Dec 2025 23:59:59 GMT" + + +# Custom common headers + + +X-Flow-ID: + name: X-Flow-ID + in: header + required: false + description: |- + The **X-Flow-ID** is a custom header containing a unique flow identifier that + was be passed to any further request. It can be used to investigate request + related log entries end events. + + See [API Guideline Rule #233][api-233] for further details. + + [api-233]: + schema: + type: string + example: GKY7oDhpSiKY_gAAAABZ_A + + +X-Mobile-Advertising-ID: + name: X-Mobile-Advertising-ID + in: header + required: false + description: |- + A unique, customer-resettable identifier provided by the operating system of + a mobile device to facilitate personalized advertising, and usually passed + by mobile apps via header when calling backend services. + + It is either the [IDFA](https://developer.apple.com/documentation/adsupport/asidentifiermanager) + (Apple Identifier for mobile Advertising) for iOS, or the + [GAID](https://support.google.com/googleplay/android-developer/answer/6048248) + (Google mobile Advertising Identifier) for Android. + schema: + type: string + example: cdda802e-fb9c-47ad-0794d394c912 + + +X-Tenant-ID: + name: X-Tenant-ID + in: header + required: false + description: |- + The unique identifier of the tenant that initiated the request to the + multi-tenant Zalando Platform. The X-Tenant-ID is set to the ID of the + business partner extracted from the `OAuth`-token of the request. The + **X-Tenant-ID** should be passed-through as generic aspect. + schema: + type: string + example: 9f8b3ca3-4be5-436c-a847-9cd55460c495 + + +X-Sales-Channel: + name: X-Sales-Channel + in: header + required: false + description: |- + Unique identifier of the sales channel related to the request. Sales + channels are owned by retailers and represent a specific consumer segment + being addressed with a specific product assortment that is offered via the + CFA retailer catalogs to consumers (see fashion platform glossary). The + **X-Sales-Channel** should be passed-through as generic aspect. + schema: + type: string + example: 52b96501-0f8d-43e7-82aa-8a96fab134d7 + + +X-Frontend-Type: + name: X-Frontend-Type + in: header + required: false + description: |- + The type of the consumer facing application (CFA) used to initiate the + request. CFAs provide business experience to their customers via different + frontend application types, `mobile-app`, `browser`, `facebook-app`, + `chat-app`, and `email`. The **X-Frontend-Type** should be passed-through + as generic aspect - there are diverse concerns, e.g. pushing mobiles with + specific coupons, that make use of it. + schema: + type: string + example: mobile-app + + +X-Device-Type: + name: X-Device-Type + in: header + required: false + description: |- + The device type used to initiate the request. There are also use cases + for steering customer experience (incl. features and content) depending on + device types, e.g. `smartphone`, `tablet`, `desktop`, and `other`. The + **X-Device-Type** should be passed-through as generic aspect. + schema: + type: string + example: tablet + + +X-Device-OS: + name: X-Device-OS + in: header + required: false + description: |- + The operating system used by the device initiating the request. On top of + device type, we want to differ between device platforms, e.g. between + computers, smartphone and tablets with `iOS`, `Android`, `Windows`, + `Linux`, and `MacOS`. The `X-Device-OS` should be passed-through as generic + aspect. + schema: + type: string + example: Android diff --git a/scripts/build-css.sh b/scripts/build-css.sh index 2f4eb0e58..00c00d285 100755 --- a/scripts/build-css.sh +++ b/scripts/build-css.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#! /bin/bash # Script to generate CSS for the Guidelines set -ex #print commands, exit on failure diff --git a/scripts/create-zally-issue.sh b/scripts/create-zally-issue.sh index b328252ed..98a9bc1a6 100755 --- a/scripts/create-zally-issue.sh +++ b/scripts/create-zally-issue.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#! /bin/bash set -ex diff --git a/scripts/generate-includes.sh b/scripts/generate-includes.sh new file mode 100755 index 000000000..51f8a65da --- /dev/null +++ b/scripts/generate-includes.sh @@ -0,0 +1,44 @@ +#! /bin/bash + +readonly SOURCES=("models/headers-1.0.0.yaml"); +readonly HEADERS=($(cat "${SOURCES[@]}" | awk ' + BEGIN { mode = "parameters" } + ($0 ~ "^#.*response headers") { + mode = "headers"; next + } + ($0 ~ "^#.*request headers$") { + mode = "parameters"; next + } + ($0 ~ "^#.*common headers$") { + mode = "parameters"; next + } + ($0 ~ "^[A-Z][A-Za-z-]*:$") { + print $0 mode + }')) + +FILE="/dev/stdout"; +for HEADER in "${HEADERS[@]}"; do + NAME="${HEADER%%:*}"; MODE="${HEADER##*:}"; + if [ -n "${1}" ]; then FILE="${1}/${NAME,,}.yaml"; fi; + awk -v name="${NAME}" -v mode="${MODE}" ' + (($0 ~ "^[A-Z][A-Za-z-]*:$" || $0 ~ "^#.*$") && content) { + print content; content = "" + } + + (content) { + if (mode == "headers" && ($1 == "in:" || $1 == "name:")) next; + if ($0 != "") { + content = content "\n " $0; empty = 0 + } else if (!empty) { + content = content "\n"; empty = 1 + } + } + + ($0 ~ "^" name ":$") { + content = "components:\n " mode ":\n " $0; empty = 0 + } + + END { + if (content) { print content } + }' "${SOURCES[@]}" > "${FILE}"; +done; diff --git a/scripts/generate-rules-json.sh b/scripts/generate-rules-json.sh index 15fbcf182..af0c6eaeb 100755 --- a/scripts/generate-rules-json.sh +++ b/scripts/generate-rules-json.sh @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#! /bin/bash # This script generates a JSON with information about the guideline rules. cat chapters/*.adoc | \ diff --git a/scripts/new-rule-id.sh b/scripts/new-rule-id.sh deleted file mode 100755 index 377505f73..000000000 --- a/scripts/new-rule-id.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash -# This script generates a new (unused) rule id. - -CONTENT_DIR=chapters -rule_ids=() - - -IFS=$'\r\n' GLOBIGNORE='*' command eval "rule_ids=($(grep -r -h '^.*\[#[0-9]\{1,5\}.*$' ${CONTENT_DIR} | sort -r))" -last_used_rule_id=`echo ${rule_ids[0]} | tr -d '\[' | tr -d '\]' | tr -d '#'` - -echo $((last_used_rule_id +1 )) \ No newline at end of file