Как украсить все запросы, чтобы взять значение из заголовка и добавить его в параметр body?
Фон
Я создаю RESTful services, используя Spring MVC. В настоящее время у меня есть следующая структура для контроллера:
@RestController
@RequestMapping(path = "myEntity", produces="application/json; charset=UTF-8")
public class MyEntityController {
@RequestMapping(path={ "", "/"} , method=RequestMethod.POST)
public ResponseEntity<MyEntity> createMyEntity(
@RequestBody MyEntity myEntity,
@RequestHeader("X-Client-Name") String clientName) {
myEntity.setClientName(clientName);
//rest of method declaration...
}
@RequestMapping(path={ "/{id}"} , method=RequestMethod.PUT)
public ResponseEntity<MyEntity> updateMyEntity(
@PathVariable Long id,
@RequestBody MyEntity myEntity,
@RequestHeader("X-Client-Name") String clientName) {
myEntity.setClientName(clientName);
//rest of method declaration...
}
@RequestMapping(path={ "/{id}"} , method=RequestMethod.PATCH)
public ResponseEntity<MyEntity> partialUpdateMyEntity(
@PathVariable Long id,
@RequestBody MyEntity myEntity,
@RequestHeader("X-Client-Name") String clientName) {
myEntity.setClientName(clientName);
//rest of method declaration...
}
}
Как вы можете видеть, все эти три метода получают один и тот же параметр для заголовка
@RequestHeader("X-Client-Name") String clientName и применяют его одинаково для каждого метода: myEntity.setClientName(clientName). Я создам аналогичные контроллеры и для POST, PUT и PATCH операций будут содержать почти тот же код, но для других сущностей. В настоящее время большинство организаций предназначены для поддержки этой области vía a супер класс: public class Entity {
protected String clientName;
//getters and setters ...
}
public class MyEntity extends Entity {
//...
}
Кроме того, я использую перехватчик, чтобы проверить, что заголовок установлен для запросов.
Вопрос
Как я могу избежать повторения одного и того же кода через классы и методы контроллера? Есть ли чистый способ достичь этого? Или я должен объявить переменную и повторить эти строки везде?
Этот вопрос также был задан в испанской общине. Вот ссылка .
3 ответов:
Мое предложение состоит в том, чтобы сохранить значение заголовка в бобе с областью запроса внутри перехватчика Spring или фильтра. Затем вы можете автоматически подключить этот боб, где вы хотите-сервис или контроллер и использовать сохраненное значение имени клиента.
Пример кода:
public class ClientRequestInterceptor extends HandlerInterceptorAdapter { private Entity clientEntity; public ClientRequestInterceptor(Entity clientEntity) { this.clientEntity = clientEntity; } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String clientName = request.getHeader("X-Client-Name"); clientEntity.setClientName(clientName); return true; } }В файле конфигурации:
@EnableWebMvc @Configuration public class WebConfig extends WebMvcConfigurerAdapter { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(clientRequestInterceptor()); } @Bean(name="clientEntity") @Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS) public Entity clientEntity() { return new Entity(); } @Bean public ClientRequestInterceptor clientRequestInterceptor() { return new ClientRequestInterceptor(clientEntity()); } }Тогда предположим, что мы должны использовать этот боб в нашем контроллере:
@RestController @RequestMapping(path = "myEntity", produces="application/json; charset=UTF-8") public class MyEntityController { @Autowired private Entity clientEntity; // here you have the filled bean @RequestMapping(path={ "", "/"} , method=RequestMethod.POST) public ResponseEntity<MyEntity> createMyEntity(@RequestBody MyEntity myEntity) { myEntity.setClientName(clientEntity.getClientName()); //rest of method declaration... } // rest of your class methods, without @RequestHeader parameters }Я не компилировал этот код, поэтому поправьте меня, если я сделал некоторые ошибки.
У меня есть интересный ответ на испанском сайте (где я также разместил этот вопрос), и на основе этого ответа я мог бы создать свой, который адаптируется к этой потребности. Вот мой ответ на ГП.
Основываясь на ответе@PaulVargas и идее из @jasilva (использовать наследование в контроллере), я думаю, что для этого случая есть более сильное решение. Конструкция состоит из двух частей:
Определите суперкласс для контроллеров с таким поведением. Я звоню этот класс
BaseController<E extends Entity>, потому чтоEntityявляется суперклассом почти для всех моих сущностей (объяснено в вопросе). В этом классе я получу значение параметра@RequestBody E entityи назначу его в параметр@ModelAttribute, Как объясняет @PaulVargas. Дженерики здесь очень помогают.Мои контроллеры расширят
BaseController<ProperEntity>, гдеProperEntity- правильный класс сущностей, который мне нужно обработать с этим контроллером. Тогда в методах вместо того, чтобы вводить параметры@RequestBodyи@RequestHeader, я буду вводить только параметры@ModelAttribute(при необходимости).Aquí muestro el código para el diseño descrito:
//1. public abstract class BaseController<E extends Entity> { @ModelAttribute("entity") public E populate( @RequestBody(required=false) E myEntity, @RequestHeader("X-Client-Name") String clientName) { if (myEntity != null) { myEntity.setCreatedBy(clientName); } return myEntity; } }Таким образом, мне не нужно переписывать эти строки кода в каждом методе и контроллере, добиваясь того, о чем я просил.//2. @RestController @RequestMapping(path = "myEntity", produces="application/json; charset=UTF-8") public class MyEntityController extends BaseController<MyEntity> { @RequestMapping(path={ "", "/"} , method=RequestMethod.POST) public ResponseEntity<MyEntity> createMyEntity( @ModelAttribute("entity") MyEntity myEntity) { //rest of method declaration... } @RequestMapping(path={ "/{id}"} , method=RequestMethod.PUT) public ResponseEntity<MyEntity> updateMyEntity( @PathVariable Long id, @ModelAttribute("entity") MyEntity myEntity) { //rest of method declaration... } @RequestMapping(path={ "/{id}"} , method=RequestMethod.PATCH) public ResponseEntity<MyEntity> partialUpdateMyEntity( @PathVariable Long id, @ModelAttribute("entity") MyEntity myEntity) { //rest of method declaration... } }
Вы можете рассмотреть возможность использования RequestBodyAdvice. Смотритеjavadocs . Объект HttpInputMessage, где вы можете получить доступ к заголовкам http, передается в интерфейсные методы.
Comments