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

2022-01-17 00:00:00 http-headers rest spring java spring-mvc

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

I'm creating RESTful services using Spring MVC. Currently, I have the following structure for a controller:

@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 操作将包含几乎相同的代码,但用于其他实体.目前,大多数实体都设计为通过超类支持该字段:

As you can see, all these three methods receive the same parameter for the header @RequestHeader("X-Client-Name") String clientName and applies it in the same way on each method: myEntity.setClientName(clientName). I will create similar controllers and for POST, PUT and PATCH operations will contain almost the same code but for other entities. Currently, most entities are designed to support this field vía a super class:

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

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

Also, I use an interceptor to verify that the header is set for requests.

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

How can I avoid repeating the same code through controller classes and methods? Is there a clean way to achieve it? Or should I declare the variable and repeat those lines everywhere?

西班牙社区也有人问过这个问题.这是链接.

This question was also asked in the Spanish community. Here's the link.

推荐答案

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

I've got an interesting answer in the Spanish site (where I also posted this question) and based on that answer I could generate mine that adapts to this need. Here's my answer on SOes.

基于 @PaulVargas's answer 和来自@jasilva 的一个想法(在控制器中使用继承)我认为这种情况下更强的解决方案.设计由两部分组成:

Based on @PaulVargas's answer and an idea from @jasilva (use inheritance in controller) I though on a stronger solution for this case. The design consists of two parts:

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

  1. Define a super class for controllers with this behavior. I call this class BaseController<E extends Entity> because Entity is the super class for almost al my entities (explained in the question). In this class I'll retrieve the value of @RequestBody E entity parameter and assign it into a @ModelAttribute parameter like @PaulVargas explains. Generics power helps a lot here.

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

My controllers will extend BaseController<ProperEntity> where ProperEntity is the proper entity class I need to handle with that controller. Then, in the methods, instead of injecting @RequestBody and @RequestHeader parameters, I'll only inject the the @ModelAttribute (if needed).

Aquí muestro el código para el diseño 描述:

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...
    }    
}

这样,我不需要在每个方法和控制器中重写那些代码行,实现我的要求.

In this way, I don't need to rewrite those lines of code in every method and controller, achieving what I've asked.

相关文章