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

Lui*_*oza 8 java rest spring spring-mvc http-headers

背景

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

@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...
    }
}
Run Code Online (Sandbox Code Playgroud)

如您所见,所有这三个方法都接收标题的相同参数,@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 {
    //...
}
Run Code Online (Sandbox Code Playgroud)

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

题

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

西班牙社区也提出了这个问题.这是链接.

Tho*_*ski 10

我的建议是将标头值存储在 Spring 拦截器或过滤器内的请求作用域 bean 中。然后你可以在任何你想要的地方自动装配这个 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

在您的配置文件中:

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

}
Run Code Online (Sandbox Code Playgroud)

然后,假设我们必须在控制器中使用这个 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<MyEntity> createMyEntity(@RequestBody MyEntity myEntity) {
        myEntity.setClientName(clientEntity.getClientName());
        //rest of method declaration...
    }
    // rest of your class methods, without @RequestHeader parameters

}
Run Code Online (Sandbox Code Playgroud)

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

  • WebMvcConfigurerAdapter 现已弃用,请改用 WebMvcConfigurer。 (2认同)

Lui*_*oza 2

我在西班牙语网站上得到了一个有趣的答案(我也在那里发布了这个问题),并且基于该答案我可以生成适应这种需求的答案。这是我对 SOes 的回答。

\n\n
\n\n

基于@PaulVargas\ 的回答和 @jasilva 的想法(在控制器中使用继承),我认为针对这种情况有一个更强大的解决方案。该设计由两部分组成:

\n\n
    \n
  1. 为具有此行为的控制器定义一个超类。我称这个类是BaseController<E extends Entity>因为它Entity是几乎所有我的实体的超类(在问题中解释)。在此类中,我将检索@RequestBody E entity参数的值并将其分配给@ModelAttribute@PaulVargas 所解释的参数。泛型的力量在这里很有帮助。

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

  4. \n
\n\n

Aqu\xc3\xad muestro el c\xc3\xb3digo para el dise\xc3\xb1o 描述:

\n\n
//1.\npublic abstract class BaseController<E extends Entity> {\n\n    @ModelAttribute("entity")\n    public E populate(\n            @RequestBody(required=false) E myEntity,\n            @RequestHeader("X-Client-Name") String clientName) {\n        if (myEntity != null) {\n            myEntity.setCreatedBy(clientName);\n        }\n        return myEntity;\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n\n\n
//2.\n@RestController\n@RequestMapping(path = "myEntity", produces="application/json; charset=UTF-8")\npublic class MyEntityController extends BaseController<MyEntity> {\n\n    @RequestMapping(path={ "", "/"} , method=RequestMethod.POST)\n    public ResponseEntity<MyEntity> createMyEntity(\n        @ModelAttribute("entity") MyEntity myEntity) {\n        //rest of method declaration...\n    }\n\n    @RequestMapping(path={ "/{id}"} , method=RequestMethod.PUT)\n    public ResponseEntity<MyEntity> updateMyEntity(\n        @PathVariable Long id,\n        @ModelAttribute("entity") MyEntity myEntity) {\n        //rest of method declaration...\n    }\n\n    @RequestMapping(path={ "/{id}"} , method=RequestMethod.PATCH)\n    public ResponseEntity<MyEntity> partialUpdateMyEntity(\n        @PathVariable Long id,\n        @ModelAttribute("entity") MyEntity myEntity) {\n        //rest of method declaration...\n    }    \n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

这样,我不需要在每个方法和控制器中重写这些代码行,就可以实现我所要求的。

\n