如何装饰所有请求从标头中获取值并将其添加到body参数?

背景

我正在使用Spring MVC创建RESTful服务。 目前,我有一个控制器的结构如下:

@RestController @RequestMapping(path = "myEntity", produces="application/json; charset=UTF-8") public class MyEntityController { @RequestMapping(path={ "", "/"} , method=RequestMethod.POST) public ResponseEntity 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 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 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操作将包含几乎相同的代码,但对于其他实体。 目前,大多数实体都是为了支持这个领域而设计的超级类:

 public class Entity { protected String clientName; //getters and setters ... } public class MyEntity extends Entity { //... } 

此外,我使用拦截器来validation是否为请求设置了标头。

如何通过控制器类和方法避免重复相同的代码? 有没有一个干净的方法来实现它? 或者我应该声明变量并在任何地方重复这些行?

西class牙社区也提出了这个问题。 这是链接 。

我的建议是将标头值存储在Spring拦截器或filter内的请求范围bean中。 然后,您可以在任何地方自动assembly此bean – 服务或控制器,并使用存储的客户端名称值。

代码示例:

 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()); } } 

然后,假设我们必须在我们的控制器中使用这个bean:

 @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 createMyEntity(@RequestBody MyEntity myEntity) { myEntity.setClientName(clientEntity.getClientName()); //rest of method declaration... } // rest of your class methods, without @RequestHeader parameters } 

我没有编译这段代码,所以如果我犯了一些错误,请纠正我。

我在西class牙语网站(我也发布了这个问题)中得到了一个有趣的答案,根据这个答案我可以生成适应这种需求的我的答案。 这是我对SOes的回答 。


基于@PaulVargas的回答和来自@jasilva的想法(在控制器中使用inheritance)我虽然对这个案例有更强的解决方案。 设计包括两部分:

  1. 使用此行为为控制器定义超类。 我将此类BaseController因为Entity几乎是我的实体的超类(在问题中解释)。 在这个类中,我将检索@RequestBody E entity参数的值,并将其分配给@PaodeVargas解释的@ModelAttribute参数。 仿制药在这方面有很大帮助。

  2. 我的控制器将扩展BaseController ,其中ProperEntity是我需要使用该控制器处理的正确实体类。 然后,在方法中,我只会注入@ModelAttribute (如果需要),而不是注入@RequestBody@RequestHeader参数。

Aquímuestroelcódigaparaeldiseñosdescrito:

 //1. public abstract class BaseController { @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 { @RequestMapping(path={ "", "/"} , method=RequestMethod.POST) public ResponseEntity createMyEntity( @ModelAttribute("entity") MyEntity myEntity) { //rest of method declaration... } @RequestMapping(path={ "/{id}"} , method=RequestMethod.PUT) public ResponseEntity updateMyEntity( @PathVariable Long id, @ModelAttribute("entity") MyEntity myEntity) { //rest of method declaration... } @RequestMapping(path={ "/{id}"} , method=RequestMethod.PATCH) public ResponseEntity partialUpdateMyEntity( @PathVariable Long id, @ModelAttribute("entity") MyEntity myEntity) { //rest of method declaration... } } 

通过这种方式,我不需要在每个方法和控制器中重写这些代码行,从而实现我所要求的。

您可以考虑使用RequestBodyAdvice。 查看javadocs 。 您可以访问http标头的HttpInputMessage对象被传递到接口方法中。