Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
204 views
in Technique[技术] by (71.8m points)

java - How to decorate all requests to take a value from header and add it in the body parameter?

Background

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

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.

Question

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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

My suggestion is to store the header value in the request scoped bean inside the Spring interceptor or filter. Then you may autowire this bean wherever you want - service or controller and use the stored client name value.

Code example:

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

In your configuration file:

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

}

Then, lets assume we have to use this bean in our controller:

@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

}

I have not compiled this code, so correct me if I made some mistakes.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...