在 JAX-WS RI 中延迟验证部分传入的 Web 服务请求
validate parts of the incoming web service request lazily in JAX-WS RI
我必须在 Java 8 网络服务 (JAX-WS RI) 中处理大请求。一个请求包含一个 "header" 和许多 "records" 像这样(不是说第 2、3、4、5 个学生记录根据 schema 是无效的):
<helloStudentsServiceRequest>
<workshop>
<name>wsname</name>
<tutor>tutorname</tutor>
</workshop>
<studentList>
<!--1 or more repetitions: -->
<student>
<Name>st1</Name>
<Birth>1999-11-11</Birth>
</student>
<student>
<Name>st2</Name>
</student>
<student>
<Name>st2</Name>
<Birth>199O-11-11</Birth>
</student>
<student>
<Name>st3</Name>
<Birth>stoneage</Birth>
</student>
<student>
<Birth>stoneage</Birth>
</student>
<student>
<Name>st6</Name>
<Birth>2001-11-12</Birth>
</student>
</studentList>
</helloStudentsServiceRequest>
我使用模式 (XSD) 来描述我的界面。例如:
<xsd:complexType name="HelloStudentsServiceRequestType">
<xsd:sequence>
<xsd:element name="workshop" type="WorkshopType" />
<xsd:element name="studentList">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="student" type="StudentType" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="WorkshopType">
<xsd:sequence>
<xsd:element name="name" type="xsd:string" />
<xsd:element name="tutor" type="xsd:string" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="StudentType">
</xsd:annotation>
<xsd:sequence>
<xsd:element name="Name" type="xsd:string" />
<xsd:element name="Birth" type="xsd:date" />
</xsd:sequence>
</xsd:complexType>
我想使用此架构来验证请求。
但是,如果请求中的一些 "record" 根据架构无效,我将无法拒绝该请求。在这种情况下,我必须处理有效记录和 return 一些关于无效记录的有意义的信息(最好是 SAX 错误)。
我希望请求的架构不包含完成此 "lazy validation" 所需的技术细节。例如 我不想 使用 "big string" 来保存 "record" 然后根据模式验证该字符串。这会破坏界面并且难以与客户沟通。所以这对我来说 不行:
...
<xsd:element name="studentList">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="student" type="xsd:string" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
...
您知道在 JAX-WS RI (JAXB2) 中解决这个问题的优雅方法吗?
可以自定义 JAXB 绑定以将 XML 的一部分映射到 DOM。详情:
https://jaxb.java.net/guide/Avoid_strong_databinding.html#Mapping_to_DOM
所以我将 "WorkshopType" 和 "StudentType" 映射到 DOM。现在可以单独验证这些 DOMs 并拒绝或部分处理传入消息。如果 DOM 验证不成功,也可以捕获 SAX 异常并将其发回客户端。
working example zipped
我使用了这个绑定定制:
<jxb:bindings version="1.0"
xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<jxb:bindings
schemaLocation="WorkshopType.xsd"
node="//xsd:complexType[@name='WorkshopType']">
<jxb:dom />
</jxb:bindings>
<jxb:bindings
schemaLocation="StudentType.xsd"
node="//xsd:complexType[@name='StudentType']">
<jxb:dom />
</jxb:bindings>
这些是模式:
对于请求:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified" xmlns="helloStudentsNS"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" jaxb:version="2.0"
targetNamespace="helloStudentsNS">
<xsd:include schemaLocation="StudentType.xsd" />
<xsd:include schemaLocation="WorkshopType.xsd" />
<xsd:element name="helloStudentsServiceRequest" type="HelloStudentsServiceRequestType" />
<xsd:element name="helloStudentsServiceResponse" type="HelloStudentsServiceResponseType" />
<xsd:complexType name="HelloStudentsServiceRequestType">
<xsd:sequence>
<xsd:element name="workshop" type="WorkshopType" />
<xsd:element name="studentList">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="student" type="StudentType" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="HelloStudentsServiceResponseType">
<xsd:sequence>
<xsd:element name="GreetingHeader" type="xsd:string" />
<xsd:element name="StudentGreeting" type="xsd:string" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
对于研讨会和学生类型:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified"
xmlns="helloStudentsNS"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
jaxb:version="2.0"
targetNamespace="helloStudentsNS">
<xsd:element name="workshop" type="WorkshopType"/>
<xsd:complexType name="WorkshopType">
<!-- <xsd:annotation><xsd:appinfo><jaxb:dom/></xsd:appinfo></xsd:annotation> -->
<xsd:sequence>
<xsd:element name="name" type="xsd:string" />
<xsd:element name="tutor" type="xsd:string" />
</xsd:sequence>
</xsd:complexType>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified"
xmlns="helloStudentsNS"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
jaxb:version="2.0" targetNamespace="helloStudentsNS">
<!--
-->
<xsd:element name="student" type="StudentType"/>
<xsd:complexType name="StudentType">
<!-- <xsd:annotation><xsd:appinfo><jaxb:dom/></xsd:appinfo></xsd:annotation> -->
<xsd:sequence>
<xsd:element name="Name" type="xsd:string" />
<xsd:element name="Birth" type="xsd:date" />
</xsd:sequence>
</xsd:complexType>
这是 java 代码:
public HelloStudentsServiceResponseType greetStudents(HelloStudentsServiceRequestType req) {
WorkshopType ws;
// unmarshall workshop data
Element element = req.getWorkshop(); // workshop is retrieved as DOM
try {
Unmarshaller u = jc.createUnmarshaller(); // jaxb ri unmarshaller not thread safe: https://jaxb.java.net/guide/Performance_and_thread_safety.html
u.setSchema(workshopSchema); // enable validation by workshopType.xsd
ws = u.unmarshal(element, WorkshopType.class).getValue(); // unmarshal
} catch (JAXBException e) {
// this is fatal
throw new RuntimeException("fatal: invalid workshop data: ", e);
}
// unmarshall students
List<String> studentGreetings = new ArrayList<>();
for(Element studentElement : req.getStudentList().getStudent()) { // retrieve students as DOMs
try {
StudentType st;
Unmarshaller u = jc.createUnmarshaller(); // jaxb ri unmarshaller not thread safe: https://jaxb.java.net/guide/Performance_and_thread_safety.html
u.setSchema(studentSchema); // enable validation by studentType.xsd
st = u.unmarshal(studentElement, StudentType.class).getValue();
studentGreetings.add("Dear " + st.getName() + ", " + st.getBirth() + ", welcome in the workshop!");
} catch (JAXBException e) {
Throwable cause = e.getLinkedException();
if(cause != null) {
studentGreetings.add("Dear student, your data is invalid: " + cause.getMessage());
}
else {
studentGreetings.add("Dear student, there's an unknown problem with your data");
}
}
}
// construct response
HelloStudentsServiceResponseType response = new HelloStudentsServiceResponseType();
response.setGreetingHeader("I'd like to greet everybody in the " + ws.getName() + " workshop. " +
"Your tutor is " + ws.getTutor());
response.getStudentGreeting().addAll(studentGreetings);
return response;
}
我必须在 Java 8 网络服务 (JAX-WS RI) 中处理大请求。一个请求包含一个 "header" 和许多 "records" 像这样(不是说第 2、3、4、5 个学生记录根据 schema 是无效的):
<helloStudentsServiceRequest>
<workshop>
<name>wsname</name>
<tutor>tutorname</tutor>
</workshop>
<studentList>
<!--1 or more repetitions: -->
<student>
<Name>st1</Name>
<Birth>1999-11-11</Birth>
</student>
<student>
<Name>st2</Name>
</student>
<student>
<Name>st2</Name>
<Birth>199O-11-11</Birth>
</student>
<student>
<Name>st3</Name>
<Birth>stoneage</Birth>
</student>
<student>
<Birth>stoneage</Birth>
</student>
<student>
<Name>st6</Name>
<Birth>2001-11-12</Birth>
</student>
</studentList>
</helloStudentsServiceRequest>
我使用模式 (XSD) 来描述我的界面。例如:
<xsd:complexType name="HelloStudentsServiceRequestType">
<xsd:sequence>
<xsd:element name="workshop" type="WorkshopType" />
<xsd:element name="studentList">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="student" type="StudentType" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="WorkshopType">
<xsd:sequence>
<xsd:element name="name" type="xsd:string" />
<xsd:element name="tutor" type="xsd:string" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="StudentType">
</xsd:annotation>
<xsd:sequence>
<xsd:element name="Name" type="xsd:string" />
<xsd:element name="Birth" type="xsd:date" />
</xsd:sequence>
</xsd:complexType>
我想使用此架构来验证请求。
但是,如果请求中的一些 "record" 根据架构无效,我将无法拒绝该请求。在这种情况下,我必须处理有效记录和 return 一些关于无效记录的有意义的信息(最好是 SAX 错误)。
我希望请求的架构不包含完成此 "lazy validation" 所需的技术细节。例如 我不想 使用 "big string" 来保存 "record" 然后根据模式验证该字符串。这会破坏界面并且难以与客户沟通。所以这对我来说 不行:
...
<xsd:element name="studentList">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="student" type="xsd:string" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
...
您知道在 JAX-WS RI (JAXB2) 中解决这个问题的优雅方法吗?
可以自定义 JAXB 绑定以将 XML 的一部分映射到 DOM。详情:
https://jaxb.java.net/guide/Avoid_strong_databinding.html#Mapping_to_DOM
所以我将 "WorkshopType" 和 "StudentType" 映射到 DOM。现在可以单独验证这些 DOMs 并拒绝或部分处理传入消息。如果 DOM 验证不成功,也可以捕获 SAX 异常并将其发回客户端。
working example zipped
我使用了这个绑定定制:
<jxb:bindings version="1.0"
xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<jxb:bindings
schemaLocation="WorkshopType.xsd"
node="//xsd:complexType[@name='WorkshopType']">
<jxb:dom />
</jxb:bindings>
<jxb:bindings
schemaLocation="StudentType.xsd"
node="//xsd:complexType[@name='StudentType']">
<jxb:dom />
</jxb:bindings>
这些是模式:
对于请求:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified" xmlns="helloStudentsNS"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" jaxb:version="2.0"
targetNamespace="helloStudentsNS">
<xsd:include schemaLocation="StudentType.xsd" />
<xsd:include schemaLocation="WorkshopType.xsd" />
<xsd:element name="helloStudentsServiceRequest" type="HelloStudentsServiceRequestType" />
<xsd:element name="helloStudentsServiceResponse" type="HelloStudentsServiceResponseType" />
<xsd:complexType name="HelloStudentsServiceRequestType">
<xsd:sequence>
<xsd:element name="workshop" type="WorkshopType" />
<xsd:element name="studentList">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="student" type="StudentType" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="HelloStudentsServiceResponseType">
<xsd:sequence>
<xsd:element name="GreetingHeader" type="xsd:string" />
<xsd:element name="StudentGreeting" type="xsd:string" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
对于研讨会和学生类型:
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified"
xmlns="helloStudentsNS"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
jaxb:version="2.0"
targetNamespace="helloStudentsNS">
<xsd:element name="workshop" type="WorkshopType"/>
<xsd:complexType name="WorkshopType">
<!-- <xsd:annotation><xsd:appinfo><jaxb:dom/></xsd:appinfo></xsd:annotation> -->
<xsd:sequence>
<xsd:element name="name" type="xsd:string" />
<xsd:element name="tutor" type="xsd:string" />
</xsd:sequence>
</xsd:complexType>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified"
xmlns="helloStudentsNS"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
jaxb:version="2.0" targetNamespace="helloStudentsNS">
<!--
-->
<xsd:element name="student" type="StudentType"/>
<xsd:complexType name="StudentType">
<!-- <xsd:annotation><xsd:appinfo><jaxb:dom/></xsd:appinfo></xsd:annotation> -->
<xsd:sequence>
<xsd:element name="Name" type="xsd:string" />
<xsd:element name="Birth" type="xsd:date" />
</xsd:sequence>
</xsd:complexType>
这是 java 代码:
public HelloStudentsServiceResponseType greetStudents(HelloStudentsServiceRequestType req) {
WorkshopType ws;
// unmarshall workshop data
Element element = req.getWorkshop(); // workshop is retrieved as DOM
try {
Unmarshaller u = jc.createUnmarshaller(); // jaxb ri unmarshaller not thread safe: https://jaxb.java.net/guide/Performance_and_thread_safety.html
u.setSchema(workshopSchema); // enable validation by workshopType.xsd
ws = u.unmarshal(element, WorkshopType.class).getValue(); // unmarshal
} catch (JAXBException e) {
// this is fatal
throw new RuntimeException("fatal: invalid workshop data: ", e);
}
// unmarshall students
List<String> studentGreetings = new ArrayList<>();
for(Element studentElement : req.getStudentList().getStudent()) { // retrieve students as DOMs
try {
StudentType st;
Unmarshaller u = jc.createUnmarshaller(); // jaxb ri unmarshaller not thread safe: https://jaxb.java.net/guide/Performance_and_thread_safety.html
u.setSchema(studentSchema); // enable validation by studentType.xsd
st = u.unmarshal(studentElement, StudentType.class).getValue();
studentGreetings.add("Dear " + st.getName() + ", " + st.getBirth() + ", welcome in the workshop!");
} catch (JAXBException e) {
Throwable cause = e.getLinkedException();
if(cause != null) {
studentGreetings.add("Dear student, your data is invalid: " + cause.getMessage());
}
else {
studentGreetings.add("Dear student, there's an unknown problem with your data");
}
}
}
// construct response
HelloStudentsServiceResponseType response = new HelloStudentsServiceResponseType();
response.setGreetingHeader("I'd like to greet everybody in the " + ws.getName() + " workshop. " +
"Your tutor is " + ws.getTutor());
response.getStudentGreeting().addAll(studentGreetings);
return response;
}