Custom validation framework
The validation framework hooks into the normal Jetty/Jersey request dispatch mechanism and is designed to allow users to
intercept the request chain to perform additional validation tasks. In its current form it is intended for intercepting
REST requests. Users can elect any validation framework they desire, such as jakarta.validation
or
the Apache Commons Validator, or they can implement one
themselves.
When to use it
This feature is intended for use cases where the standard DTO validation, that ships with EDC’s APIs is not sufficient. Please check out the OpenAPI spec to find out more about the object schema.
EDC features various data types that do not have a strict schema but are extensible, for example Asset
/AssetDto
,
or a DataRequest
/DataRequestDto
. This was done by design, to allow for maximum flexibility and openness. However,
users may still want to put a more rigid schema on top of those data types, for example a use case may require an
Asset
to always have a owner
property or may require a contentType
to be present. The standard EDC validation
scheme has no way of enforcing that, so this is where custom validation enters the playing field.
Building blocks
There are two important components necessary for custom validation:
- the
InterceptorFunction
: a function that accepts the intercepted method’s parameters as argument (asObject[]
), and returns aResult<Void>
to indicate the validation success. It must not throw an exception, or dispatch to the target resource is not guaranteed. - the
ValidationFunctionRegistry
: allInterceptorFunctions
must be registered there, using one of three registration methods (see below).
Custom validation works by supplying an InterceptorFunction
to the ValidationFunctionRegistry
in one of the
following ways:
bound to a resource-method: here, we register the
InterceptorFunction
to any of a controller’s methods. That means, we need compile-time access to the controller class, because we use reflection to obtain theMethod
:var method = YourController.class.getDeclaredMethods("theMethod", /*parameter types*/) var yourFunction = objects -> Result.success(); // you validation logic goes here registry.addFunction(method, yourFunction);
Consequently
yourFunction
will get invoked beforeYourController#theMethod
is invoked by the request dispatcher. Note that there is currently no way to bind anInterceptorFunction
directly to an HTTP endpoint.bound to an argument type: the interceptor function gets bound to all resource methods that have a particular type in their signature:
var yourFunction = objects -> Result.success(); // your validation logic goes here registry.addFunction(YourObjectDto.class, yourFunction);
The above function would therefore get invoked in all controllers on the classpath, that have a
YourObjectDto
in their signature, e.g.public void createObject(YourObjectDto dto)
andpublic boolean deleteObject (YourObjectDto dto)
would both get intercepted, even if they are defined in different controller classes. This is the recommended way in the situation described above - adding additional schema restrictions on extensible typesglobally, for all resource methods: this is intended for interceptor functions that should get invoked on all resource methods. This is generally not recommended and should only be used in very specific situations such as logging
Please check out this test for a comprehensive example how validation can be enabled. All functions are registered during the extension’s initialization phase.
Limitations and caveats
InterceptorFunction
objects must not throw exceptions- all function registration must happen during the
initialize
phase of the extension lifecycle. - interceptor functions should not perform time-consuming tasks, such as invoking other backend systems, so as not to cause timeouts in the request chain
- for method-based interception compile-time access to the resource is required. This might not be suitable for a lot of situations.
- returning a
Result.failure(...)
will result in anHTTP 400 BAD REQUEST
status code. This is the only supported status code at this time. Note that the failure message will be part of the HTTP response body. - binding methods directly to paths (“endpoints”) is not supported.
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.