ether-http-core define los contratos del pipeline HTTP de Ether: HttpExchange, HttpHandler, Middleware, HttpResource y Route. Es independiente del servidor HTTP concreto.
Instalación
<dependency>
<groupId>dev.rafex.ether.http.core</groupId>
<artifactId>ether-http-core</artifactId>
<version>8.0.0-SNAPSHOT</version>
</dependency>
HttpResource — handler por verbo HTTP
Implementa HttpResource para exponer un recurso REST:
public class UsersResource implements HttpResource {
private final UserService userService;
private final JsonCodec codec;
public UsersResource(UserService userService, JsonCodec codec) {
this.userService = userService;
this.codec = codec;
}
@Override
public boolean get(HttpExchange x) {
List<User> users = userService.findAll();
x.json(200, users);
return true;
}
@Override
public boolean post(HttpExchange x) {
CreateUserRequest req = codec.readValue(x.bodyBytes(), CreateUserRequest.class);
User created = userService.create(req);
x.json(201, created);
return true;
}
@Override
public Set<String> supportedMethods() {
return Set.of("GET", "POST");
}
}
HttpExchange — contexto de la petición
String method = exchange.method();
String path = exchange.path();
String userId = exchange.pathParam("id");
Map<String, String> allPath = exchange.pathParams();
String sort = exchange.queryFirst("sort");
List<String> tags = exchange.queryAll("tag");
Map<String, List<String>> all = exchange.queryParams();
exchange.json(200, body);
exchange.text(200, "OK");
exchange.noContent(204);
exchange.methodNotAllowed();
exchange.options();
Route — patrón de URL
Route r1 = Route.of("/users", Set.of("GET", "POST"));
Route r2 = Route.of("/users/{id}", Set.of("GET", "PUT", "DELETE"));
boolean ok = r2.allows("GET");
Optional<Map<String, String>> params = r2.match("/users/42");
Middleware — composición de interceptores
Middleware logging = next -> exchange -> {
LOG.info("{} {}", exchange.method(), exchange.path());
boolean handled = next.handle(exchange);
LOG.info("→ completado");
return handled;
};
Middleware auth = next -> exchange -> {
String token = exchange.headerFirst("Authorization");
if (token == null) { exchange.noContent(401); return true; }
return next.handle(exchange);
};
HttpHandler pipeline = auth.wrap(logging.wrap(resource::handle));
QuerySpec y RsqlParser — filtrado y paginación
QuerySpec spec = QuerySpec.fromRequest(exchange);
String rsql = spec.filter();
Sort sort = spec.sort();
int page = spec.page();
int size = spec.size();
RsqlNode tree = RsqlParser.parse(rsql);
AuthPolicy — política de autenticación/autorización
public class JwtAuthPolicy implements AuthPolicy {
private final TokenVerifier verifier;
@Override
public boolean authenticate(HttpExchange exchange) {
String header = exchange.headerFirst("Authorization");
if (header == null || !header.startsWith("Bearer ")) return false;
String token = header.substring(7);
VerificationResult result = verifier.verify(token, Instant.now());
if (!result.isValid()) return false;
exchange.attribute("claims", result.claims());
return true;
}
@Override
public boolean authorize(HttpExchange exchange, String... requiredRoles) {
TokenClaims claims = (TokenClaims) exchange.attribute("claims");
return claims.roles().containsAll(List.of(requiredRoles));
}
}
Más información