Core: Introduce X-Total for GET request with pagination

Created on 6 Dec 2017  路  17Comments  路  Source: api-platform/core

I noted seems totalItems value returns only in format Hydra collection.
What about idea add X-Total, X-Current-Page, X-Page-Count, X-Limit to headers for any pagination request particulary json format?!

doc enhancement

Most helpful comment

We are using headers for pagination in json format:

<?php

declare(strict_types=1);

namespace AppBundle\EventSubscriber;

use ApiPlatform\Core\Bridge\Doctrine\Orm\Paginator;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;

final class AddPaginationHeaders implements EventSubscriberInterface
{
    public function addHeaders(FilterResponseEvent $event): void
    {
        $request = $event->getRequest();

        if (($data = $request->attributes->get('data')) && $data instanceof Paginator) {
            $from = $data->count() ? ($data->getCurrentPage() - 1) * $data->getItemsPerPage() : 0;
            $to = $data->getCurrentPage() < $data->getLastPage() ? $data->getCurrentPage() * $data->getItemsPerPage() : $data->getTotalItems();

            $response = $event->getResponse();
            $response->headers->add([
                'Accept-Ranges' => 'items',
                'Range-Unit' => 'items',
                'Content-Range' => \sprintf('%u-%u/%u', $from, $to, $data->getTotalItems()),
            ]);
        }
    }

    /**
     * {@inheritdoc}
     */
    public static function getSubscribedEvents(): array
    {
        return [
            KernelEvents::RESPONSE => 'addHeaders',
        ];
    }
}

All 17 comments

Why not if it's an opt-in feature. Do you know some kind of standard for those headers (using the X- prefix is deprecated)?

OData has something, there is also the Range HTTP header: http://otac0n.com/blog/2012/11/21/range-header-i-choose-you.html

We are using headers for pagination in json format:

<?php

declare(strict_types=1);

namespace AppBundle\EventSubscriber;

use ApiPlatform\Core\Bridge\Doctrine\Orm\Paginator;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;

final class AddPaginationHeaders implements EventSubscriberInterface
{
    public function addHeaders(FilterResponseEvent $event): void
    {
        $request = $event->getRequest();

        if (($data = $request->attributes->get('data')) && $data instanceof Paginator) {
            $from = $data->count() ? ($data->getCurrentPage() - 1) * $data->getItemsPerPage() : 0;
            $to = $data->getCurrentPage() < $data->getLastPage() ? $data->getCurrentPage() * $data->getItemsPerPage() : $data->getTotalItems();

            $response = $event->getResponse();
            $response->headers->add([
                'Accept-Ranges' => 'items',
                'Range-Unit' => 'items',
                'Content-Range' => \sprintf('%u-%u/%u', $from, $to, $data->getTotalItems()),
            ]);
        }
    }

    /**
     * {@inheritdoc}
     */
    public static function getSubscribedEvents(): array
    {
        return [
            KernelEvents::RESPONSE => 'addHeaders',
        ];
    }
}

@norkunas nice! I need try this. @dunglas good if we decide foramt how return that data and introduce to platform from out of box feature

@dunglas when I suggested it, you've said there's no reason to introduce a custom hypermedia standard if you support Hydra, glad you've changed your mind. :+1:

While I like @norkunas approach, I feel like it would need to hook into the doc generator since you're exposing information which might be used for the SDK generator (Swagger).

Looking for this! I needed just a simple application/json format that counts items.

@norkunas: Easy solution. Thanks mate.

Another sample with GitHub API: https://developer.github.com/v3/#pagination

They use Link header to display the next page and the last page.

I'm not sure there is any convention about this, you may choose a default one and add the possibility to easily change it.

@dunglas According to this comment, the range header may break some cli supports.

EDIT: Plus this one telling the range is just for bytes.

I'm not sure it's the best solution. :thinking:

Link is pretty common for next/prev/etc (it exists for years), but harder to parse.

Range is definitely for partial content serving. IMHO we just need to add @norkunas snippet to the documentation to close this issue.

@soyuka Why not proposing an option for that?

@soyuka @dunglas may I propose to add the opt-in listener to the core?

Yes we can add a new OData sub-namespace and progressively start to add support for some OData features.

Another sample with GitHub API: https://developer.github.com/v3/#pagination

They use Link header to display the next page and the last page.

I'm not sure there is any convention about this, you may choose a default one and add the possibility to easily change it.

Is there any effort to implement the Link header in the future?

We already do: https://github.com/api-platform/core/blob/345612c913e1aca6da4f4aa1cd885421ca6385ff/src/Hydra/EventListener/AddLinkHeaderListener.php but maybe it's missing some informations?
Also note that you can easily add your own listener that adds your own headers!

Thank you :+1:
Is there any consensus to use Link header over hypermedia by representation format (like HAL, json:api, ...)?
My question is: should we using a protocol-agnostic format or using a format-agnostic protocol to make our app hypermedia-driven?

(Sorry for asking here. Maybe there's a discussion forum that I didn't found yet)

Was this page helpful?
0 / 5 - 0 ratings