猿问

我可以强制要求在 Spring 中至少包含两个标头之一吗?

我的一个标题拼写错误,我想在向后兼容的同时更改它。

@RequestHeader(value = "Custmer-Key") String customerKey

我想添加一个拼写正确的标题Customer-Key,并至少强制其中一个。有任何想法吗?


蝴蝶不菲
浏览 139回答 3
3回答

慕的地8271018

我会在这里做一些假设。在您的具体情况下,每一个都可能正确,也可能不正确,但目的是提供更好的背景信息,说明此类解决方案何时可行并且使用起来有意义。你需要保持向后兼容性(这个很简单......你写的)您有一个相当大的代码库,可能基于微服务并由多个开发人员维护,并且您希望避免跨越多个团队的大量提交,将修复集中在一个所有服务都打算使用的公共共享库中您的标头不仅使用 Spring 获取,有时还通过直接访问请求来获取你在一个生产应用程序中工作,你希望尽可能少地更改代码,因为它的一些内部工作很难理解该解决方案包括连接自定义过滤器及其配置。过滤器会将HttpServletRequest实例与另一个允许操作标头的实例交换。首先,创建自己的过滤器,如下所示:import org.springframework.context.annotation.Configuration;import org.springframework.core.Ordered;import org.springframework.core.annotation.Order;import javax.servlet.*;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletRequestWrapper;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.util.*;@Configuration@Order(Ordered.HIGHEST_PRECEDENCE)public class HeadersFilter implements Filter {&nbsp; &nbsp; private static final String WRONG_HEADER = "Custmer-Key";&nbsp; &nbsp; private static final String RIGHT_HEADER = "Customer-Key";&nbsp; &nbsp; @Override&nbsp; &nbsp; public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {&nbsp; &nbsp; &nbsp; &nbsp; HttpServletRequest request = (HttpServletRequest) servletRequest;&nbsp; &nbsp; &nbsp; &nbsp; HttpServletResponse response = (HttpServletResponse) servletResponse;&nbsp; &nbsp; &nbsp; &nbsp; String newHeaderValue = request.getHeader(RIGHT_HEADER);&nbsp; &nbsp; &nbsp; &nbsp; String headerValue;&nbsp; &nbsp; &nbsp; &nbsp; if(newHeaderValue != null) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; headerValue = newHeaderValue;&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; else {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; headerValue = request.getHeader(WRONG_HEADER);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; HeadersRewriteHttpServletRequestWrapper requestWrapper = new HeadersRewriteHttpServletRequestWrapper(request);&nbsp; &nbsp; &nbsp; &nbsp; requestWrapper.setCustomHeader(WRONG_HEADER, headerValue);&nbsp; &nbsp; &nbsp; &nbsp; filterChain.doFilter(requestWrapper, response);&nbsp; &nbsp; }&nbsp; &nbsp; public static class HeadersRewriteHttpServletRequestWrapper extends HttpServletRequestWrapper {&nbsp; &nbsp; &nbsp; &nbsp; private Map<String, String> customHeaders;&nbsp; &nbsp; &nbsp; &nbsp; HeadersRewriteHttpServletRequestWrapper(HttpServletRequest request) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; super(request);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; customHeaders = new HashMap<>();&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; void setCustomHeader(String name, String value) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; customHeaders.put(name, value);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; private String getCustomHeader(String name) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return customHeaders.get(name);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; @Override&nbsp; &nbsp; &nbsp; &nbsp; public String getHeader(String name) { // not needed by spring but useful if someone uses this method directly&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; String header = super.getHeader(name);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if(header != null) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return header;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return getCustomHeader(name);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; @Override&nbsp; &nbsp; &nbsp; &nbsp; public Enumeration<String> getHeaderNames() {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Set<String> names = new HashSet<>(Collections.list(super.getHeaderNames()));&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; names.addAll(customHeaders.keySet());&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return Collections.enumeration(names);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; @Override&nbsp; &nbsp; &nbsp; &nbsp; public Enumeration<String> getHeaders(String name) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; List<String> headers = Collections.list(super.getHeaders(name));&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; String customHeader = getCustomHeader(name);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if(headers.isEmpty() && customHeader != null) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; headers.add(customHeader);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return Collections.enumeration(headers);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}其次,连接 Spring 配置以创建此过滤器的实例并在必要时注入它。import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configurationpublic class FilterConfiguration {&nbsp; &nbsp; @Bean&nbsp; &nbsp; public HeadersFilter headersFilterBean() {&nbsp; &nbsp; &nbsp; &nbsp; return new HeadersFilter();&nbsp; &nbsp; }}就是这样。Customer-Key假设您的应用程序没有阻止它工作的怪癖(在这种情况下祝您调试顺利),此代码将采用和的内容,优先考虑Custmer-Key并将Customer-Key它们写入假Custmer-Key标头中。这样你就不必触摸任何控制器,它们应该继续透明地工作。

人到中年有点甜

下一个方法是创建一个注释 OneOf 或其他东西。我使用了一种比使用 Aspect 更简单的方法。使用这种方法,您可以验证请求参数、Requestbody 和 RequestHeader@Target({TYPE, ANNOTATION_TYPE})@Retention(RUNTIME)@Constraint(validatedBy = OneOfValidator.class)@Documentedpublic @interface OneOf {&nbsp; &nbsp; String message() default "";&nbsp; &nbsp; String[] value();}创建如下所示的验证器类。public class OneOfValidator implements ConstraintValidator<OneOf, Object> {&nbsp; &nbsp; private String[] fields;&nbsp; &nbsp; private String fieldList;&nbsp; &nbsp; public void initialize(OneOf annotation) {&nbsp; &nbsp; &nbsp; &nbsp; this.fields = annotation.value();&nbsp; &nbsp; &nbsp; &nbsp; fieldList = Arrays.toString(fields);&nbsp; &nbsp; }&nbsp; &nbsp; public boolean isValid(Object value, ConstraintValidatorContext context) {&nbsp; &nbsp; &nbsp; &nbsp; BeanWrapper wrapper = PropertyAccessorFactory.forBeanPropertyAccess(value);&nbsp; &nbsp; &nbsp; &nbsp; int matches = countNumberOfMatches(wrapper);&nbsp; &nbsp; &nbsp; &nbsp; if (matches > 1) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; setErrorMessage(context, <your message>);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return false;&nbsp; &nbsp; &nbsp; &nbsp; } else if (matches == 0) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; setErrorMessage(context, <your message>);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return false;&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; return true;&nbsp; &nbsp; }&nbsp; &nbsp; private int countNumberOfMatches(BeanWrapper wrapper) {&nbsp; &nbsp; &nbsp; &nbsp; int matches = 0;&nbsp; &nbsp; &nbsp; &nbsp; for (String field : fields) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Object value = wrapper.getPropertyValue(field);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; boolean isPresent = detectOptionalValue(value);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (value != null && isPresent) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; matches++;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; return matches;&nbsp; &nbsp; }&nbsp; &nbsp; private boolean detectOptionalValue(Object value) {&nbsp; &nbsp; &nbsp; &nbsp; if (value instanceof Optional) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return ((Optional)value).isPresent();&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; if (value instanceof String) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return StringUtils.hasText((String)value);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; return true;&nbsp; &nbsp; }&nbsp; &nbsp; private void setErrorMessage(ConstraintValidatorContext context, String template) {&nbsp; &nbsp; &nbsp; &nbsp; context.disableDefaultConstraintViolation();&nbsp; &nbsp; &nbsp; &nbsp; context&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .buildConstraintViolationWithTemplate(template)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .addNode(fieldList)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .addConstraintViolation();&nbsp; &nbsp; }在控制器中,您可以创建如下所示的内容。&nbsp; &nbsp; @GetMapping(value = "your path")&nbsp; &nbsp; public ResponseEntity<HeaderDataDTO> getBuildDetails(@RequestHeader(value = "Custmer-Key") String custmerKey,@RequestHeader(value = "Customer-Key") String customerKey&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ) {&nbsp; &nbsp; &nbsp; &nbsp; HeaderDataDTO data = new HeaderDataDTO();data.setCustomerKey(customerKey);data.setCustmerKey(custmerKey);data.validate();&nbsp; &nbsp; &nbsp; &nbsp; return new ResponseEntity<>(data,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; HttpStatus.OK);&nbsp; &nbsp; }您可以如下定义 DTO。@Valid@OneOf(value = {"customerKey", "custmerKey"})public class HeaderDataDTO extends HeaderValidator {&nbsp; &nbsp; private String customerKey;&nbsp; &nbsp; private String custmerKey;//getter and setterHeaderValidator 应该如下所示。Validate 方法将验证对象。import org.springframework.util.CollectionUtils;import javax.validation.ConstraintViolation;import javax.validation.Valid;import javax.validation.Validation;import javax.validation.Validator;public abstract class HeaderValidator {&nbsp; &nbsp; public boolean validate() {&nbsp; &nbsp; &nbsp; &nbsp; Validator validator = Validation&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .buildDefaultValidatorFactory()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .getValidator();&nbsp; &nbsp; &nbsp; &nbsp; Set<ConstraintViolation<HeaderValidator>> violations = validator.validate(this);&nbsp; &nbsp; &nbsp; &nbsp; if (!CollectionUtils.isEmpty(violations)) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; throw <your exception>&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; return true;&nbsp; &nbsp; }

小唯快跑啊

您可以像下面这样创建一个拦截器。@Component@Primarypublic class HeadersInterceptor extends HandlerInterceptorAdapter {&nbsp; &nbsp; public boolean preHandle(HttpServletRequest request,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; HttpServletResponse response,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Object handler) throws Exception {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; HttpInputMessage inputMessage=new ServletServerHttpRequest(request);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;HttpHeaders httpHeaders = inputMessage.getHeaders();//validation code for header goes here.//return true if validation is successful&nbsp; &nbsp;&nbsp;return true;&nbsp; &nbsp; }&nbsp;}并将拦截器添加到您的配置中。&nbsp;@Configurationpublic class InterceptorConfig implements WebMvcConfigurer {&nbsp; &nbsp; @Autowired&nbsp; &nbsp; HeadersInterceptor headersInterceptor;&nbsp; &nbsp; public void addInterceptors(InterceptorRegistry registry) {&nbsp; &nbsp; &nbsp; &nbsp; registry.addInterceptor(headersInterceptor);&nbsp; &nbsp; }}`现在您可以以任何方式自定义您的验证。
随时随地看视频慕课网APP

相关分类

Java
我要回答