Return HTTP 状态 BAD_REQUEST 在 Spring 中使用自定义解析器
Return HTTP Status BAD_REQUEST using a custom resolver in Spring
我正在使用 Spring Boot 1.5.15 开发 REST API。我实现了一个客户 HandlerMethodArgumentResolver
来映射一个 HTTP header。具体来说,我给HTTP赋值headerSome-Header
,去掉前缀"XXX ".
首先,我定义了一个自定义注解。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface SomeHeader {
}
然后,我实现了一个自定义解析器。
public class SomeHeaderArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterAnnotation(SomeHeader.class) != null;
}
@Override
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) {
HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
final String headerValue = request.getHeader("Some-Header");
return headerValue.replace("XXX ", "");
}
}
最后,我让 Spring 知道配置 class 中的解析器。
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new SomeHeaderArgumentResolver());
}
}
现在,我可以在我需要的任何控制器中使用以下映射。
@PostMapping("/some/paath")
public void someMethod(@SomeHeader String someHeaderValue) {
// Method body...
}
但是,Some-Header
信息对我来说是强制性的。如果它不存在,我希望 Spring returns 对调用者的 400 Bad Request 响应。这与我使用 @RequestHeader("Some-Header")
注释可以获得的行为相同。
我可以复制相同的行为吗?可能,我不想使用专用的 controller advice.
您可以为任何情况声明自己的异常并将控制器中的 ExceptionHandler 设置为 return 正确 http-status。
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler({SameHeaderException.class})
public Object onSameHeaderException(SameHeaderException e) {
return ImmutableMap.of("reason", e.getMessage());
}
所以如果 header 不存在,你可以抛出这个异常:
if (someHeaderValue == null) { throw new SameHeaderException(); }
如果您看到用于 @RequestHeader
的 RequestHeaderMethodArgumentResolver
的实现,您将看到 AbstractNamedValueMethodArgumentResolver
抽象 [=33] 的 handleMissingValue
方法的重写实现=] 如下:
@Override
protected void handleMissingValue(String name, MethodParameter parameter) throws ServletRequestBindingException {
throw new ServletRequestBindingException("Missing request header '" + name +
"' for method parameter of type " + parameter.getNestedParameterType().getSimpleName());
}
此handleMissingValue
方法用于AbstractNamedValueMethodArgumentResolver
的resolveArgument
方法,RequestHeaderMethodArgumentResolver
根据某些条件对其进行扩展。因此,当 header 不存在并且抛出 ServletRequestBindingException
时,Spring 的 DefaultHandlerExceptionResolver
会处理并发送 400 响应.
这就是 @RequestHeader
情况下验证的工作方式。因此,您可以在 SomeHeaderArgumentResolver
class 的 resolveArgument
方法中实现类似的内容,如下所示:
public class SomeHeaderArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterAnnotation(SomeHeader.class) != null;
}
@Override
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) {
HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
final String headerValue = request.getHeader("Some-Header");
if(headerValue != null){
return headerValue.replace("XXX ", "");
} else {
//handle scenario if header absent with ServletRequestBindingException
}
}
}
感谢@madhu-bhat 的建议,我明白了 class 扩展的正确方法是让 Spring 发挥作用。
class RequestHeaderMethodArgumentResolver
,被 Spring 用于解析 Java object 中的 HTTP headers 值,扩展了抽象 class AbstractNamedValueMethodArgumentResolver
。 class 允许您指定 header 值是否有一些默认值,使用 createNamedValueInfo
方法。
所以,它遵循代码。
public class SomeHeaderArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
@Override
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
// The second parameter specifies if the value is required,
// and the third if there is some default value.
return new NamedValueInfo("", true, null);
}
@Override
protected Object resolveName(String name,
MethodParameter parameter,
NativeWebRequest request) {
final String headerValue = request.getHeader("Some-Value");
if (StringUtils.isEmpty(headerValue)) {
// Returning null tells Spring that there is no value for the parameter
return null;
}
return headerValue.replace("XXX ", "");
}
@Override
public boolean supportsParameter(MethodParameter parameter) {
return (parameter.hasParameterAnnotation(SomeHeader.class) &&
!Map.class.isAssignableFrom(
parameter.nestedIfOptional().getNestedParameterType()));
}
}
我唯一不喜欢的是我正在使用为处理命名值而开发的结构,但我没有命名值.
希望对您有所帮助。
我正在使用 Spring Boot 1.5.15 开发 REST API。我实现了一个客户 HandlerMethodArgumentResolver
来映射一个 HTTP header。具体来说,我给HTTP赋值headerSome-Header
,去掉前缀"XXX ".
首先,我定义了一个自定义注解。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface SomeHeader {
}
然后,我实现了一个自定义解析器。
public class SomeHeaderArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterAnnotation(SomeHeader.class) != null;
}
@Override
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) {
HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
final String headerValue = request.getHeader("Some-Header");
return headerValue.replace("XXX ", "");
}
}
最后,我让 Spring 知道配置 class 中的解析器。
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(new SomeHeaderArgumentResolver());
}
}
现在,我可以在我需要的任何控制器中使用以下映射。
@PostMapping("/some/paath")
public void someMethod(@SomeHeader String someHeaderValue) {
// Method body...
}
但是,Some-Header
信息对我来说是强制性的。如果它不存在,我希望 Spring returns 对调用者的 400 Bad Request 响应。这与我使用 @RequestHeader("Some-Header")
注释可以获得的行为相同。
我可以复制相同的行为吗?可能,我不想使用专用的 controller advice.
您可以为任何情况声明自己的异常并将控制器中的 ExceptionHandler 设置为 return 正确 http-status。
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler({SameHeaderException.class})
public Object onSameHeaderException(SameHeaderException e) {
return ImmutableMap.of("reason", e.getMessage());
}
所以如果 header 不存在,你可以抛出这个异常:
if (someHeaderValue == null) { throw new SameHeaderException(); }
如果您看到用于 @RequestHeader
的 RequestHeaderMethodArgumentResolver
的实现,您将看到 AbstractNamedValueMethodArgumentResolver
抽象 [=33] 的 handleMissingValue
方法的重写实现=] 如下:
@Override
protected void handleMissingValue(String name, MethodParameter parameter) throws ServletRequestBindingException {
throw new ServletRequestBindingException("Missing request header '" + name +
"' for method parameter of type " + parameter.getNestedParameterType().getSimpleName());
}
此handleMissingValue
方法用于AbstractNamedValueMethodArgumentResolver
的resolveArgument
方法,RequestHeaderMethodArgumentResolver
根据某些条件对其进行扩展。因此,当 header 不存在并且抛出 ServletRequestBindingException
时,Spring 的 DefaultHandlerExceptionResolver
会处理并发送 400 响应.
这就是 @RequestHeader
情况下验证的工作方式。因此,您可以在 SomeHeaderArgumentResolver
class 的 resolveArgument
方法中实现类似的内容,如下所示:
public class SomeHeaderArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterAnnotation(SomeHeader.class) != null;
}
@Override
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) {
HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
final String headerValue = request.getHeader("Some-Header");
if(headerValue != null){
return headerValue.replace("XXX ", "");
} else {
//handle scenario if header absent with ServletRequestBindingException
}
}
}
感谢@madhu-bhat 的建议,我明白了 class 扩展的正确方法是让 Spring 发挥作用。
class RequestHeaderMethodArgumentResolver
,被 Spring 用于解析 Java object 中的 HTTP headers 值,扩展了抽象 class AbstractNamedValueMethodArgumentResolver
。 class 允许您指定 header 值是否有一些默认值,使用 createNamedValueInfo
方法。
所以,它遵循代码。
public class SomeHeaderArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
@Override
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
// The second parameter specifies if the value is required,
// and the third if there is some default value.
return new NamedValueInfo("", true, null);
}
@Override
protected Object resolveName(String name,
MethodParameter parameter,
NativeWebRequest request) {
final String headerValue = request.getHeader("Some-Value");
if (StringUtils.isEmpty(headerValue)) {
// Returning null tells Spring that there is no value for the parameter
return null;
}
return headerValue.replace("XXX ", "");
}
@Override
public boolean supportsParameter(MethodParameter parameter) {
return (parameter.hasParameterAnnotation(SomeHeader.class) &&
!Map.class.isAssignableFrom(
parameter.nestedIfOptional().getNestedParameterType()));
}
}
我唯一不喜欢的是我正在使用为处理命名值而开发的结构,但我没有命名值.
希望对您有所帮助。