BroadNet
HTTP API & webhooksDesign de API

Erros de API (problem+json) e paginação (cursor, header Link)

Atualizado em 7 min de leituraBaseado em padrões
Resposta direta

A RFC 9457 problem+json normaliza as respostas de erro como um objeto JSON com type, title, status, detail e instance, servido como application/problem+json — para que os clientes façam parse dos erros de forma uniforme. A paginação divide coleções grandes em páginas usando um cursor (estável) ou offset (simples), com links next/prev transportados no header Link da RFC 8288.

§01

problem+json: um objeto de erro padrão

Formas de erro à medida obrigam cada cliente a tratar como caso especial cada API. A RFC 9457 (que torna obsoleta a RFC 7807) fixa um único formato de erro JSON, extensível, servido com o media type application/problem+json para que um cliente saiba interpretá-lo como um problema. O objeto tem cinco membros padrão: type, um URI que identifica a classe do problema e serve também de link para documentação humana; title, um resumo curto, estável e legível dessa classe; status, o status code HTTP ecoado no corpo; detail, uma explicação legível específica desta ocorrência; e instance, um URI que identifica esta ocorrência em particular. Quaisquer membros adicionais (um array de erros de validação, um request id, uma pista de retry) estendem o objeto sem quebrar os padrão, pelo que um cliente genérico lê os campos comuns e um cliente específico lê também as extensões.

Corpo de erro problem+json da RFC 9457json
HTTP/1.1 422 Unprocessable Content
Content-Type: application/problem+json

{
  "type": "https://example/problems/invalid-destination",
  "title": "Invalid destination number",
  "status": 422,
  "detail": "The field 'to' is not a valid E.164 number.",
  "instance": "/v1/messages/req-01HZY9",
  "errors": [ { "field": "to", "issue": "not_e164" } ]
}
§02

Type, title e instance

A disciplina que torna o problem+json útil é manter type e title estáveis e orientados a máquina, ao passo que detail e instance variam por ocorrência. Um cliente deve ramificar por type (um URI estável), nunca pela prosa em detail ou title, porque detail é livre de mudar e pode ser localizado. O URI de type resolve idealmente para documentação dessa classe de erro, para que um programador que o siga aterre numa explicação e num remédio. instance identifica o pedido específico que falhou — útil para citar num ticket de suporte — e espelha muitas vezes um request id que a API também devolve num header. Como type e title identificam a classe e status/detail/instance descrevem a ocorrência, um tipo de problema pode ser reutilizado em muitas falhas sem ambiguidade.

§03

Taxonomia de erros: retryable vs terminal

Para além da forma, o cliente precisa de saber o que fazer, e a classe do status code responde a isso. Um 4xx é um erro de cliente: o pedido tal como enviado nunca terá sucesso, pelo que repeti-lo sem alteração é inútil — a correção é corrigir o pedido (400/422 para input inválido, 401/403 para autenticação, 404 para um recurso em falta, 409 para um conflito). Um 5xx é um erro de servidor ou a montante: o pedido pode muito bem ter sucesso mais tarde, pelo que é retryable com backoff — idealmente sob o mesmo Idempotency-Key para que o retry não possa enviar em duplicado. O 429 é o caso transitório especial: retryable, mas só depois do atraso Retry-After. Codificar isto no status code (e reforçá-lo com um tipo de problema) permite a um cliente decidir automaticamente entre corrigir-e-reenviar, recuar-e-repetir e desistir.

Classe de erro e a ação correta do cliente
ClasseSignificadoPermite nova tentativa?Ação do cliente
400 / 422Entrada malformada/inválidaNoCorrija a solicitação e envie-a novamente
401 / 403Não autenticado / proibidoNoCorrija as credenciais ou o âmbito
404Recurso não encontradoNoCorrija o identificador
409Conflito de estado (ex: reutilização de chave)NoResolver o conflito
429Limite de taxa excedidoSim (com atraso)Aguardar o Retry-After e tentar novamente
500 / 502 / 503Falha no servidor/upstreamSimBackoff + nova tentativa com a mesma chave de idempotência
§04

Paginação: cursor vs offset

Um endpoint de coleção que poderia devolver milhões de mensagens tem de paginar. Os dois modelos principais trocam simplicidade por correção. A paginação por offset (?limit=50&offset=100) é trivial e permite ao cliente saltar para uma página arbitrária, mas é instável numa coleção em mudança: se linhas forem inseridas ou apagadas entre páginas, os itens deslocam-se e o cliente salta ou repete registos — e offsets profundos ficam lentos porque o armazenamento tem de percorrer e descartar tudo o que os antecede. A paginação por cursor (keyset) entrega ao cliente um cursor opaco que codifica a posição do último item visto (por exemplo um id e um timestamp); o pedido seguinte pede as linhas após esse cursor. É estável sob escritas concorrentes e mantém-se rápida a qualquer profundidade porque a query salta diretamente para o cursor, ao custo de não suportar saltos arbitrários de página. Para recursos de alto volume e append-heavy, como logs de mensagens, a paginação por cursor é o default correto.

