3 回答
TA贡献1796条经验 获得超4个赞
我会在这里做一些假设。在您的具体情况下,每一个都可能正确,也可能不正确,但目的是提供更好的背景信息,说明此类解决方案何时可行并且使用起来有意义。
你需要保持向后兼容性(这个很简单......你写的)
您有一个相当大的代码库,可能基于微服务并由多个开发人员维护,并且您希望避免跨越多个团队的大量提交,将修复集中在一个所有服务都打算使用的公共共享库中
您的标头不仅使用 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 {
private static final String WRONG_HEADER = "Custmer-Key";
private static final String RIGHT_HEADER = "Customer-Key";
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String newHeaderValue = request.getHeader(RIGHT_HEADER);
String headerValue;
if(newHeaderValue != null) {
headerValue = newHeaderValue;
}
else {
headerValue = request.getHeader(WRONG_HEADER);
}
HeadersRewriteHttpServletRequestWrapper requestWrapper = new HeadersRewriteHttpServletRequestWrapper(request);
requestWrapper.setCustomHeader(WRONG_HEADER, headerValue);
filterChain.doFilter(requestWrapper, response);
}
public static class HeadersRewriteHttpServletRequestWrapper extends HttpServletRequestWrapper {
private Map<String, String> customHeaders;
HeadersRewriteHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
customHeaders = new HashMap<>();
}
void setCustomHeader(String name, String value) {
customHeaders.put(name, value);
}
private String getCustomHeader(String name) {
return customHeaders.get(name);
}
@Override
public String getHeader(String name) { // not needed by spring but useful if someone uses this method directly
String header = super.getHeader(name);
if(header != null) {
return header;
}
return getCustomHeader(name);
}
@Override
public Enumeration<String> getHeaderNames() {
Set<String> names = new HashSet<>(Collections.list(super.getHeaderNames()));
names.addAll(customHeaders.keySet());
return Collections.enumeration(names);
}
@Override
public Enumeration<String> getHeaders(String name) {
List<String> headers = Collections.list(super.getHeaders(name));
String customHeader = getCustomHeader(name);
if(headers.isEmpty() && customHeader != null) {
headers.add(customHeader);
}
return Collections.enumeration(headers);
}
}
}
其次,连接 Spring 配置以创建此过滤器的实例并在必要时注入它。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FilterConfiguration {
@Bean
public HeadersFilter headersFilterBean() {
return new HeadersFilter();
}
}
就是这样。Customer-Key假设您的应用程序没有阻止它工作的怪癖(在这种情况下祝您调试顺利),此代码将采用和的内容,优先考虑Custmer-Key并将Customer-Key它们写入假Custmer-Key标头中。这样你就不必触摸任何控制器,它们应该继续透明地工作。
TA贡献1895条经验 获得超7个赞
下一个方法是创建一个注释 OneOf 或其他东西。我使用了一种比使用 Aspect 更简单的方法。使用这种方法,您可以验证请求参数、Requestbody 和 RequestHeader
@Target({TYPE, ANNOTATION_TYPE})
@Retention(RUNTIME)
@Constraint(validatedBy = OneOfValidator.class)
@Documented
public @interface OneOf {
String message() default "";
String[] value();
}
创建如下所示的验证器类。
public class OneOfValidator implements ConstraintValidator<OneOf, Object> {
private String[] fields;
private String fieldList;
public void initialize(OneOf annotation) {
this.fields = annotation.value();
fieldList = Arrays.toString(fields);
}
public boolean isValid(Object value, ConstraintValidatorContext context) {
BeanWrapper wrapper = PropertyAccessorFactory.forBeanPropertyAccess(value);
int matches = countNumberOfMatches(wrapper);
if (matches > 1) {
setErrorMessage(context, <your message>);
return false;
} else if (matches == 0) {
setErrorMessage(context, <your message>);
return false;
}
return true;
}
private int countNumberOfMatches(BeanWrapper wrapper) {
int matches = 0;
for (String field : fields) {
Object value = wrapper.getPropertyValue(field);
boolean isPresent = detectOptionalValue(value);
if (value != null && isPresent) {
matches++;
}
}
return matches;
}
private boolean detectOptionalValue(Object value) {
if (value instanceof Optional) {
return ((Optional)value).isPresent();
}
if (value instanceof String) {
return StringUtils.hasText((String)value);
}
return true;
}
private void setErrorMessage(ConstraintValidatorContext context, String template) {
context.disableDefaultConstraintViolation();
context
.buildConstraintViolationWithTemplate(template)
.addNode(fieldList)
.addConstraintViolation();
}
在控制器中,您可以创建如下所示的内容。
@GetMapping(value = "your path")
public ResponseEntity<HeaderDataDTO> getBuildDetails(@RequestHeader(value = "Custmer-Key") String custmerKey,@RequestHeader(value = "Customer-Key") String customerKey
) {
HeaderDataDTO data = new HeaderDataDTO();
data.setCustomerKey(customerKey);
data.setCustmerKey(custmerKey);
data.validate();
return new ResponseEntity<>(data,
HttpStatus.OK);
}
您可以如下定义 DTO。
@Valid
@OneOf(value = {"customerKey", "custmerKey"})
public class HeaderDataDTO extends HeaderValidator {
private String customerKey;
private String custmerKey;
//getter and setter
HeaderValidator 应该如下所示。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 {
public boolean validate() {
Validator validator = Validation
.buildDefaultValidatorFactory()
.getValidator();
Set<ConstraintViolation<HeaderValidator>> violations = validator.validate(this);
if (!CollectionUtils.isEmpty(violations)) {
throw <your exception>
}
return true;
}
TA贡献1863条经验 获得超2个赞
您可以像下面这样创建一个拦截器。
@Component
@Primary
public class HeadersInterceptor extends HandlerInterceptorAdapter {
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
HttpInputMessage inputMessage=new ServletServerHttpRequest(request);
HttpHeaders httpHeaders = inputMessage.getHeaders();
//validation code for header goes here.
//return true if validation is successful
return true;
}
}
并将拦截器添加到您的配置中。
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Autowired
HeadersInterceptor headersInterceptor;
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(headersInterceptor);
}
}`
现在您可以以任何方式自定义您的验证。
添加回答
举报