Как украсить все запросы, чтобы взять значение из заголовка и добавить его в параметр 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 {
//...
}


Кроме того, я использую перехватчик, чтобы проверить, что заголовок установлен для запросов.



Вопрос



Как я могу избежать повторения одного и того же кода через классы и методы контроллера? Есть ли чистый способ достичь этого? Или я должен объявить переменную и повторить эти строки везде?



Этот вопрос также был задан в испанской общине. Вот ссылка .
686   3  

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 (использовать наследование в контроллере), я думаю, что для этого случая есть более сильное решение. Конструкция состоит из двух частей:

  1. Определите суперкласс для контроллеров с таким поведением. Я звоню этот класс BaseController<E extends Entity>, потому что Entity является суперклассом почти для всех моих сущностей (объяснено в вопросе). В этом классе я получу значение параметра @RequestBody E entity и назначу его в параметр @ModelAttribute, Как объясняет @PaulVargas. Дженерики здесь очень помогают.

  2. Мои контроллеры расширят 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

    Ничего не найдено.