Paginação por cursor vs offset
PropriedadeOffsetCursor (conjunto de teclas)
Requisição?limit=50&offset=100?limit=50&after=<cursor>
Salto de página arbitrárioSimNão (somente sequencial)
Estável sob inserções/exclusõesNão — os itens mudam de posiçãoSim
Desempenho em páginas profundasDegrada (escanear + descartar)Constante (seek até o cursor)
Ideal paraColeções pequenas e estáveisLogs grandes, com muitas inclusões ao final (append)
§05

O header Link (RFC 8288)

Em vez de enterrar os URLs next/previous no corpo, uma API pode transportá-los no header Link padrão definido pela RFC 8288 (Web Linking), usando tipos de relação registados — next, prev, first, last. O cliente segue o link rel="next" para avançar sem construir URLs por si nem conhecer o esquema de cursor, o que mantém o mecanismo de paginação inteiramente controlado pelo servidor e o deixa evoluir (offset hoje, cursor amanhã) sem quebrar clientes. Uma abordagem redundante comum também espelha os links no corpo JSON para clientes que não leem headers, mas o header Link é o lar normalizado, ao nível do transporte, para eles e é o mesmo mecanismo que um browser ou um cliente de API bem-comportado já compreendem.

Paginação através do header Link da RFC 8288http
HTTP/1.1 200 OK
Content-Type: application/json
Link: </v1/messages?after=01HZY8Q3M6&limit=50>; rel="next",
      </v1/messages?limit=50>; rel="first"

{ "data": [ /* up to 50 messages */ ],
  "page": { "count": 50, "has_more": true } }
§06

Limites e defaults de tamanho de página

O tamanho da página tem de ser limitado no servidor, não deixado ao cliente, ou um único pedido de uma página enorme torna-se um vetor de negação de serviço e uma fonte de latência imprevisível. O padrão é um default sensato (usado quando o cliente omite limit) e um máximo rígido a que o servidor limita silenciosamente — um cliente que peça mais recebe simplesmente o máximo. Anunciar tanto o tamanho de página efetivo como se restam mais páginas (uma flag has_more ou a presença de um link rel="next") permite a um cliente iterar com segurança: continuar a seguir next até deixar de haver next. Combinado com paginação por cursor e erros problem+json, isto dá uma API de coleção previsível de consumir a qualquer escala. A BroadNet documenta os seus códigos de erro e paginação concretos para programadores.

Perguntas frequentes

Perguntas frequentes

O que é o problem+json e porquê usá-lo?

problem+json (RFC 9457, que torna obsoleta a RFC 7807) é um formato de erro JSON padrão servido como application/problem+json, com os membros type, title, status, detail e instance. Permite que cada cliente faça parse dos erros da mesma forma em vez de tratar como caso especial uma forma à medida por API, e é extensível para que membros extra possam ser acrescentados sem quebrar os padrão.

Por que campo do problem+json deve um cliente ramificar?

type — um URI estável que identifica a classe de erro. title e detail são legíveis por humanos e podem mudar ou ser localizados, pelo que o código nunca deve decidir pela sua prosa. O URI de type resolve idealmente para documentação desse erro, ao passo que status, detail e instance descrevem a ocorrência específica.

Como sabe um cliente se deve repetir um erro?

Pela classe do status. Um 4xx é um erro de cliente que não terá sucesso sem alteração, pelo que se corrige o pedido em vez de repetir. Um 5xx é uma falha de servidor/a montante que pode ter sucesso mais tarde, pelo que se repete com backoff sob o mesmo Idempotency-Key. O 429 é retryable mas só depois do atraso Retry-After.

Qual é a diferença entre paginação por cursor e por offset?

O offset (?limit&offset) é simples e suporta saltar para qualquer página, mas é instável quando linhas são inseridas ou apagadas entre pedidos e abranda em offsets profundos. A paginação por cursor (keyset) passa um marcador opaco do último item visto e pede as linhas após ele — estável sob escritas concorrentes e rápida a qualquer profundidade, mas só sequencial. O cursor é o default certo para logs de mensagens grandes.

Como ajuda o header Link da RFC 8288 na paginação?

Transporta os URLs das páginas next, prev, first e last num header padrão usando tipos de relação registados, para que o cliente siga rel="next" para avançar sem construir URLs nem conhecer o esquema de cursor. Isso mantém o mecanismo de paginação controlado pelo servidor e livre de evoluir, e é um mecanismo que os clientes HTTP genéricos já compreendem.

Porquê limitar o tamanho de página no servidor?

Porque um tamanho de página ilimitado deixa um pedido puxar um conjunto de resultados enorme, o que é um risco de negação de serviço e dá latência imprevisível. O servidor aplica um default quando limit é omitido e limita silenciosamente a um máximo rígido, e anuncia se restam mais páginas para que os clientes possam iterar seguindo next até este estar ausente.

Referências

Especificações de origem

Os padrões publicados nos quais esta página se baseia. Abre num novo separador.

BNMantido pela equipa de engenharia BroadNet·Mensagens de nível de operadora desde 2003
Coloque-o no fio.

Construa com uma vinculação SMPP real e uma API REST.

Solicite acesso de teste com chaves de teste reais: envie primeiro para o seu próprio número verificado e, em seguida, passe para o ambiente de produção em mais de 200 países assim que a sua identidade de remetente e rotas forem aprovadas.