JAX-RS Bean 验证的自定义异常映射
Custom exception mapping for JAX-RS Bean validation
我正在使用 Apache CXF、Spring 和 JAX-RS 构建 REST Web 服务,我需要在输入 JSON 验证失败时发送自定义异常。
我没有使用业务逻辑,而是尝试使用 CXF、JAX-RS 的开箱即用的 bean 验证功能。
Bean 验证工作正常,但是,它总是抛出 500 异常,这不太有用。根据文档,这是 org.apache.cxf.jaxrs.validation.ValidationExceptionMapper 的预期行为。
我正在尝试扩展异常映射器以便抛出不同的错误消息,但未调用此自定义映射器。
下面是我的代码设置 ->
pom.xml
<!-- Spring Framework -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>3.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>3.2.7.RELEASE</version>
</dependency>
<!-- CXF -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-core</artifactId>
<version>3.0.2</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxrs</artifactId>
<version>3.0.2</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-bundle-jaxrs</artifactId>
<version>2.7.5</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-jaxrs</artifactId>
<version>1.9.3</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-xc</artifactId>
<version>1.9.3</version>
</dependency>
<!-- Hibernate validation -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.1.3.Final</version>
</dependency>
<!-- JSR 303 Validation framework -->
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-bean-validation</artifactId>
<version>2.19</version>
</dependency>
JAX-RS 服务器
<jaxrs:server address="/" id="iamService">
<jaxrs:serviceBeans>
<bean class="com.XXXXXX.iam.service.core.endpoint.impl.IAMIdentityServiceImpl" />
</jaxrs:serviceBeans>
<jaxrs:inInterceptors>
<ref bean="validationInInterceptor" />
</jaxrs:inInterceptors>
<jaxrs:outInterceptors>
<ref bean="validationOutInterceptor" />
<ref bean="exceptionMapperOutInterceptor" />
</jaxrs:outInterceptors>
<jaxrs:extensionMappings>
<entry key="json" value="application/json" />
</jaxrs:extensionMappings>
<jaxrs:providers>
<ref bean='jsonProvider' />
<ref bean="ampfExceptionMapper"/>
</jaxrs:providers>
</jaxrs:server>
<bean id="ampfExceptionMapper" class="com.xxxxxxx.iam.service.core.interceptor.AmpfValidationExceptionMapper"/>
<bean id="exceptionMapperOutInterceptor" class="org.apache.cxf.jaxrs.interceptor.JAXRSOutExceptionMapperInterceptor"/>
<bean id="validationProvider" class="org.apache.cxf.validation.BeanValidationProvider" />
<bean id="validationInInterceptor" class="org.apache.cxf.jaxrs.validation.JAXRSBeanValidationInInterceptor">
<property name="provider" ref="validationProvider" />
</bean>
<bean id="validationOutInterceptor" class="org.apache.cxf.jaxrs.validation.JAXRSBeanValidationOutInterceptor">
<property name="provider" ref="validationProvider" />
</bean>
自定义异常映射器
@Provider
@Produces(MediaType.APPLICATION_JSON)
public class AmpfValidationExceptionMapper implements ExceptionMapper<ConstraintViolationException>{
@Override
public Response toResponse(ConstraintViolationException exception) {
System.out.println("Executing Ameriprise Service exception mapping");
UniqueIDValidationResponse response = new UniqueIDValidationResponse();
response.setResponse("Invalid request");
return Response.ok(response).build();
}
}
豆子class
public class UniqueIDValidationRequest {
/*@Autowired
private Validator validator;*/
@XmlAttribute(name = "ssoId")
@NotBlank
@NotEmpty
@Length(max=20,min=5)
protected String ssoId;
@NotEmpty
@NotBlank
@XmlAttribute(name = "epHashId")
protected String epHashId;
此问题现已解决。我必须做的是创建一个自定义拦截器。这将捕获 bean 验证时发生的错误并添加实际的 bean 验证错误。请检查下面的 InInterceptor 代码 -->
@Provider
public class IamServInInterceptor extends
BeanValidationInInterceptor implements ContainerRequestFilter{
private static Logger logger = LoggerFactory.getLogger(IamServInInterceptor.class);
public IamServInInterceptor() {
logger.debug("IamServInInterceptor default consturctor called");
}
public IamServInInterceptor(String phase) {
super(phase);
logger.debug("IamServInInterceptor phase : " + phase);
}
@Override
public void filter(ContainerRequestContext arg0) throws IOException {
// TODO Auto-generated method stub
logger.debug("IamServInInterceptor filter is called");
InterceptorChain chain = PhaseInterceptorChain.getCurrentMessage()
.getInterceptorChain();
chain.add(this);
}
protected Object getServiceObject(Message message) {
logger.debug("IamServInInterceptor getServiceObject called");
return ValidationUtils.getResourceInstance(message);
}
protected void handleValidation(Message message, Object resourceInstance,
Method method, List<Object> arguments){
try {
logger.debug("IamServInInterceptor handleValidation called");
super.handleValidation(message, resourceInstance, method, arguments);
} catch (ConstraintViolationException ex) {
logger.debug("IamServInInterceptor handleValidation has some exception");
UniqueIDValidationResponse response = new UniqueIDValidationResponse();
response.setResponse("INPUT_VALIDATION_FAILED");
ConstraintViolationException constraintException = (ConstraintViolationException)ex;
Set<ConstraintViolation<?>> constraint=
constraintException.getConstraintViolations();
Iterator<ConstraintViolation<?>> iterator = constraint.iterator();
while(iterator.hasNext()){
ConstraintViolation<?> error = iterator.next();
logger.debug("Error : " + error.toString());
response.getMessage().add(error.getMessage());
}
ex.printStackTrace();
message.getExchange().put(Response.class, Response.ok(response).build());
}
}
}
我用异常映射器解决了:
import org.apache.cxf.validation.ResponseConstraintViolationException;
import org.slf4j.LoggerFactory;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
import java.util.Iterator;
/**
* Created by Hyun Woo Son on 1/9/18
**/
@Provider
public class ValidationExceptionMapperCustom implements ExceptionMapper<ValidationException> {
private static org.slf4j.Logger logger = LoggerFactory.getLogger(ValidationExceptionMapperCustom.class);
public Response toResponse(ValidationException exception) {
Response.Status errorStatus = Response.Status.INTERNAL_SERVER_ERROR;
ResponseVo responseVo = new ResponseVo();
if (exception instanceof ConstraintViolationException) {
ConstraintViolationException constraint = (ConstraintViolationException)exception;
Iterator i$ = constraint.getConstraintViolations().iterator();
StringBuilder errorMsg = new StringBuilder(RestExcepcionEnum.PARAMETROS_EXCEPCION.getValue());
errorMsg.append("\n");
while(i$.hasNext()) {
ConstraintViolation<?> violation = (ConstraintViolation)i$.next();
logger.info("error {}",violation.getRootBeanClass().getSimpleName() + "." + violation.getPropertyPath() + ": " + violation.getMessage());
errorMsg.append("La propiedad : "+violation.getPropertyPath() + " con error: " + violation.getMessage() );
errorMsg.append("\n");
}
if (!(constraint instanceof ResponseConstraintViolationException)) {
errorStatus = Response.Status.BAD_REQUEST;
}
responseVo.setMensaje(errorMsg.toString());
}
else{
responseVo.setMensaje("Error inesperado: "+exception.getMessage());
logger.debug("Error de validacion {}",exception.getMessage(),exception);
}
return Response.status(errorStatus).entity(responseVo).build();
}
}
并将其添加到应用程序 class:
我正在使用 Apache CXF、Spring 和 JAX-RS 构建 REST Web 服务,我需要在输入 JSON 验证失败时发送自定义异常。
我没有使用业务逻辑,而是尝试使用 CXF、JAX-RS 的开箱即用的 bean 验证功能。
Bean 验证工作正常,但是,它总是抛出 500 异常,这不太有用。根据文档,这是 org.apache.cxf.jaxrs.validation.ValidationExceptionMapper 的预期行为。
我正在尝试扩展异常映射器以便抛出不同的错误消息,但未调用此自定义映射器。
下面是我的代码设置 ->
pom.xml
<!-- Spring Framework -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>3.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>3.2.7.RELEASE</version>
</dependency>
<!-- CXF -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-core</artifactId>
<version>3.0.2</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxrs</artifactId>
<version>3.0.2</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-bundle-jaxrs</artifactId>
<version>2.7.5</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-jaxrs</artifactId>
<version>1.9.3</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-xc</artifactId>
<version>1.9.3</version>
</dependency>
<!-- Hibernate validation -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.1.3.Final</version>
</dependency>
<!-- JSR 303 Validation framework -->
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-bean-validation</artifactId>
<version>2.19</version>
</dependency>
JAX-RS 服务器
<jaxrs:server address="/" id="iamService">
<jaxrs:serviceBeans>
<bean class="com.XXXXXX.iam.service.core.endpoint.impl.IAMIdentityServiceImpl" />
</jaxrs:serviceBeans>
<jaxrs:inInterceptors>
<ref bean="validationInInterceptor" />
</jaxrs:inInterceptors>
<jaxrs:outInterceptors>
<ref bean="validationOutInterceptor" />
<ref bean="exceptionMapperOutInterceptor" />
</jaxrs:outInterceptors>
<jaxrs:extensionMappings>
<entry key="json" value="application/json" />
</jaxrs:extensionMappings>
<jaxrs:providers>
<ref bean='jsonProvider' />
<ref bean="ampfExceptionMapper"/>
</jaxrs:providers>
</jaxrs:server>
<bean id="ampfExceptionMapper" class="com.xxxxxxx.iam.service.core.interceptor.AmpfValidationExceptionMapper"/>
<bean id="exceptionMapperOutInterceptor" class="org.apache.cxf.jaxrs.interceptor.JAXRSOutExceptionMapperInterceptor"/>
<bean id="validationProvider" class="org.apache.cxf.validation.BeanValidationProvider" />
<bean id="validationInInterceptor" class="org.apache.cxf.jaxrs.validation.JAXRSBeanValidationInInterceptor">
<property name="provider" ref="validationProvider" />
</bean>
<bean id="validationOutInterceptor" class="org.apache.cxf.jaxrs.validation.JAXRSBeanValidationOutInterceptor">
<property name="provider" ref="validationProvider" />
</bean>
自定义异常映射器
@Provider
@Produces(MediaType.APPLICATION_JSON)
public class AmpfValidationExceptionMapper implements ExceptionMapper<ConstraintViolationException>{
@Override
public Response toResponse(ConstraintViolationException exception) {
System.out.println("Executing Ameriprise Service exception mapping");
UniqueIDValidationResponse response = new UniqueIDValidationResponse();
response.setResponse("Invalid request");
return Response.ok(response).build();
}
}
豆子class
public class UniqueIDValidationRequest {
/*@Autowired
private Validator validator;*/
@XmlAttribute(name = "ssoId")
@NotBlank
@NotEmpty
@Length(max=20,min=5)
protected String ssoId;
@NotEmpty
@NotBlank
@XmlAttribute(name = "epHashId")
protected String epHashId;
此问题现已解决。我必须做的是创建一个自定义拦截器。这将捕获 bean 验证时发生的错误并添加实际的 bean 验证错误。请检查下面的 InInterceptor 代码 -->
@Provider
public class IamServInInterceptor extends
BeanValidationInInterceptor implements ContainerRequestFilter{
private static Logger logger = LoggerFactory.getLogger(IamServInInterceptor.class);
public IamServInInterceptor() {
logger.debug("IamServInInterceptor default consturctor called");
}
public IamServInInterceptor(String phase) {
super(phase);
logger.debug("IamServInInterceptor phase : " + phase);
}
@Override
public void filter(ContainerRequestContext arg0) throws IOException {
// TODO Auto-generated method stub
logger.debug("IamServInInterceptor filter is called");
InterceptorChain chain = PhaseInterceptorChain.getCurrentMessage()
.getInterceptorChain();
chain.add(this);
}
protected Object getServiceObject(Message message) {
logger.debug("IamServInInterceptor getServiceObject called");
return ValidationUtils.getResourceInstance(message);
}
protected void handleValidation(Message message, Object resourceInstance,
Method method, List<Object> arguments){
try {
logger.debug("IamServInInterceptor handleValidation called");
super.handleValidation(message, resourceInstance, method, arguments);
} catch (ConstraintViolationException ex) {
logger.debug("IamServInInterceptor handleValidation has some exception");
UniqueIDValidationResponse response = new UniqueIDValidationResponse();
response.setResponse("INPUT_VALIDATION_FAILED");
ConstraintViolationException constraintException = (ConstraintViolationException)ex;
Set<ConstraintViolation<?>> constraint=
constraintException.getConstraintViolations();
Iterator<ConstraintViolation<?>> iterator = constraint.iterator();
while(iterator.hasNext()){
ConstraintViolation<?> error = iterator.next();
logger.debug("Error : " + error.toString());
response.getMessage().add(error.getMessage());
}
ex.printStackTrace();
message.getExchange().put(Response.class, Response.ok(response).build());
}
}
}
我用异常映射器解决了:
import org.apache.cxf.validation.ResponseConstraintViolationException;
import org.slf4j.LoggerFactory;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
import java.util.Iterator;
/**
* Created by Hyun Woo Son on 1/9/18
**/
@Provider
public class ValidationExceptionMapperCustom implements ExceptionMapper<ValidationException> {
private static org.slf4j.Logger logger = LoggerFactory.getLogger(ValidationExceptionMapperCustom.class);
public Response toResponse(ValidationException exception) {
Response.Status errorStatus = Response.Status.INTERNAL_SERVER_ERROR;
ResponseVo responseVo = new ResponseVo();
if (exception instanceof ConstraintViolationException) {
ConstraintViolationException constraint = (ConstraintViolationException)exception;
Iterator i$ = constraint.getConstraintViolations().iterator();
StringBuilder errorMsg = new StringBuilder(RestExcepcionEnum.PARAMETROS_EXCEPCION.getValue());
errorMsg.append("\n");
while(i$.hasNext()) {
ConstraintViolation<?> violation = (ConstraintViolation)i$.next();
logger.info("error {}",violation.getRootBeanClass().getSimpleName() + "." + violation.getPropertyPath() + ": " + violation.getMessage());
errorMsg.append("La propiedad : "+violation.getPropertyPath() + " con error: " + violation.getMessage() );
errorMsg.append("\n");
}
if (!(constraint instanceof ResponseConstraintViolationException)) {
errorStatus = Response.Status.BAD_REQUEST;
}
responseVo.setMensaje(errorMsg.toString());
}
else{
responseVo.setMensaje("Error inesperado: "+exception.getMessage());
logger.debug("Error de validacion {}",exception.getMessage(),exception);
}
return Response.status(errorStatus).entity(responseVo).build();
}
}
并将其添加到应用程序 class: