#java #jakarta-ee #jax-rs #resteasy #wildfly-18
#java #джакарта-ee #jax-rs #resteasy #wildfly-18
Вопрос:
У меня есть генератор кода, который генерирует интерфейсы для конечных точек JAX-RS, и мое серверное приложение реализует эти интерфейсы для обеспечения бизнес-логики.
Проблема сейчас в том, что я не могу использовать фильтры контейнеров с привязкой к имени для улучшения бизнес-логики или для повышения безопасности: любая @NameBinding
аннотация маркера в реализующем классе или его методах игнорируется, и соответствующий фильтр не вызывается.
Вот минимальный пример: (код находится на Kotlin, но проблема та же, когда реализована на чистой Java)
// generated
data class FooDto(val filtered: Boolean)
// generated
@Path("/")
interface OpenApiGeneratedInterface {
@GET
@Path("/foo/bar")
@Produces("application/json")
fun foo(): FooDto
}
// my implementation
class ImplementingApiController : OpenApiGeneratedInterface {
@TestMarker
override fun foo() = FooDto(filtered = false)
}
// may come from external dependency
@NameBinding
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
annotation class TestMarker
// may come from external dependency
@Provider
@TestMarker
class TestFilter: ContainerResponseFilter {
override fun filter(
requestContext: ContainerRequestContext,
responseContext: ContainerResponseContext,
) {
responseContext.entity = FooDto(filtered = true)
}
}
Когда делается запрос на /foo/bar
, я получаю {"filtered":false}
, поэтому фильтр не запущен.
Когда я перемещаю @TestMarker
аннотацию из ImplementingApiController::foo
в OpenApiGeneratedInterface::foo
, я вместо этого получаю {"filtered":true}
, так что на этот раз фильтр действительно запустился. Обратите внимание, что изменение интерфейса в действительности невозможно, поскольку генерируются реальные интерфейсы. Я сделал это только в примере, чтобы показать, что фильтр работает в целом.
Проблема, по-видимому, заключается в том, что система ищет аннотации маркеров только в интерфейсах и никогда в реализующих классах.
Вот полная картина; Я контролирую:
- Класс
ImplementingApiController
- Система, в которой запущено приложение (чтобы я мог изменить конфигурацию или добавить дополнительные фильтры / перехватчики)
У меня нет или почти нет контроля над:
OpenApiGeneratedInterface
Интерфейс (генерируется из спецификации OpenAPI)- Классы DTO, такие как
FooDto
(также сгенерированные) - Генератор кода, который создает эти интерфейсы (это удаленный проект)
@TestMarker
Аннотация и соответствующий ей фильтр (взяты из еще одного проекта)
Это оставляет мне мало места для маневра, чтобы заставить это работать.
Возможно ли это вообще в этом созвездии, и если да, то как это будет работать?
Что я пробовал до сих пор:
- Добавьте
@Path
@Provider
аннотацию илиImplementingApiController
, чтобы заставить систему использовать этот класс для обнаружения аннотаций (не сработало) - Добавьте
javax.ws.rs.container.DynamicFeature
и подключите фильтры, выполнив поиск реализаций интерфейса с помощью отражения (может сработать, но это будет очень некрасиво, когда интерфейс и реализация не управляются одним и тем же загрузчиком классов) - Добавьте мой собственный
ContainerResponseFilter
, который всегда активен, и вызывайте фактические фильтры динамически (также требуется то же безумие отражения, что и с aDynamicFeature
)
Дополнительные идеи:
- Измените генератор кода, чтобы исключить аннотации JAX-RS в интерфейсе и аннотировать все самостоятельно (работает, но почти полностью теряет смысл)
- Измените генератор кода, чтобы включить все виды аннотаций маркеров, которые мне нужны (тогда я столкнусь с проблемами циклических зависимостей при создании сгенерированного кода)
Комментарии:
1. Если я правильно помню, у меня была похожая проблема. Чтобы убедить JAX-RS читать аннотации из моего класса реализации, мне пришлось поместить
@Path
в реализацию и исключить ее из интерфейса. Если сделать это подобным образом, JAX-RS объединит все остальные аннотации из моего класса интерфейс и даст мне то, что я хочу. Если это правильно, вы могли бы выбрать вариант внесения минимальных изменений в генератор.2. Спасибо за ответ, Никос. К сожалению, это не работает: (
3. на самом деле, это меняет ситуацию тонким способом, который я могу обнаружить с помощью a
DynamicFeature
: — когда@Path("/")
только в интерфейсе, обнаруживается только интерфейс, а полный класс реализации игнорируется — когда@Path("/")
только в классе реализации, обнаруживается класс реализации, но только аннотации наинтерфейс все еще используется. В настоящее время я экспериментирую с aDynamicFeature
, который исправил бы это, выполнив поиск аннотаций маркеров вручную… это может сработать!4. Оглядываясь назад на спецификацию JAX-RS v2.1, глава 3.6, она описывает именно это поведение. В нем, среди прочего, говорится: » аннотации наследуются […] при условии, что метод [подкласса] и его параметры не имеют собственных аннотаций JAX-RS», «Если у подкласса или метода реализации есть какие-либо аннотации JAX-RS , тогда все аннотации суперкласса илиметоды интерфейса игнорируются » и » рекомендуется всегда повторять аннотации, а не полагаться на наследование аннотаций «.
5. У меня почти получилось. Я смог написать
DynamicFeature
, который правильно определяет все случаи наследования, когда дополнительные интерфейсы маркеров будут игнорироваться, и пытается зарегистрировать соответствующие фильтры. суть кода , но система не позволяет мне зарегистрировать фильтр, который уже зарегистрирован автоматически@Provider
. Мне нужно как-то включить фильтр вместо его регистрации…