Introdução
Requisições HTTP GET são a forma mais comum de buscar dados da web. Em Zig, a biblioteca padrão oferece std.http.Client para fazer requisições HTTP de forma eficiente e com controle total sobre headers, timeouts e tratamento de erros.
Nesta receita, você aprenderá a fazer requisições HTTP GET, processar respostas, lidar com headers e consumir APIs REST.
Pré-requisitos
- Zig instalado (versão 0.13+). Veja o guia de instalação
- Conhecimento básico de Zig. Consulte a introdução ao Zig
Requisição GET Simples
O exemplo mais básico de uma requisição HTTP GET:
const std = @import("std");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Criar cliente HTTP
var client = std.http.Client{ .allocator = allocator };
defer client.deinit();
// Fazer a requisição GET
const uri = try std.Uri.parse("http://httpbin.org/get");
var header_buf: [4096]u8 = undefined;
var req = try client.open(.GET, uri, .{
.server_header_buffer = &header_buf,
});
defer req.deinit();
try req.send();
try req.wait();
// Verificar status
if (req.status != .ok) {
std.debug.print("Erro HTTP: {}\n", .{req.status});
return;
}
// Ler o corpo da resposta
var body = std.ArrayList(u8).init(allocator);
defer body.deinit();
var buf: [4096]u8 = undefined;
while (true) {
const bytes_read = try req.reader().read(&buf);
if (bytes_read == 0) break;
try body.appendSlice(buf[0..bytes_read]);
}
std.debug.print("Status: {}\n", .{req.status});
std.debug.print("Corpo: {s}\n", .{body.items});
}
Saída esperada
Status: http.Status.ok
Corpo: {
"args": {},
"headers": {
"Host": "httpbin.org",
...
},
"url": "http://httpbin.org/get"
}
GET com Headers Customizados
Adicione headers personalizados à requisição:
const std = @import("std");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var client = std.http.Client{ .allocator = allocator };
defer client.deinit();
const uri = try std.Uri.parse("https://api.example.com/data");
var header_buf: [4096]u8 = undefined;
var req = try client.open(.GET, uri, .{
.server_header_buffer = &header_buf,
.extra_headers = &.{
.{ .name = "Authorization", .value = "Bearer meu-token-aqui" },
.{ .name = "Accept", .value = "application/json" },
.{ .name = "User-Agent", .value = "ZigClient/1.0" },
},
});
defer req.deinit();
try req.send();
try req.wait();
std.debug.print("Status: {}\n", .{req.status});
}
Função Reutilizável de GET
Encapsule a lógica em uma função para reutilização:
const std = @import("std");
const HttpResponse = struct {
status: std.http.Status,
body: []const u8,
allocator: std.mem.Allocator,
pub fn deinit(self: *HttpResponse) void {
self.allocator.free(self.body);
}
};
fn httpGet(allocator: std.mem.Allocator, url: []const u8) !HttpResponse {
var client = std.http.Client{ .allocator = allocator };
defer client.deinit();
const uri = try std.Uri.parse(url);
var header_buf: [8192]u8 = undefined;
var req = try client.open(.GET, uri, .{
.server_header_buffer = &header_buf,
});
defer req.deinit();
try req.send();
try req.wait();
// Ler corpo completo
const body = try req.reader().readAllAlloc(allocator, 1024 * 1024); // máx 1 MB
return .{
.status = req.status,
.body = body,
.allocator = allocator,
};
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Usar a função reutilizável
var response = try httpGet(allocator, "http://httpbin.org/get");
defer response.deinit();
std.debug.print("Status: {}\n", .{response.status});
std.debug.print("Tamanho do corpo: {d} bytes\n", .{response.body.len});
}
Consumindo uma API REST com JSON
Combine HTTP GET com parsing JSON para consumir APIs:
const std = @import("std");
const Todo = struct {
userId: i64,
id: i64,
title: []const u8,
completed: bool,
};
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var client = std.http.Client{ .allocator = allocator };
defer client.deinit();
const uri = try std.Uri.parse("https://jsonplaceholder.typicode.com/todos/1");
var header_buf: [4096]u8 = undefined;
var req = try client.open(.GET, uri, .{
.server_header_buffer = &header_buf,
});
defer req.deinit();
try req.send();
try req.wait();
if (req.status != .ok) {
std.debug.print("Erro: {}\n", .{req.status});
return;
}
// Ler corpo
const body = try req.reader().readAllAlloc(allocator, 1024 * 1024);
defer allocator.free(body);
// Parsear JSON
const parsed = try std.json.parseFromSlice(Todo, allocator, body, .{
.ignore_unknown_fields = true,
});
defer parsed.deinit();
const todo = parsed.value;
std.debug.print("Todo #{d}:\n", .{todo.id});
std.debug.print(" Título: {s}\n", .{todo.title});
std.debug.print(" Completo: {}\n", .{todo.completed});
std.debug.print(" Usuário: {d}\n", .{todo.userId});
}
Saída esperada
Todo #1:
Título: delectus aut autem
Completo: false
Usuário: 1
Tratamento de Erros HTTP
Trate diferentes códigos de status HTTP adequadamente:
const std = @import("std");
fn handleHttpResponse(status: std.http.Status) void {
switch (status) {
.ok => std.debug.print("Sucesso (200)!\n", .{}),
.not_found => std.debug.print("Recurso não encontrado (404).\n", .{}),
.unauthorized => std.debug.print("Não autorizado (401). Verifique suas credenciais.\n", .{}),
.forbidden => std.debug.print("Acesso proibido (403).\n", .{}),
.internal_server_error => std.debug.print("Erro interno do servidor (500).\n", .{}),
.too_many_requests => std.debug.print("Muitas requisições (429). Aguarde.\n", .{}),
else => std.debug.print("Status inesperado: {d}\n", .{@intFromEnum(status)}),
}
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var client = std.http.Client{ .allocator = allocator };
defer client.deinit();
const uri = try std.Uri.parse("http://httpbin.org/status/404");
var header_buf: [4096]u8 = undefined;
var req = try client.open(.GET, uri, .{
.server_header_buffer = &header_buf,
});
defer req.deinit();
try req.send();
try req.wait();
handleHttpResponse(req.status);
}
Dicas e Boas Práticas
Sempre libere recursos: Use
deferparaclient.deinit()ereq.deinit().Verifique o status: Nem toda resposta é
200 OK. Sempre verifique o código de status.Limite o tamanho da resposta: Use
readAllAlloccom um limite máximo para evitar uso excessivo de memória.Reutilize o client: Se fizer múltiplas requisições, reutilize a instância de
std.http.Client.Para JSON: Combine com parsing JSON em Zig para APIs REST.
Receitas Relacionadas
- Como fazer requisições HTTP POST em Zig - Envio de dados
- Como parsear JSON em Zig - Processar respostas JSON
- Como parsear URLs em Zig - Manipulação de URLs
- Como fazer lookup DNS em Zig - Resolução de nomes