我有一组用 Java 编写的 Rest 控制器。最初的要求是为某些端点添加一些标头,但是我们决定在几乎所有 Rest 控制器中添加这些标头字段。现在该项目包含 100 多个类似的 API:
@GetMapping("/products/{comp}")
public ResponseEntity<List<Product>> getAllProducts(
@RequestHeader(USER_HDR) String user,
@RequestHeader(GUID_HDR) String guid,
@RequestHeader(value = CALLER_HDR, required = false) String caller,
@RequestHeader(value = LANG_HDR, required = false) String language,
@PathVariable @NotNull Integer comp
) {
return ResponseEntity.ok(service.get(comp, ProductUtils.processHeaders(user,guid,caller,language)));
}
@GetMapping("/products")
public ResponseEntity<List<Product>> getAllProducts(
@RequestHeader(USER_HDR) String user,
@RequestHeader(GUID_HDR) String guid,
@RequestHeader(value = CALLER_HDR, required = false) String caller,
@RequestHeader(value = LANG_HDR, required = false) String language
) {
return ResponseEntity.ok(service.getAllRecords(ProductUtils.processHeaders(user,guid,caller,language)));
}
从代码中可以明显看出,元组用户、guid、调用者、语言在源代码中无处不在,但是,如何重构并将代码放在“一个地方”或尝试使其更易于维护,这不是那么明显吗?例如,如果我们需要添加第 5 个参数,则需要使用 100 个 API。
在 Java Spring boot 中执行此操作的规范方法是什么?
理想情况下,我想要这样的东西:
@GetMapping("/products/{comp}")
public ResponseEntity<List<Product>> getAllProducts(
"common handling"
@PathVariable @NotNull Integer comp
) {
return ResponseEntity.ok(service.get(comp, ProductUtils.processHeaders(user,guid,caller,language)));
}
@GetMapping("/products")
public ResponseEntity<List<Product>> getAllProducts(
"common handling"
) {
return ResponseEntity.ok(service.getAllRecords(ProductUtils.processHeaders(user,guid,caller,language)));
}
有什么想法吗?控制器建议?还有别的吗?
为了解决代码重复问题并使代码在 Spring Boot 应用程序中更易于维护,您可以创建一个自定义过滤器来在公共标头到达控制器之前提取和处理它们。另外,您可以将头参数封装成一个对象,以增强代码的可读性和可维护性。
总而言之,通过创建
RequestHeaders
DTO、实现 CustomHeaderFilter
并将其注册到 FilterRegistrationBean
来集中标头处理,以在 Spring Boot 控制器中统一应用通用标头。
这是建议的方法:
创建标头DTO(数据传输对象):
定义一个表示公共头参数的类。此类将保存从标头中提取的值。
public class RequestHeaders {
private String user;
private String guid;
private String caller;
private String language;
// getters and setters
}
创建过滤器:
实现过滤器来拦截传入请求并提取公共标头,然后将它们存储在请求属性中。
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class CustomHeaderFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
RequestHeaders headers = new RequestHeaders();
headers.setUser(request.getHeader("user"));
headers.setGuid(request.getHeader("guid"));
headers.setCaller(request.getHeader("caller"));
headers.setLanguage(request.getHeader("language"));
request.setAttribute("requestHeaders", headers);
filterChain.doFilter(request, response);
}
}
使用FilterRegistrationBean注册过滤器:
在主应用程序类中使用
FilterRegistrationBean
注册自定义过滤器。
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class WebConfig {
@Bean
public FilterRegistrationBean<CustomHeaderFilter> customHeaderFilter() {
FilterRegistrationBean<CustomHeaderFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new CustomHeaderFilter());
registrationBean.addUrlPatterns("/api/*"); // Adjust the URL pattern as needed
return registrationBean;
}
}
自定义
addUrlPatterns
方法以匹配您想要应用过滤器的 URL。
修改控制器以使用 DTO:
修改控制器以使用
RequestHeaders
DTO 而不是单独的标头参数。
@GetMapping("/products/{comp}")
public ResponseEntity<List<Product>> getAllProducts(@PathVariable @NotNull Integer comp,
@ModelAttribute("requestHeaders") RequestHeaders headers) {
return ResponseEntity.ok(service.get(comp, headers));
}
@GetMapping("/products")
public ResponseEntity<List<Product>> getAllProducts(@ModelAttribute("requestHeaders") RequestHeaders headers) {
return ResponseEntity.ok(service.getAllRecords(headers));
}
现在,如果需要添加新的标头参数或进行更改,只需更新
RequestHeaders
类和过滤器逻辑。这种方法集中了标头处理并增强了可维护性。
您可以使用以下方法使控制器的接口独立于标头数量:
@GetMapping("/products/{comp}")
public ResponseEntity<List<Product>> getAllProducts(
@RequestHeader Map<String, String> headers,
@PathVariable @NotNull Integer comp
) {
MyHeaders headers = MyHeaders(headers);
return ResponseEntity.ok(service.get(comp, ProductUtils.processHeaders(headers.getUser,headers.getGuid,headers.getCaller,headers.getLanguage)));
}
您需要将 MyHeaders 类实现为共享组件。