Skip to main content

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 Pointer
  • regex - 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 characters
  • binary - Hexadecimal encoded octets
  • binary-spaced - Space-separated hexadecimal octets
  • password - Password (UI hint only, no validation)

Format Validation Examples​

Example - Email Validation
@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
}
Example - UUID Validation
@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"
}
Example - URI Validation
@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")
}
Example - Date/Time Validation
@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) and validateOutput() (for serialized objects)
  • Error Handling: Validation failures throw SchemaValidationException with 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, and password are 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:

ConstraintMapping
@NotNullrequired=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
@Emailformat="email"
@Positiveminimum=0, exclusiveMinimum=true
@PositiveOrZerominimum=0
@Negativemaximum=0, exclusiveMaximum=true
@NegativeOrZeromaximum=0
@NotEmptyrequired=true, minLength=1, minItems=1
@NotBlankrequired=true, minLength=1, pattern=".*\\S.*"
@DecimalMin(value, inclusive)minimum=value with optional exclusiveMinimum
@DecimalMax(value, inclusive)maximum=value with optional exclusiveMaximum

Jakarta Validation Examples​

Example - Basic Validation
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
}
Example - Decimal Constraints
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;
}
Example - Combining Juneau and Jakarta Annotations
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​

  1. Automatic Detection: Juneau uses reflection to detect annotations in the jakarta.validation.constraints package
  2. No Direct Dependency: The main Juneau modules do not depend on jakarta.validation-api, making it optional
  3. Schema Mapping: Jakarta Validation constraints are automatically translated to OpenAPI schema properties
  4. Validation Integration: Constraints are applied during HTTP part parsing and validation
  5. OpenAPI Documentation: Constraints appear in generated OpenAPI/Swagger documentation

Validation on REST Parameters​

Jakarta Validation annotations can be applied directly to REST method parameters:

Example - Parameter-Level Validation
@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:

Example - Remote Interface with Validation
@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:

Example - 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:

Example - Custom Error Handling
@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:

Example - Testing Validation
@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​

Discussion

Share feedback or follow-up questions for this page directly through GitHub.