tangguo

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

java

背景
我正在使用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);
        //resto de declaración del método...
    }

    @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);
        //resto de declaración del método...
    }

    @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);
        //resto de declaración del método...
    }
}

可以看出,这三种方法对于标头是相同的参数,@RequestHeader("X-Client-Name") String clientName并且在每种方法中都采用相同的方式:myEntity.setClientName(clientName)。我将创建类似的控制器,对于POST,PUT和PATCH操作,它们将包含类似的代码,但面向其他实体。当前,大多数实体旨在通过父类支持此字段:

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

注意,我使用拦截器来验证是否已根据请求设置标头。


如何避免在控制器和方法中重复相同的代码?有没有一种干净的方法可以实现这一目标,而无需代码重复?还是我注定要在将来的控制器的所有方法中重复参数和同一行代码?


阅读 338

收藏
2020-11-20

共2个答案

小编典典

可以使用class删除一些重复的代码org.springframework.web.bind.annotation.ModelAttribute。也就是说:

@RestController
@RequestMapping(path = "myEntity", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public class MyEntityController {

    @ModelAttribute("model")
    public MyEntity populate(
            @RequestBody MyEntity myEntity,
            @RequestHeader("X-Client-Name") String clientName) {
        myEntity.setClientName(clientName);
        return myEntity;
    }

    @RequestMapping(path = {"", "/"}, method = RequestMethod.POST)
    public ResponseEntity<MyEntity> createMyEntity(
            @ModelAttribute("model") MyEntity myEntity) {
        ⋮
    }

    @RequestMapping(path = {"/{id}"}, method = RequestMethod.PUT)
    public ResponseEntity<MyEntity> updateMyEntity(
            @PathVariable Long id,
            @ModelAttribute("model") MyEntity myEntity) {
        ⋮
    }

    @RequestMapping(path = {"/{id}"}, method = RequestMethod.PATCH)
    public ResponseEntity<MyEntity> partialUpdateMyEntity(
            @PathVariable Long id,
            @ModelAttribute("model") MyEntity myEntity) {
        ⋮
    }

}

该方法populate将在每次请求到该控制器调用与注释的方法之前被调用@RequestMapping。

由于始终在每个请求上执行此方法,因此即使对于那些不使用此类数据的方法,也需要有效负载和标头(”X-Client-Name”),并且如果它们未在请求中传播,则将引发异常。您可以使用属性修改该要求。也就是说:required = false

    @ModelAttribute("model")
    public MyEntity populate(
            @RequestBody(required = false) MyEntity myEntity,
            @RequestHeader(required = false, value = "X-Client-Name") String clientName) {
        if (myEntity != null) {
            myEntity.setClientName(clientName);
        }
        return myEntity;
    }
2020-11-20
小编典典

基于@PaulVargas的答案和@jasilva的想法(使用继承),我能够针对自己的需求提出更强大的解决方案。简而言之,它分为两个部分:

  1. 对控制器使用基类。我将其称为“BaseController<E extends Entity>帖子”,这Entity是我实体的主要课程。在这个课堂上我将捕捉参数的值@RequestBody E entity,并@ModelAttribute在为他的答案解释通过@PaulVargas分配给它。泛型的力量进入这一部分。

  2. 在我的控制器,我将延长BaseController<ProperEntity>,其中ProperEntity的实体类,我需要的过程是如此的注入是自动的。

在这里,我显示了所描述设计的代码:

//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) {
        //resto de declaración del método...
    }

    @RequestMapping(path={ "/{id}"} , method=RequestMethod.PUT)
    public ResponseEntity<MyEntity> updateMyEntity(
        @PathVariable Long id,
        @ModelAttribute("entity") MyEntity myEntity) {
        //resto de declaración del método...
    }

    @RequestMapping(path={ "/{id}"} , method=RequestMethod.PATCH)
    public ResponseEntity<MyEntity> partialUpdateMyEntity(
        @PathVariable Long id,
        @ModelAttribute("entity") MyEntity myEntity) {
        //resto de declaración del método...
    }    
}
2020-11-20