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);
}
}`
現在您可以以任何方式自定義您的驗證。
添加回答
舉報