【问题标题】:How to customize spring webservice's PayloadValidatingInterceptor response message?如何自定义spring webservice的PayloadValidatingInterceptor响应消息?
【发布时间】:2013-04-01 10:18:17
【问题描述】:

我有一个使用 jaxb 和 spring webservice 构建的 java web 服务应用程序。

我在 xsd 中有一个复杂类型,如下所示:

...

<complexType name="GetRecordsRequest">
    <sequence>
        <element name="maxRecords" type="int" maxOccurs="1" minOccurs="1"/>
    </sequence>
</complexType>

...

使用 xjc,我得到了从 xsd 生成的 jaxb 类:

public class GetRecordsRequest {
    protected int maxRecords;

    public int getMaxRecords() {
        return maxRecords;
    }

    public void setMaxRecords(int value) {
        this.maxRecords = value;
    }
}

我在 spring context.xml 中使用了 PayloadValidatingInterceptor 来确保用户不能为 maxRecords 输入除整数以外的任何内容:

<bean class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping">
    <property name="interceptors">
        <list>
            <ref local="validatingInterceptor" />
        </list>
    </property>
</bean>

<bean id="validatingInterceptor" class="org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor">
    <property name="schema" value="/WEB-INF/schemas/webservice.xsd" />
    <property name="validateRequest" value="true" />
    <property name="validateResponse" value="true" />
</bean>

当我在 Soap UI 中输入这个肥皂请求 xml 时:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://www.test.com/ns1">
   <soapenv:Header/>
   <soapenv:Body>
      <ns1:GetRecordsRequest>
         <ns1:maxRecords></ns1:maxRecords>
      </ns1:GetRecordsRequest>
   </soapenv:Body>
</soapenv:Envelope>

我得到的回复信息是:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Header/>
   <SOAP-ENV:Body>
      <SOAP-ENV:Fault>
         <faultcode>SOAP-ENV:Client</faultcode>
         <faultstring xml:lang="en">Validation error</faultstring>
         <detail>
            <spring-ws:ValidationError xmlns:spring-ws="http://springframework.org/spring-ws">cvc-datatype-valid.1.2.1: '' is not a valid value for 'integer'.</spring-ws:ValidationError>
            <spring-ws:ValidationError xmlns:spring-ws="http://springframework.org/spring-ws">cvc-type.3.1.3: The value '' of element 'cis:maxRecords' is not valid.</spring-ws:ValidationError>
         </detail>
      </SOAP-ENV:Fault>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

您可以看到结果是仅针对一个字段的两行神秘消息。我可以通过只写一行来使响应消息更漂亮吗?有没有办法自定义验证错误响应消息?

【问题讨论】:

    标签: java web-services spring xsd spring-ws


    【解决方案1】:

    您可以通过扩展 PayloadValidatingInterceptor 并覆盖 handleRequestValidationErrors 来自定义验证错误消息。我们可以在messageContext的body中设置自定义的错误信息。

    1) 代替请求验证错误的 SOAP 错误,您可以返回带有验证错误消息的自定义 xml 响应。

    2) SAXParseException[] 错误包含请求验证错误。您可以选择仅返回一个错误作为响应。 (或)对于某些预定义的错误,您可以返回自定义错误消息,而不是 SAXParseException 中返回的错误消息。

    /**
    * The Class CustomValidatingInterceptor.
    */
    public class CustomValidatingInterceptor extends PayloadValidatingInterceptor{
    
    /* (non-Javadoc)
     * @see org.springframework.ws.soap.server.endpoint.interceptor.AbstractFaultCreatingValidatingInterceptor#handleRequestValidationErrors(org.springframework.ws.context.MessageContext, org.xml.sax.SAXParseException[])
     */
    @Override
    protected boolean handleRequestValidationErrors(MessageContext messageContext, SAXParseException[] errors) throws TransformerException {
        JAXBContext jaxbContext;
        StringWriter stringWriter = new StringWriter();
        ResponseTransactionDetail transactionDetail = null;
    
        for (SAXParseException error : errors) {
            logger.debug("XML validation error on request: " + error.getMessage());
        }
        if (messageContext.getResponse() instanceof SoapMessage) {
    
            /**
             * Get SOAP response body in message context (SOAP Fault)
             */
    
            SaajSoapMessage soapMessage = (SaajSoapMessage)messageContext.getResponse();
            SoapBody body = soapMessage.getSoapBody();
    
            // marshal custom error response to stringWriter
    
            /**
            * Transform body
            */
            Source source = new StreamSource(new StringReader(stringWriter.toString()));
            identityTransform.transform(source, body.getPayloadResult());
            stringWriter.close();
        }
        return false;
    }
    

    【讨论】:

      【解决方案2】:

      您可以使用AbstractValidatingInterceptor(PayloadValidatingInterceptor 是这个抽象类的一个实现)的方法来自定义验证错误响应,即:

      • setDetailElementName(QName detailElementName)
      • setFaultStringOrReason(String faultStringOrReason)

      部分示例:

      public final class MyPayloadValidatingInterceptor
      extends PayloadValidatingInterceptor {
      
          @Override
          protected Source getValidationRequestSource(WebServiceMessage webSerMessage_) {
              _source = webSerMessage_.getPayloadSource();
              validateSchema(_source);
              return _source;
          }
      
          private void validateSchema(Source source_) throws Exception {
              SchemaFactory _schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
              Schema _schema = _schemaFactory.newSchema(getSchemas()[0].getFile());
      
              Validator _validator = _schema.newValidator();
              DOMResult _result = new DOMResult();
              try {
                  _validator.validate(source_, _result);
              } catch (SAXException _exception) {
                  // modify your soapfault here                
              }
          }
      }
      

      【讨论】:

      • 那么我应该做的是创建一个从 AbstractValidatingInterceptor 扩展的类,然后实现这两个方法,对吗?如果正确,如何从 QName 对象中获取元素名称?我在 QName 中没有看到任何元素名称字段。
      • 不,你应该使用你的 PayloadValidatingInterceptor。首先检测是否有故障,然后使用提供的两种方法修改故障消息。您是否从 QName 检查了 localpart ?它应该是元素名称。
      • 嗨,我创建了一个扩展PayloadValidatingInterceptor 的类,然后我还覆盖了setDetailElementNamesetFaultStringOrReason 方法。我还在eclipse中的两个方法中都设置了断点,但是当我运行webservice时,断点没有命中,这意味着这两个方法根本没有被调用。有什么我想念的吗?你想看代码吗?如果你愿意,我可以用 pastebin 发给你。
      • @suud:不,这些方法的默认实现很好。您需要修改 getValidationRequestSource(WebServiceMessage request) 方法。其中,需要调用 super.getValidationRequestSource 并检测是否有故障。你可能需要做一些 try catch。
      • 嗨,我真的不知道该怎么做。你能给出一个示例代码吗?我需要的只是以下信息:1)具有无效值的元素名称列表/数组。 2) 点 1 中每个元素的元素值列表/数组。 3) 如果可能的话,点 1 中每个元素的错误原因列表/数组(例如由于数据类型错误导致值无效,插入空值用于强制元素等)。
      猜你喜欢
      • 2012-02-16
      • 2016-07-10
      • 2014-07-29
      • 2018-11-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-24
      • 2019-10-07
      相关资源
      最近更新 更多