skip to content

How do an OData client and service agree on a protocol version, and how did v2's DataServiceVersion headers differ from v4's OData-Version and OData-MaxVersion?

level: middleimportance: should knowfreq 11%

answer

  1. one header per direction
  2. the client's ceiling
  3. lowest that works versus greatest allowed
  4. absent header defaults

basics

~20 s

Both lines pair a version header with a ceiling. In v2 a server answers with the lowest version that serves the request, capped by MaxDataServiceVersion; in v4 the service MUST answer with the greatest version it supports that does not exceed OData-MaxVersion.

solid answer

~40 s

Each request can say which protocol version produced its payload and the highest version the client can read. In v2 those are `DataServiceVersion` and `MaxDataServiceVersion`; the server rejects a request whose version it doesn't support with a 4xx, and its response `DataServiceVersion` should be the *lowest* version able to fulfil the request. In v4 they are `OData-Version` and `OData-MaxVersion`: the service MUST interpret the request payload by the stated `OData-Version` or fail with a 4xx, and MUST generate the response at the *greatest* version it supports that is at or below `OData-MaxVersion`, stating it in a mandatory response `OData-Version`. Request and response versions are independent. Without `OData-MaxVersion`, 4.01 says a service SHOULD keep answering at the version it launched with, which is why interoperable clients always send the header.

code

http · 12 lines
http
GET /sales/Orders?$top=1 HTTP/1.1
Host: api.example.com
OData-MaxVersion: 4.0
Accept: application/json

HTTP/1.1 200 OK
OData-Version: 4.0
Vary: OData-MaxVersion
Content-Type: application/json

{"@odata.context": "https://api.example.com/sales/$metadata#Orders",
 "value": [{"ID": 1001, "Status": "Open"}]}

go deeper

for a junior

Recall the two header pairs: DataServiceVersion and MaxDataServiceVersion in v2, OData-Version and OData-MaxVersion in v4.

for a middle

Explain the opposite selection rules: v2 answers with the lowest version that fulfils the request, v4 with the greatest supported version at or below the client's ceiling.

for a senior

Show the operational consequences: silent clients and the 4.01 initial-publication rule, Vary for caches, and why every client should send OData-MaxVersion.

for a principal

Separate protocol versioning from model versioning and argue which one a planned change actually needs before anyone touches the service root.

## What is being negotiated An OData **protocol version** governs three things at once: URL conventions, payload format and HTTP interaction rules. It is not the version of your data model or of your API. Both OData lines let the two sides state two facts on every exchange: - the version used to **produce a payload** (request or response), and - the **maximum version** the client can **read** in a response. The headers and the selection rule changed between the Microsoft-published v2 and the OASIS v4 line. | Aspect | OData v2 | OData 4.0 / 4.01 | |---|---|---| | Version of a payload | `DataServiceVersion` | `OData-Version` | | Client's ceiling | `MaxDataServiceVersion` | `OData-MaxVersion` | | Response version chosen | the **lowest** version that can fulfil the request (SHOULD) | the **greatest** supported version not above `OData-MaxVersion` (MUST) | | Response header | should be sent | MUST be sent | | Version values | `1.0`, `2.0` | `4.0`, `4.01` | ## The v2 rules The v2 overview calls this "limited capability negotiation" and only *highly encourages* the headers. 1. If a request has no `DataServiceVersion`, the server must assume the highest version it supports. 2. If it has no `MaxDataServiceVersion`, the server assumes the same value as `DataServiceVersion`; if both are missing, it assumes the client can read the server's maximum. 3. If the request's `DataServiceVersion` exceeds what the server supports, the server must answer with a 4xx and should describe the error in the OData error format. 4. If `MaxDataServiceVersion` is below the minimum version needed to build the response, the server must return an error. 5. The response `DataServiceVersion` should be the **lowest** version that fulfils the request. Rule 5 means a plain read is often answered as `1.0`, while a request using a 2.0-only feature such as `$inlinecount` or `$select` comes back as `2.0`. A v2 client therefore has to cope with both response shapes. ## The v4 rules - A client **SHOULD** send `OData-Version` to state the version of its request payload. The service **MUST** interpret the payload by that version or fail with a 4xx. - Without `OData-Version`, the service **MUST** assume the minimum of `OData-MaxVersion` (if sent) and the highest version it understands. - A client **SHOULD** send `OData-MaxVersion`; the 4.01 interoperability list goes further and says clients **MUST**. If it is sent, the service **MUST** generate the response at the **greatest supported version** at or below it, using decimal comparison. - The service **MUST** put `OData-Version` on every response, and the client **MUST** read the payload by that version. - Request and response payloads are independent and may carry different `OData-Version` values. ```http GET /sales/Orders?$top=1 HTTP/1.1 Host: api.example.com OData-MaxVersion: 4.0 Accept: application/json HTTP/1.1 200 OK OData-Version: 4.0 Vary: OData-MaxVersion Content-Type: application/json {"@odata.context": "https://api.example.com/sales/$metadata#Orders", "value": [ ... ]} ``` Here a service that supports both 4.0 and 4.01 answers in 4.0 because the client capped it there, so the payload keeps the `odata.` prefix. ## When the client sends no ceiling This rule moved between the OASIS releases: - **4.0**: the service SHOULD treat the request as if `OData-MaxVersion` were its own maximum. - **4.01**: the service SHOULD return responses with the same version over time, treating the missing header as the maximum version it supported **at its initial publication**. A service launched on 4.0 and later upgraded therefore keeps answering silent clients in 4.0. The 4.01 conformance list still allows a service to return 4.01 content to a client that did not send `OData-MaxVersion: 4.0`, so the safe client habit is to always send the header. ## Caching and batches - If a response varies with the version chosen, 4.01 requires the service to send `Vary` listing `OData-MaxVersion`, so a shared cache does not hand a 4.01 body to a 4.0 client. - Inside a batch, a part without its own version headers inherits those of the enclosing batch request. ## What the headers do not do They version the **protocol**, not your entity model. Breaking model changes are a separate mechanism: 4.0 expects a new service root, and 4.01 adds schema versioning with the `Core.SchemaVersion` annotation and the `$schemaversion` query option. A v4 specification also defines no handling for `DataServiceVersion`, so these headers cannot bridge a v2 client to a v4 service.

  • Why does a shared HTTP cache in front of an OData service care about OData-MaxVersion?
    When a service can produce the same resource in 4.0 or 4.01, the body depends on a request header the URL does not show. 4.01 therefore requires the service to send `Vary` listing `OData-MaxVersion` whenever the response varies by version, so a cache keys on that header and does not serve a 4.01 payload (unprefixed `@context`, `@type` without `#`) to a client capped at 4.0.
  • In OData v2, what must a server do when MaxDataServiceVersion is lower than the version it needs to answer?
    Return an error rather than a response the client cannot read. The v2 rules say the server must check that `MaxDataServiceVersion` (or its derived value) is at least the minimum version needed to generate the response, and if not, answer with an error in the OData XML or JSON error format. Likewise, a request whose `DataServiceVersion` exceeds the server's maximum gets a 4xx.

saying these in an interview costs you the question

  • The response version is always the OData-Version the client sent on its request.
  • A v4 service answers with the lowest version that works, exactly as v2 did.
  • A v4 service must reject any request that lacks OData-MaxVersion with 400.
  • OData-Version tells clients which version of the service's data model they are using.
  • A v4 service must accept DataServiceVersion as an alias for OData-Version.