# HTTP, pedidos e Ajax

URIs, pedidos e respostas, cabeçalhos, REST, JSON e pesquisa com fetch sobre o PHP.

Página: https://resumos.rgo.pt/cadeiras/ltw/http-ajax-json/

O navegador e o servidor conversam por HTTP: o cliente envia um pedido e o servidor devolve uma resposta. Cada clique, cada formulário e cada pesquisa com `fetch` é um pedido destes. Perceber o formato da conversa permite depurar os dois lados e desenhar interfaces limpas entre eles.

[Vídeo: Hyper Text Transfer Protocol Crash Course](https://www.youtube.com/watch?v=0OrmKCB0UrQ)

A miniatura vem do YouTube. O vídeo só carrega quando clicas. [Abrir no YouTube](https://www.youtube.com/watch?v=0OrmKCB0UrQ)

## Ler um pedido e uma resposta

Um pedido tem um método, um caminho, cabeçalhos e às vezes um corpo. Uma resposta tem um estado, cabeçalhos e um corpo. Eis a pesquisa da mercearia escrita à mão, com o pedido a ir do navegador para o servidor e a resposta a voltar:

![Conversa HTTP da pesquisa: o navegador envia GET /pesquisa.php?q=queijo ao servidor PHP, que devolve 200 OK com corpo em application/json.](https://resumos.rgo.pt/cadeiras/ltw/http-ajax-json/figura-1.svg)

```
GET /pesquisa.php?q=queijo HTTP/1.1
Host: localhost:8000
Accept: application/json

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 42

[{"nome":"Queijo da Serra","preco":8.5}]
```

A primeira linha do pedido diz o método (`GET`), o caminho com os parâmetros (`/pesquisa.php?q=queijo`) e a versão do protocolo. A primeira linha da resposta diz o estado: `200 OK` correu bem, `404` não encontrou, `500` partiu no servidor. O cabeçalho `Content-Type` diz em que formato vem o corpo, e o corpo traz os dados. Quando uma página falhar, abre o separador de rede das ferramentas de desenvolvimento e lê esta conversa: o estado e o corpo dizem de que lado está o erro antes de tocares no código.

Os métodos mais usados são `GET` para ler, `POST` para criar ou enviar formulários, `PUT` para substituir e `DELETE` para apagar. A diferença que decide qual usar:

```
GET /pesquisa.php?q=queijo HTTP/1.1
```

O `GET` lê sem alterar nada no servidor: é seguro repetir, marcar como favorito e recarregar. Os parâmetros viajam no endereço, por isso nunca levam palavras passe nem ações. Leituras e pesquisas vão por aqui.

```
POST /encomenda.php HTTP/1.1
Content-Type: application/x-www-form-urlencoded

nome=Ana&qtd=2
```

O `POST` entrega um corpo para o servidor guardar ou alterar algo: encomendar, entrar, apagar. Repetir pode duplicar a ação, por isso o navegador avisa antes de reenviar. Tudo o que altera vai por aqui ou pelos outros métodos de escrita.

Regra prática: `GET` nunca altera nada no servidor, por isso é seguro repetir e marcar como favorito; tudo o que altera vai por `POST` ou pelos outros métodos.

## REST e JSON

Uma interface REST organiza o servidor por recursos com endereços estáveis: `GET /produtos` lista, `GET /produtos/3` lê um, `POST /produtos` cria. O formato de troca é quase sempre JSON, que é texto com a sintaxe dos objetos e arrays do JavaScript:

```
{ "nome": "Queijo da Serra", "preco": 8.5, "stock": true }
```

Em [PHP](https://resumos.rgo.pt/cadeiras/ltw/http-ajax-json/php-dinamicas-bd/), uma página que devolve JSON em vez de HTML é um serviço que qualquer cliente consome. O par do `fetch` da secção seguinte é este `api.php` mínimo, que reaproveita a consulta da página de pesquisa:

```
<?php
header('Content-Type: application/json');
$db = new PDO('sqlite:loja.sqlite');
$termo = $_GET['q'] ?? '';
$stmt = $db->prepare('SELECT nome, preco FROM produtos WHERE nome LIKE :t');
$stmt->execute([':t' => '%' . $termo . '%']);
echo json_encode($stmt->fetchAll());
?>
```

O `header` antes de qualquer saída declara o formato, e o `json_encode` transforma as linhas em texto JSON. Sem o cabeçalho certo, o `fetch` recebe texto que o `r.json()` recusa. Em JavaScript, `JSON.parse` transforma texto em objeto e `JSON.stringify` faz o inverso.

[Vídeo: RESTful APIs in 100 Seconds](https://www.youtube.com/watch?v=-MTSQjw5DrM)

A miniatura vem do YouTube. O vídeo só carrega quando clicas. [Abrir no YouTube](https://www.youtube.com/watch?v=-MTSQjw5DrM)

## Pesquisa com fetch sem recarregar

O `fetch` faz um pedido HTTP a partir do JavaScript e devolve uma promessa com a resposta. A pesquisa da mercearia filtra a lista sem recarregar a página:

```
async function pesquisar(termo) {
  const r = await fetch('api.php?q=' + encodeURIComponent(termo));
  if (!r.ok) throw new Error('HTTP ' + r.status);
  const produtos = await r.json();
  const lista = document.querySelector('#resultados');
  lista.replaceChildren();
  for (const p of produtos) {
    const li = document.createElement('li');
    li.textContent = p.nome + ' : ' + p.preco + ' €';
    lista.append(li);
  }
}
document.querySelector('#q').addEventListener('input', (e) => {
  pesquisar(e.target.value).catch(() => {
    document.querySelector('#resultados').textContent =
      'Pesquisa indisponível.';
  });
});
```

O `encodeURIComponent` protege acentos e espaços no parâmetro. O `await` pausa a função até a resposta chegar sem bloquear a página. O teste `r.ok` apanha estados como 404 e 500 antes de tentar ler JSON que não existe. E o `catch` mostra uma mensagem em vez de deixar a lista antiga a fingir que está atualizada. Cada pedido destes aparece no separador de rede, onde confirmas o endereço, o estado e o corpo antes de culpares o código.

O erro mais comum

Esquecer que o `fetch` é assíncrono e usar `produtos` fora da função ou antes do `await`, onde ainda é uma promessa pendente. Tudo o que depende da resposta vive depois do `await`, dentro da mesma função assíncrona.
