如何装饰所有请求从header中取值并添加到body参数中?
How to decorate all requests to take a value from header and add it in the body parameter?
背景
我正在使用 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...
}
}
如您所见,所有这三种方法都为 header @RequestHeader("X-Client-Name") String clientName
接收相同的参数,并以相同的方式将其应用于每个方法:myEntity.setClientName(clientName)
。我将创建类似的控制器,对于 POST,PUT 和 PATCH 操作将包含几乎相同的代码,但用于其他实体。目前,大多数实体都设计为通过超级 class:
支持此字段
public class Entity {
protected String clientName;
//getters and setters ...
}
public class MyEntity extends Entity {
//...
}
此外,我使用拦截器来验证是否为请求设置了 header。
问题
如何避免通过控制器 classes 和方法重复相同的代码?有没有一种干净的方法来实现它?还是我应该声明变量并在所有地方重复这些行?
西班牙社区也有人问过这个问题。这里是 the link.
我的建议是将 header 值存储在 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;
}
}
在您的配置文件中:
@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<MyEntity> createMyEntity(@RequestBody MyEntity myEntity) {
myEntity.setClientName(clientEntity.getClientName());
//rest of method declaration...
}
// rest of your class methods, without @RequestHeader parameters
}
我没有编译这段代码,如果有错误请指正。
我在西班牙网站上得到了一个有趣的答案(我也在那里发布了这个问题),基于这个答案,我可以生成适合这个需求的答案。这里是 my answer on SOes.
基于 @PaulVargas's answer 和@jasilva 的想法(在控制器中使用继承),我虽然针对这种情况提出了更强大的解决方案。设计由两部分组成:
为具有此行为的控制器定义一个超级 class。我称之为 class BaseController<E extends Entity>
因为 Entity
是几乎所有我的实体的超级 class (在问题中解释)。在此 class 中,我将检索 @RequestBody E entity
参数的值并将其分配给 @ModelAttribute
参数,如@PaulVargas 解释的那样。泛型的力量在这里有很大帮助。
我的控制器将扩展 BaseController<ProperEntity>
,其中 ProperEntity
是正确的实体 class 我需要处理该控制器。然后,在方法中,我不会注入 @RequestBody
和 @RequestHeader
参数,而是只注入 @ModelAttribute
(如果需要)。
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...
}
}
这样,我就不需要在每个方法和控制器中重写那些代码行,实现我所要求的。
您可以考虑使用RequestBodyAdvice。见javadocs。
您可以访问 http headers 的 HttpInputMessage object 被传递到接口方法中。
背景
我正在使用 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...
}
}
如您所见,所有这三种方法都为 header @RequestHeader("X-Client-Name") String clientName
接收相同的参数,并以相同的方式将其应用于每个方法:myEntity.setClientName(clientName)
。我将创建类似的控制器,对于 POST,PUT 和 PATCH 操作将包含几乎相同的代码,但用于其他实体。目前,大多数实体都设计为通过超级 class:
public class Entity {
protected String clientName;
//getters and setters ...
}
public class MyEntity extends Entity {
//...
}
此外,我使用拦截器来验证是否为请求设置了 header。
问题
如何避免通过控制器 classes 和方法重复相同的代码?有没有一种干净的方法来实现它?还是我应该声明变量并在所有地方重复这些行?
西班牙社区也有人问过这个问题。这里是 the link.
我的建议是将 header 值存储在 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;
}
}
在您的配置文件中:
@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<MyEntity> createMyEntity(@RequestBody MyEntity myEntity) {
myEntity.setClientName(clientEntity.getClientName());
//rest of method declaration...
}
// rest of your class methods, without @RequestHeader parameters
}
我没有编译这段代码,如果有错误请指正。
我在西班牙网站上得到了一个有趣的答案(我也在那里发布了这个问题),基于这个答案,我可以生成适合这个需求的答案。这里是 my answer on SOes.
基于 @PaulVargas's answer 和@jasilva 的想法(在控制器中使用继承),我虽然针对这种情况提出了更强大的解决方案。设计由两部分组成:
为具有此行为的控制器定义一个超级 class。我称之为 class
BaseController<E extends Entity>
因为Entity
是几乎所有我的实体的超级 class (在问题中解释)。在此 class 中,我将检索@RequestBody E entity
参数的值并将其分配给@ModelAttribute
参数,如@PaulVargas 解释的那样。泛型的力量在这里有很大帮助。我的控制器将扩展
BaseController<ProperEntity>
,其中ProperEntity
是正确的实体 class 我需要处理该控制器。然后,在方法中,我不会注入@RequestBody
和@RequestHeader
参数,而是只注入@ModelAttribute
(如果需要)。
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...
}
}
这样,我就不需要在每个方法和控制器中重写那些代码行,实现我所要求的。
您可以考虑使用RequestBodyAdvice。见javadocs。 您可以访问 http headers 的 HttpInputMessage object 被传递到接口方法中。