HTTP Part Validation
HTTP parts can be automatically validated against their schema definitions using format validation and Jakarta Bean Validation constraints.
Format Validation
Juneau supports comprehensive format validation for HTTP parts based on JSON Schema Draft 2020-12 and OpenAPI 3.x specifications. When a format is specified in a @Schema annotation, the value is automatically validated against that format.
Supported Formats
The following format types are supported:
Email Formats
email- Email address (RFC 5321)idn-email- Internationalized email address (RFC 6531)
Hostname Formats
hostname- Internet host name (RFC 1123)idn-hostname- Internationalized host name (RFC 5890)
IP Address Formats
ipv4- IPv4 address (RFC 2673)ipv6- IPv6 address (RFC 4291)
URI/IRI Formats
uri- Universal Resource Identifier (RFC 3986)uri-reference- URI Reference (RFC 3986)iri- Internationalized Resource Identifier (RFC 3987)iri-reference- IRI Reference (RFC 3987)
Other Formats
uuid- Universally Unique Identifier (RFC 4122)uri-template- URI Template (RFC 6570)json-pointer- JSON Pointer (RFC 6901)relative-json-pointer- Relative JSON Pointerregex- Regular expression (ECMA-262)date- Full date (RFC 3339)date-time- Date and time (RFC 3339)date-time-zone- Date and time with time zone (RFC 3339)time- Time (RFC 3339)duration- Duration (RFC 3339 Appendix A / ISO 8601)
Transformation Formats
byte- BASE-64 encoded charactersbinary- Hexadecimal encoded octetsbinary-spaced- Space-separated hexadecimal octetspassword- Password (UI hint only, no validation)
Format Validation Examples
@RestPost("/users")
public User createUser(
@Query(name="email", schema=@Schema(format="email"))
String email
) {
// Email is automatically validated as a valid email address
// Invalid emails will throw SchemaValidationException
}
@RestGet("/users/{id}")
public User getUser(
@Path(name="id", schema=@Schema(format="uuid"))
String id
) {
// ID is validated as a valid UUID format
// e.g., "550e8400-e29b-41d4-a716-446655440000"
}
@RestPost("/links")
public void addLink(
@FormData(name="url", schema=@Schema(format="uri", required=true))
String url
) {
// URL is validated as a valid URI
// Must include scheme (e.g., "https://example.com")
}
@RestGet("/appointments")
public List<Appointment> getAppointments(
@Query(name="startDate", schema=@Schema(format="date"))
String startDate,
@Query(name="endDate", schema=@Schema(format="date"))
String endDate
) {
// Dates are validated as RFC 3339 full-date (YYYY-MM-DD)
}
Validation Behavior
- Validation Points: Format validation occurs in both
validateInput()(for incoming string values) andvalidateOutput()(for serialized objects) - Error Handling: Validation failures throw
SchemaValidationExceptionwith detailed error messages - Null Handling: The literal string
"null"is treated as a valid value for all formats - Relaxed Patterns: Date and time formats use relaxed patterns to accommodate various serialization formats
- Transformation Formats: Formats like
byte,binary, andpasswordare treated as transformation hints rather than validation constraints
Jakarta Bean Validation Integration
Juneau automatically detects and processes Jakarta Bean Validation constraints without requiring a direct dependency on jakarta.validation-api. This allows you to use standard validation annotations alongside Juneau's @Schema annotation.
Supported Constraints
The following Jakarta Bean Validation constraints are automatically mapped to OpenAPI schema properties:
| Constraint | Mapping |
|---|---|
@NotNull | required=true |
@Size(min=X, max=Y) | minLength=X, maxLength=Y, minItems=X, maxItems=Y |
@Min(value) | minimum=value |
@Max(value) | maximum=value |
@Pattern(regexp) | pattern=regexp |
@Email | format="email" |
@Positive | minimum=0, exclusiveMinimum=true |
@PositiveOrZero | minimum=0 |
@Negative | maximum=0, exclusiveMaximum=true |
@NegativeOrZero | maximum=0 |
@NotEmpty | required=true, minLength=1, minItems=1 |
@NotBlank | required=true, minLength=1, pattern=".*\\S.*" |
@DecimalMin(value, inclusive) | minimum=value with optional exclusiveMinimum |
@DecimalMax(value, inclusive) | maximum=value with optional exclusiveMaximum |
Jakarta Validation Examples
import jakarta.validation.constraints.*;
public class CreateUserRequest {
@NotNull
@Email
@Size(max=255)
private String email;
@NotBlank
@Size(min=8, max=100)
@Pattern(regexp="^(?=.*[A-Z])(?=.*[a-z])(?=.*\\d).+$")
private String password;
@Positive
@Max(150)
private Integer age;
}
@RestPost("/users")
public User createUser(@Content CreateUserRequest request) {
// Jakarta Validation constraints are automatically applied
// Invalid requests throw SchemaValidationException
}
import jakarta.validation.constraints.*;
import java.math.BigDecimal;
public class ProductRequest {
@NotBlank
@Size(min=1, max=200)
private String name;
@NotNull
@DecimalMin(value="0.01", inclusive=true)
@DecimalMax(value="999999.99", inclusive=true)
private BigDecimal price;
@PositiveOrZero
private Integer stock;
}
public class SearchRequest {
@NotBlank
@Pattern(regexp="^[a-zA-Z0-9 ]+$")
@Size(min=3, max=100)
@Schema(description="Search query", example="Juneau framework")
private String query;
@PositiveOrZero
@Max(1000)
@Schema(description="Maximum results", _default="10")
private Integer limit;
@NotNull
@Pattern(regexp="^(asc|desc)$")
@Schema(description="Sort order", _enum={"asc", "desc"})
private String order;
}
How It Works
- Automatic Detection: Juneau uses reflection to detect annotations in the
jakarta.validation.constraintspackage - No Direct Dependency: The main Juneau modules do not depend on
jakarta.validation-api, making it optional - Schema Mapping: Jakarta Validation constraints are automatically translated to OpenAPI schema properties
- Validation Integration: Constraints are applied during HTTP part parsing and validation
- OpenAPI Documentation: Constraints appear in generated OpenAPI/Swagger documentation
Validation on REST Parameters
Jakarta Validation annotations can be applied directly to REST method parameters:
@RestPost("/transfer")
public Response transfer(
@FormData(name="amount")
@NotNull
@DecimalMin("0.01")
@DecimalMax("10000.00")
BigDecimal amount,
@FormData(name="fromAccount")
@NotBlank
@Pattern(regexp="^[0-9]{10}$")
String fromAccount,
@FormData(name="toAccount")
@NotBlank
@Pattern(regexp="^[0-9]{10}$")
String toAccount
) {
// Validation is automatically applied before method execution
}
Validation with Remote Proxies
Jakarta Validation annotations also work seamlessly with REST client remote proxies:
@Remote(path="/api/users")
public interface UserService {
@RemotePost("/create")
User createUser(
@FormData(name="email")
@NotNull
@Email
String email,
@FormData(name="username")
@NotBlank
@Size(min=3, max=50)
@Pattern(regexp="^[a-zA-Z0-9_]+$")
String username,
@FormData(name="age")
@Positive
@Max(150)
Integer age
);
}
// Usage - validation occurs automatically
RestClient client = RestClient.create().build();
UserService service = client.getRemote(UserService.class);
// This will throw SchemaValidationException if email is invalid
service.createUser("invalid-email", "user123", 25);
Best Practices
Combining Validation Approaches
You can combine format validation, Jakarta Validation, and custom @Schema constraints for comprehensive validation:
@RestPost("/products")
public Product createProduct(
@Content
@NotNull
Product product
) {...}
public class Product {
@NotBlank
@Size(min=1, max=200)
@Schema(description="Product name", example="Coffee Maker")
private String name;
@NotNull
@DecimalMin("0.01")
@DecimalMax("999999.99")
@Schema(description="Product price in USD", format="decimal")
private BigDecimal price;
@Email
@Schema(description="Contact email", format="email")
private String contactEmail;
@Pattern(regexp="^https?://.*")
@Schema(description="Product website", format="uri")
private String website;
@Size(max=36)
@Pattern(regexp="^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$")
@Schema(description="Product SKU", format="uuid")
private String sku;
}
Error Handling
Validation errors throw SchemaValidationException which can be caught and handled:
@RestPost("/users")
public Response createUser(@Content CreateUserRequest request) {
try {
// Validation happens automatically during parameter parsing
User user = userService.create(request);
return Response.ok(user);
} catch (SchemaValidationException e) {
return Response
.status(400)
.entity(new ErrorResponse("Validation failed: " + e.getMessage()))
.build();
}
}
Testing with Validation
When writing tests, validation can be bypassed or verified:
@Test
public void testEmailValidation() {
RestClient client = MockRestClient
.create(MyResource.class)
.build();
// This should fail validation
assertThrows(SchemaValidationException.class, () -> {
client.post("/users")
.query("email", "invalid-email")
.complete();
});
// This should pass validation
client.post("/users")
.query("email", "valid@example.com")
.complete()
.assertStatus(200);
}
See Also
- HttpPartSchema - Schema validation API
- HttpPartFormat - Format enumeration
- @Schema - Schema annotation
- SchemaValidationException - Validation exception
- HTTP Part Annotations - Overview of HTTP part annotations
Share feedback or follow-up questions for this page directly through GitHub.