Skip to content

Feat/gh7137 content range#7856

Open
Nayte91 wants to merge 2 commits intoapi-platform:mainfrom
Nayte91:feat/GH7137-content-range
Open

Feat/gh7137 content range#7856
Nayte91 wants to merge 2 commits intoapi-platform:mainfrom
Nayte91:feat/GH7137-content-range

Conversation

@Nayte91
Copy link

@Nayte91 Nayte91 commented Mar 19, 2026

Q A
Branch? main
Tickets #7137
License MIT
Doc PR no but I can do this after

Pagination in headers: Range request header and content-range response handling.

I created a PR for this topic, please see the issue for the 'why it's cool'.

For now, there is 2 separate commits:

  • 1 for content-range response
  • 1 for range request

Range request header

This commit adds server-side parsing of the Range request header, enabling clients to request specific slices of a collection via standard HTTP semantics.

RFC 9110 sections followed

§14.2 Range — defines the request header format: Range: <unit>=<first-pos>-<last-pos>. Unrecognized formats or units are silently ignored (the server delegates normally), as recommended by the spec.
§15.3.7 206 Partial Content — the response status when a valid Range request is successfully fulfilled. The operation status is set to 206 so RespondProcessor returns it alongside the Content-Range header.
§15.5.17 416 Range Not Satisfiable — returned when the range is syntactically valid but not satisfiable (start > end, or range not aligned to page boundaries).

Summed up design considerations

  • where does API-Platform give access to request's headers?
  • where do we give pagination's info to ORMs?
  • where do we put code that link those?

Main logic implemented

  • A new RangeHeaderProvider decorator on the read provider — parses the Range header, converts the range to page/itemsPerPage pagination filters via _api_filters, and sets the operation status to 206. Follows the same decorator pattern as JsonApiProvider.
  • Service registration in provider.php — decorates api_platform.state_provider.read (at priority 1).

Tests

  • A dedicated RangeHeaderProviderTest — 9 unit tests covering delegation (no header, wrong operation type, wrong HTTP method), ignored formats (unparseable, wrong unit), valid ranges, and 416 errors.
  • Two additional integration tests in ContentRangeHeaderTest — verify the full flow: Range header in → 206 status + Content-Range header out.

Content-range response header

This commit adds RFC 9110-compliant Content-Range and Accept-Ranges response headers to all paginated collection endpoints.

RFC 9110 sections followed

§14.4 Content-Range — defines the header grammar: range-unit SP (range-resp/unsatisfied-range). We use custom range units (for example book) as allowed by the extensible range unit mechanism. The unsatisfied-range production (*/complete-length) requires complete-length to be a digit, so the header is omitted when both the page is empty and the total is unknown (since */* would be invalid ABNF).
§14.3 Accept-Ranges — advertises which range unit the server supports for a given resource. Sent on every paginated collection response.
§14.1 Range Units — confirms that range units are extensible tokens, not limited to bytes.
§15.3.7 206 Partial Content — clarifies that 206 is strictly reserved for responses to Range requests. Since this commit only handles the response side (no Range request parsing yet), the status code remains 200.

Summed up design considerations

  • where does API-Platform give access to ORMs' pagination info?
  • where do we build response's headers before sending it?
  • where do we code the link between those?

Main logic implemented

  • Private method HttpResponseHeadersTrait::addContentRangeHeaders() — fetches range from paginator (0-indexed offsets) and emits the Content-Range & Accept-Ranges headers.

Tests

  • Dedicated ContentRangeHeaderTest — 11 test cases covering partial collections, full collections, page offsets, unknown totals, empty pages, non-collection operations, and the unit fallback.

Limitations & concerns

  1. Current use of a HEAD verb, to just receive pagination through header's Content-range, doesn't save any cycles as it is processed just like a GET. The content is stripped right before sending the response. There's a clear room for improvement here! → Will be in a totally separated issue, because it's another topic.
  2. Potential BC break for projects that use and make proper implementation of those headers
  3. API break if status codes are willing to change (if your project is wired to make things on 200 responses but API-Platform fires some 206),
  4. In committed files there's a lot of comments with RFC links, to help reviewing. It's meant to be removed.
  5. I did a specific testfiles, ContentRangeHeaderTest & RangeHeaderProviderTest to help designing the feature. We can totally merge everything in other testfiles if wanted.
  6. feat(state): ... → is it really the best domain? Don't we have a headers domain?
  7. Content-Range & Range topics can totally be in 2 separate PR, if you want to zoom in for reviews & discussions. Let me know.
  8. We need to discuss also about resource in plural in Content-Range. I put it in plural because it was easy in current implementation.

@Nayte91 Nayte91 force-pushed the feat/GH7137-content-range branch 3 times, most recently from 7f9c148 to 31e727b Compare March 21, 2026 09:26
Nayte91 and others added 2 commits March 21, 2026 10:46
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Nayte91 Nayte91 force-pushed the feat/GH7137-content-range branch from 31e727b to 1606fe2 Compare March 21, 2026 09:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant