Introdução
Requisições HTTP POST são usadas para enviar dados ao servidor, como formulários, payloads JSON ou uploads de arquivos. Diferente do GET que apenas solicita dados, o POST modifica ou cria recursos no servidor.
Nesta receita, você aprenderá a fazer requisições POST em Zig usando std.http.Client, enviando diferentes tipos de dados.
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
- Familiaridade com requisições HTTP GET
POST com Corpo JSON
O caso mais comum: enviar dados JSON para uma API:
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://httpbin.org/post");
var header_buf: [4096]u8 = undefined;
var req = try client.open(.POST, uri, .{
.server_header_buffer = &header_buf,
.extra_headers = &.{
.{ .name = "Content-Type", .value = "application/json" },
},
});
defer req.deinit();
// Definir o corpo da requisição
const json_body =
\\{"nome": "Maria Silva", "email": "maria@exemplo.com", "idade": 28}
;
req.transfer_encoding = .{ .content_length = json_body.len };
try req.send();
// Escrever o corpo
var written: usize = 0;
while (written < json_body.len) {
written += try req.write(json_body[written..]);
}
try req.finish();
try req.wait();
// Ler resposta
const body = try req.reader().readAllAlloc(allocator, 1024 * 1024);
defer allocator.free(body);
std.debug.print("Status: {}\n", .{req.status});
std.debug.print("Resposta: {s}\n", .{body});
}
Saída esperada
Status: http.Status.ok
Resposta: {
"data": "{\"nome\": \"Maria Silva\", \"email\": \"maria@exemplo.com\", \"idade\": 28}",
...
}
POST com Struct Serializada para JSON
Serialize uma struct Zig diretamente para JSON antes de enviar:
const std = @import("std");
const Usuario = struct {
nome: []const u8,
email: []const u8,
idade: u32,
ativo: bool,
};
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Criar o objeto a ser enviado
const usuario = Usuario{
.nome = "João Santos",
.email = "joao@exemplo.com",
.idade = 35,
.ativo = true,
};
// Serializar para JSON
var json_buf = std.ArrayList(u8).init(allocator);
defer json_buf.deinit();
try std.json.stringify(usuario, .{}, json_buf.writer());
const json_body = json_buf.items;
std.debug.print("JSON a enviar: {s}\n", .{json_body});
// Enviar a requisição POST
var client = std.http.Client{ .allocator = allocator };
defer client.deinit();
const uri = try std.Uri.parse("https://httpbin.org/post");
var header_buf: [4096]u8 = undefined;
var req = try client.open(.POST, uri, .{
.server_header_buffer = &header_buf,
.extra_headers = &.{
.{ .name = "Content-Type", .value = "application/json" },
.{ .name = "Accept", .value = "application/json" },
},
});
defer req.deinit();
req.transfer_encoding = .{ .content_length = json_body.len };
try req.send();
var written: usize = 0;
while (written < json_body.len) {
written += try req.write(json_body[written..]);
}
try req.finish();
try req.wait();
if (req.status == .ok or req.status == .created) {
std.debug.print("Usuário criado com sucesso!\n", .{});
} else {
std.debug.print("Erro: {}\n", .{req.status});
}
}
POST com Form URL-Encoded
Envie dados de formulário no formato application/x-www-form-urlencoded:
const std = @import("std");
fn urlEncode(allocator: std.mem.Allocator, params: []const [2][]const u8) ![]u8 {
var result = std.ArrayList(u8).init(allocator);
errdefer result.deinit();
for (params, 0..) |param, i| {
if (i > 0) try result.append('&');
// Chave
for (param[0]) |c| {
if (std.ascii.isAlphanumeric(c) or c == '-' or c == '_' or c == '.' or c == '~') {
try result.append(c);
} else if (c == ' ') {
try result.append('+');
} else {
try result.appendSlice(&.{ '%', std.fmt.digitToChar(c >> 4, .upper), std.fmt.digitToChar(c & 0xf, .upper) });
}
}
try result.append('=');
// Valor
for (param[1]) |c| {
if (std.ascii.isAlphanumeric(c) or c == '-' or c == '_' or c == '.' or c == '~') {
try result.append(c);
} else if (c == ' ') {
try result.append('+');
} else {
try result.appendSlice(&.{ '%', std.fmt.digitToChar(c >> 4, .upper), std.fmt.digitToChar(c & 0xf, .upper) });
}
}
}
return result.toOwnedSlice();
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Codificar parâmetros do formulário
const params = [_][2][]const u8{
.{ "usuario", "maria_silva" },
.{ "senha", "minha_senha_123" },
.{ "lembrar", "true" },
};
const form_body = try urlEncode(allocator, ¶ms);
defer allocator.free(form_body);
std.debug.print("Form body: {s}\n", .{form_body});
var client = std.http.Client{ .allocator = allocator };
defer client.deinit();
const uri = try std.Uri.parse("https://httpbin.org/post");
var header_buf: [4096]u8 = undefined;
var req = try client.open(.POST, uri, .{
.server_header_buffer = &header_buf,
.extra_headers = &.{
.{ .name = "Content-Type", .value = "application/x-www-form-urlencoded" },
},
});
defer req.deinit();
req.transfer_encoding = .{ .content_length = form_body.len };
try req.send();
var written: usize = 0;
while (written < form_body.len) {
written += try req.write(form_body[written..]);
}
try req.finish();
try req.wait();
std.debug.print("Status: {}\n", .{req.status});
}
Função POST Reutilizável
Uma função genérica para facilitar requisições POST:
const std = @import("std");
const PostResponse = struct {
status: std.http.Status,
body: []const u8,
allocator: std.mem.Allocator,
pub fn deinit(self: *PostResponse) void {
self.allocator.free(self.body);
}
};
fn httpPost(
allocator: std.mem.Allocator,
url: []const u8,
content_type: []const u8,
payload: []const u8,
) !PostResponse {
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(.POST, uri, .{
.server_header_buffer = &header_buf,
.extra_headers = &.{
.{ .name = "Content-Type", .value = content_type },
},
});
defer req.deinit();
req.transfer_encoding = .{ .content_length = payload.len };
try req.send();
var written: usize = 0;
while (written < payload.len) {
written += try req.write(payload[written..]);
}
try req.finish();
try req.wait();
const body = try req.reader().readAllAlloc(allocator, 4 * 1024 * 1024);
return .{
.status = req.status,
.body = body,
.allocator = allocator,
};
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const json =
\\{"titulo": "Meu Post", "conteudo": "Olá mundo!"}
;
var resp = try httpPost(
allocator,
"https://httpbin.org/post",
"application/json",
json,
);
defer resp.deinit();
std.debug.print("Status: {}\n", .{resp.status});
std.debug.print("Corpo ({d} bytes)\n", .{resp.body.len});
}
Dicas e Boas Práticas
Sempre defina Content-Type: O servidor precisa saber o formato dos dados enviados.
Defina
transfer_encoding: Sempre configure ocontent_lengthantes de enviar o corpo.Trate erros de rede: Conexões podem falhar. Use tratamento de erros adequado.
Valide a resposta: Verifique sempre o código de status HTTP retornado.
Serialize corretamente: Use geração de JSON para criar payloads complexos.
Receitas Relacionadas
- Como fazer requisições HTTP GET em Zig - Requisições de leitura
- Como parsear JSON em Zig - Processar respostas JSON
- Como gerar JSON em Zig - Serializar dados para envio
- Como parsear URLs em Zig - Manipulação de URLs