Java 如何向使用XmlAdapter的JAXB注释类注册处理程序,以便向REST资源客户端发送错误消息?
我在调查 默认情况下,会抛出异常,但没有处理程序在侦听它。结果,这样的请求到达JAR-RS方法,但提交的thad字段值“不正确”,设置为NULL 我在JAVADOC中读到,必须设置一个处理程序,以便在这种情况发生时可以做一些事情。但我找不到有关如何设置此类处理程序的信息 我的端点类似于:Java 如何向使用XmlAdapter的JAXB注释类注册处理程序,以便向REST资源客户端发送错误消息?,java,jaxb,jax-rs,Java,Jaxb,Jax Rs,我在调查 默认情况下,会抛出异常,但没有处理程序在侦听它。结果,这样的请求到达JAR-RS方法,但提交的thad字段值“不正确”,设置为NULL 我在JAVADOC中读到,必须设置一个处理程序,以便在这种情况发生时可以做一些事情。但我找不到有关如何设置此类处理程序的信息 我的端点类似于: @Path("/path") public class MyResource { @POST @Path("something") public Response postSometh
@Path("/path")
public class MyResource {
@POST
@Path("something")
public Response postSomething(JaxbAnnotatedRequest request) {
//processing....
}
}
我看不出有什么真正优雅的方式来处理这件事。但要回答你的问题 “我在中读到,必须设置处理程序,以便在发生这种情况时可以执行某些操作。但我找不到有关如何设置此类处理程序的信息。” 解组将在一段时间内完成。应向注册,该注册是为每个请求创建的。因此,我们需要以某种方式利用当前使用的
MessageBodyReader
,我不确定该怎么做,或者以这种方式添加处理程序是否是可配置的。如果是,我相信它是特定于实现的
一种方法是编写自己的MessageBodyReader
来处理此特定类型。比如说
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import javax.ws.rs.Consumes;
import javax.ws.rs.InternalServerErrorException;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.Provider;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.ValidationEvent;
import javax.xml.bind.ValidationEventHandler;
@Provider
@Consumes(MediaType.APPLICATION_XML)
public class JaxbAnnotatedRequestMessageBodyReader
implements MessageBodyReader<JaxbAnnotatedRequest> {
JAXBContext context;
public JaxbAnnotatedRequestMessageBodyReader() {
try {
context = JAXBContext.newInstance(JaxbAnnotatedRequest.class);
} catch (JAXBException ex) {
throw new InternalServerErrorException();
}
}
@Override
public boolean isReadable(Class<?> type, Type type1,
Annotation[] antns, MediaType mt) {
return JaxbAnnotatedRequest.class == type;
}
@Override
public JaxbAnnotatedRequest readFrom(Class<JaxbAnnotatedRequest> type, Type type1,
Annotation[] antns, MediaType mt, MultivaluedMap<String, String> mm,
InputStream in) throws IOException, WebApplicationException {
try {
Unmarshaller unmarshaller = context.createUnmarshaller();
unmarshaller.setEventHandler(new ValidationEventHandler() {
@Override
public boolean handleEvent(ValidationEvent event) {
String nfe = NumberFormatException.class.getCanonicalName();
if (nfe.equals(event.getMessage())) {
throw new WebApplicationException(Response.status(400)
.entity("Bad Number Formatting").build());
}
System.out.println(event.getMessage());
return false;
}
});
JaxbAnnotatedRequest request
= (JaxbAnnotatedRequest) unmarshaller.unmarshal(in);
return request;
} catch (JAXBException ex) {
throw new WebApplicationException(Response.status(400).entity(
"Caught JAXBEception: " + ex.toString()).build());
}
}
}
import java.io.IOException;
导入java.io.InputStream;
导入java.lang.annotation.annotation;
导入java.lang.reflect.Type;
导入javax.ws.rs.Consumes;
导入javax.ws.rs.InternalServerErrorException;
导入javax.ws.rs.WebApplicationException;
导入javax.ws.rs.core.MediaType;
导入javax.ws.rs.core.MultivaluedMap;
导入javax.ws.rs.core.Response;
导入javax.ws.rs.ext.MessageBodyReader;
导入javax.ws.rs.ext.Provider;
导入javax.xml.bind.JAXBContext;
导入javax.xml.bind.JAXBException;
导入javax.xml.bind.Unmarshaller;
导入javax.xml.bind.ValidationEvent;
导入javax.xml.bind.ValidationEventHandler;
@提供者
@使用(MediaType.APPLICATION_XML)
公共类JaxbAnnotatedRequestMessageBodyReader
实现MessageBodyReader{
JAXBContext上下文;
public JaxbAnnotatedRequestMessageBodyReader(){
试一试{
context=JAXBContext.newInstance(JaxbAnnotatedRequest.class);
}捕获(JAXBEException-ex){
抛出新的InternalServerErrorException();
}
}
@凌驾
公共布尔值可读取(类类型,类型1,
注释[]ANTS,MediaType mt){
返回JaxbAnnotatedRequest.class==type;
}
@凌驾
公共JAXBannotatedRequestReadFrom(类类型,类型1,
注释[]antns,MediaType mt,多值地图mm,
InputStream in)引发IOException、WebApplicationException{
试一试{
Unmarshaller Unmarshaller=context.createUnmarshaller();
unmarshaller.setEventHandler(新的ValidationEventHandler(){
@凌驾
公共布尔handleEvent(ValidationEvent事件){
字符串nfe=NumberFormatException.class.getCanonicalName();
if(nfe.equals(event.getMessage())){
抛出新的WebApplicationException(Response.status(400)
.entity(“错误的数字格式”).build();
}
System.out.println(event.getMessage());
返回false;
}
});
JaxbAnnotatedRequest请求
=(JaxbAnnotatedRequest)解组器。解组器(in);
返回请求;
}捕获(JAXBEException-ex){
抛出新的WebApplicationException(Response.status(400.entity(
捕获JAXBEception:“+ex.toString()).build());
}
}
}
ValidationEventHandler
是在readFrom
方法中的Unmarshaller
上设置的
另一个选项(尽管也有限制)是使用,这是JAX-RS 2.0规范的一部分。如果您想使用此功能,您可能需要查看实现的文档。唯一的问题是假设您要使用
@NotNull
注释,您将知道请求值是否实际为null,或者JAXB是否将其设置为null,因此您需要发送一些通用消息。在我看来,您遇到的问题是验证问题。如今,输入验证是开发人员不可避免的责任。对于JSON或XML之类的数据文件,这可以通过使用模式验证
轻松完成。此验证需要集成到数据转换过程中,建议在编组和解编组中使用。如果出现null
值或任何其他不适当的输入,如to short post code等,则会抛出jaxbeexception
,然后将其包装成更具表现力的异常。最后,您必须声明一个ExceptionMapper
,它将捕获异常并向调用者发送相应的消息。下面是一个例子
实体类地址.java
@XmlRootElement(name = "address", namespace = DataEntity.NSP)
public class Address implements Serializable {
/**
*
*/
private static final long serialVersionUID = 2508433924387468026L;
private Integer id;
private String street;
private String number;
private Short zipCode;
private String city;
protected Address() {
}
public Address(final Integer id, final String street, final String number,
final Short zipCode, final String city) {
setId(id);
setStreet(street);
setNumber(number);
setZipCode(zipCode);
setCity(city);
}
@XmlAttribute
public Integer getId() {
return id;
}
private void setId(Integer id) {
ValidationHelper.validateId(id);
this.id = id;
}
@XmlElement(namespace = DataEntity.NSP)
public String getStreet() {
return street;
}
private void setStreet(String street) {
ValidationHelper.validateAddressStreet(street);
this.street = street;
}
@XmlElement(namespace = DataEntity.NSP)
public String getNumber() {
return number;
}
private void setNumber(String number) {
ValidationHelper.validateAddressNumber(number);
this.number = number;
}
@XmlElement(namespace = DataEntity.NSP)
public Short getZipCode() {
return zipCode;
}
private void setZipCode(Short zipCode) {
ValidationHelper.validateAddressZipCode(zipCode);
this.zipCode = zipCode;
}
@XmlElement(namespace = DataEntity.NSP)
public String getCity() {
return city;
}
private void setCity(String city) {
ValidationHelper.validateAddressCity(city);
this.city = city;
}
}
架构验证文件:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns="https://localhost/JAX-RS_BookStore_Service/xsd/" targetNamespace="https://localhost/JAX-RS_BookStore_Service/xsd/"
elementFormDefault="qualified" xmlns:pref="https://localhost/JAX-RS_BookStore_Service/xsd/">
<xs:include schemaLocation="Types.xsd" />
<xs:element name="address" type="addressType" />
</xs:schema>
<?xml version="1.0" encoding="ISO-8859-1"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns="https://localhost/JAX-RS_BookStore_Service/xsd/"
targetNamespace="https://localhost/JAX-RS_BookStore_Service/xsd/"
elementFormDefault="qualified" xmlns:pref="https://localhost/JAX-RS_BookStore_Service/xsd/">
<xs:simpleType name="unsignedInteger">
<xs:restriction base="xs:integer">
<xs:minInclusive value="1" />
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="addressStreet">
<xs:restriction base="xs:string">
<xs:pattern value="[a-zA-ZöäüÖÄÜß -]{2,32}" />
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="addressNumber">
<xs:restriction base="xs:string">
<xs:pattern value="[0-9]{1}[0-9a-z/\\ -]{0,7}" />
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="addressCity">
<xs:restriction base="xs:string">
<xs:pattern value="[a-zA-ZöäüÖÄÜß -]{2,32}" />
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="zipCodeMoreThan4Digits">
<xs:restriction base="xs:short">
<xs:minInclusive value="1000"></xs:minInclusive>
<xs:maxInclusive value="9999"></xs:maxInclusive>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="addressType">
<xs:sequence>
<xs:element name="city" type="addressCity" />
<xs:element name="number" type="addressNumber" />
<xs:element name="street" type="addressStreet" />
<xs:element name="zipCode" type="zipCodeMoreThan4Digits" />
</xs:sequence>
<xs:attribute name="id" type="unsignedInteger" use="required">
</xs:attribute>
</xs:complexType>
</xs:schema>
concreateMeassagBodyReader
实现,它提供特定的XSD文件。
public abstract class AbstractXmlValidationReader<T> implements
MessageBodyReader<T> {
private final Providers providers;
private final Schema schema;
/**
* Instantiates the XML validation class (MessageBodyReader)
*
* @param providers
* @param servletContext
* @param xsdFileName
*/
public AbstractXmlValidationReader(final Providers providers,
final ServletContext servletContext, final String xsdFileName) {
this.providers = providers;
try {
SchemaFactory sf = SchemaFactory
.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
File xsd = new File(servletContext.getRealPath(xsdFileName));
schema = sf.newSchema(xsd);
} catch (Exception e) {
throw new RuntimeException(
"Unable to create XSD validation schema", e);
}
}
@Override
public boolean isReadable(Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType) {
@SuppressWarnings("unchecked")
Class<T> readableClass = (Class<T>) ((ParameterizedType) getClass()
.getGenericSuperclass()).getActualTypeArguments()[0];
if (type == readableClass
&& type.isAnnotationPresent(XmlRootElement.class)) {
return true;
}
return false;
}
/**
* Source adapted from: <a href=
* "http://stackoverflow.com/questions/3428273/validate-jaxbelement-in-jpa-jax-rs-web-service"
* >stackoverflow.com</a>
*/
@Override
public T readFrom(Class<T> type, Type genericType,
Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
throws IOException, WebApplicationException {
try {
JAXBContext jaxbContext = null;
ContextResolver<JAXBContext> resolver = providers
.getContextResolver(JAXBContext.class, mediaType);
if (null != resolver) {
jaxbContext = resolver.getContext(type);
}
if (null == jaxbContext) {
jaxbContext = JAXBContext.newInstance(type);
}
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
unmarshaller.setSchema(schema);
@SuppressWarnings("unchecked")
T entity = (T) unmarshaller.unmarshal(entityStream);
return entity;
} catch (JAXBException e) {
throw new MessageBodyReaderValidationException(
"Failure while performing xml validation or xml marhalling!",
e);
}
}
}
@Provider
@Consumes(MediaType.APPLICATION_XML)
public class AddressXmlValidationReader extends
AbstractXmlValidationReader<Address> {
private final static String xsdFileName = "/xsd/Address.xsd";
public AddressXmlValidationReader(@Context Providers providers,
@Context ServletContext servletContext) {
super(providers, servletContext, xsdFileName);
}
}
使用Firefox RESTClient插件发出POST请求的结果。 请注意,
标记的内容为空,NULL
值
@Provider
public class MessageBodyReaderValidationExceptionMapper implements
ExceptionMapper<MessageBodyReaderValidationException> {
@Override
public Response toResponse(MessageBodyReaderValidationException e) {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity(e.getMessage()).type(MediaType.TEXT_PLAIN).build();
}
}