8

SOAP サービスを使用しています。XML 要素に不適切な入力を指定すると、適切な入力で要求と応答が期待どおりに機能します。

リクエスト本文:

...
<ns:myIntegerElement>asdf</ns:myIntegerElement>
...

私の例外リゾルバーが呼び出されます。このリゾルバーは例外リゾルバーの単なる実装であるため、例外マッピングはなく、抽象メソッドに System.out がいくつかあるだけです

<bean id="exceptionResolver" class="com.mycompany.ws.MyExceptionResolver">

ただし、次のようなリクエストを送信すると:

...
    <ns:myIntegSOMETHINGGOTTOTALLYMESSUP!!!ent>asdf</ns:myIntegerElement>
...

私のリゾルバーはまったく実行されません

ルート デバッグ レベルを持つように log4j をセットアップすると、次の出力が表示されます。

2010-08-09 10:30:01,900 [スレッド:http-8080-2] DEBUG [org.springframework.ws.transport.http.WebServiceMessageReceiverHandlerAdapter] - 着信を受け入れる [org.springframework.ws.transport.http.HttpServletConnection@c46dcf ] から [ http://localhost:8080/myws/MyWebServices/] へ エラー: 「要素タイプ "ns:MESSEDUPELEMENT" は、一致する終了タグ "" で終了する必要があります。」2010-08-09 10:30:01,920 [スレッド:http-8080-2] DEBUG [org.springframework.ws.transport.http.MessageDispatcherServlet] - リクエスト org.springframework.ws.soap.saaj.SaajSoapMessageException を完了できませんでした:エンベロープにアクセスできませんでした: 指定されたソースからエンベロープを作成できません: ; ネストされた例外は com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl です: 指定されたソースからエンベロープを作成できません: org.springframework.ws.soap.saaj.support.SaajUtils.getSaajVersion(SaajUtils.java:162) at org org.springframework.ws.soap.saaj.SaajSoapMessage.(SaajSoapMessage.java: org.springframework.ws.soap.saaj.support.SaajUtils.getSaajVersion( SaajUtils.java:159) ... 24 以上 原因: javax.xml.transform.TransformerException: org.xml.sax.SAXParseException: 要素タイプ "smm:smm-aid" は、一致する終了タグで終了する必要があります " "。com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:719) で com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl. java:313) com.sun.xml.internal.messaging.saaj.util.transform.EfficientStreamingTransformer.transform(EfficientStreamingTransformer.java:393) で com.sun.xml で。internal.messaging.saaj.soap.EnvelopeFactory.createEnvelope(EnvelopeFactory.java:102) ... 27 以上 原因: org.xml.sax.SAXParseException: エレメント タイプ "smm:smm-aid" は、一致する終了タグ "". com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1231) com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java: 522) org.xml.sax.helpers.XMLFilterImpl.parse(XMLFilterImpl.java:333) com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transformIdentity(TransformerImpl.java:636) com .sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:707) ... 30 以上 createEnvelope(EnvelopeFactory.java:102) ... 27 以上 原因: org.xml.sax.SAXParseException: 要素タイプ "smm:smm-aid" は、一致する終了タグ "" で終了する必要があります。com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1231) com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java: 522) org.xml.sax.helpers.XMLFilterImpl.parse(XMLFilterImpl.java:333) com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transformIdentity(TransformerImpl.java:636) com .sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:707) ... 30 以上 createEnvelope(EnvelopeFactory.java:102) ... 27 以上 原因: org.xml.sax.SAXParseException: 要素タイプ "smm:smm-aid" は、一致する終了タグ "" で終了する必要があります。com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1231) com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java: 522) org.xml.sax.helpers.XMLFilterImpl.parse(XMLFilterImpl.java:333) com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transformIdentity(TransformerImpl.java:636) com .sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:707) ... 30 以上 要素タイプ「smm:smm-aid」は、一致する終了タグ「」で終了する必要があります。com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1231) com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java: 522) org.xml.sax.helpers.XMLFilterImpl.parse(XMLFilterImpl.java:333) com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transformIdentity(TransformerImpl.java:636) com .sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:707) ... 30 以上 要素タイプ「smm:smm-aid」は、一致する終了タグ「」で終了する必要があります。com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1231) com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java: 522) org.xml.sax.helpers.XMLFilterImpl.parse(XMLFilterImpl.java:333) com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transformIdentity(TransformerImpl.java:636) com .sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:707) ... 30 以上

ここでは、Spring が例外の可能性を見逃しており、それをラップしていないように見えますが、そのような基本的なエラー状態がキャッチされない可能性は低いと思われます。この問題の根本を見つけるのを手伝ってくれる人はいますか?

web.xml と servlet.xml も含めます。

web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <servlet>
        <servlet-name>ws</servlet-name>
        <servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
        <init-param>
            <param-name>transformWsdlLocations</param-name>
            <param-value>true</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>ws</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>

サーブレット コンテキスト:

<context:component-scan base-package="com.mycomp.proj.ws" />
    <bean id="smmService" class="com.mycomp.proj.ws.SMMRequestHandlingServiceStubImpl"/>

    <bean class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping"/>
    <bean class="org.springframework.ws.server.endpoint.adapter.MarshallingMethodEndpointAdapter">
        <constructor-arg ref="marshaller"/>
    </bean>
    <bean id="marshaller" class="org.springframework.oxm.castor.CastorMarshaller">
        <property name="mappingLocations">
            <list>
                <value>classpath:mapping.xml</value>
                <value>classpath:hoursOfOperationMapping.xml</value>
            </list>
        </property>
    </bean>

    <bean id="smmws" class="org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition">
        <property name="schema" ref="schema" />
        <property name="portTypeName" value="SMM" />
        <property name="locationUri" value="/SMMWebServices/"/>
        <property name="targetNamespace" value="http://mycomp.proj.com" />
    </bean>

    <bean id="exceptionResolver" class="com.wdp.smm.ws.MyExceptionResolver"/>

    <bean id="schema" class="org.springframework.xml.xsd.SimpleXsdSchema">
        <property name="xsd" value="/WEB-INF/ws.xsd" />
    </bean>
4

8 に答える 8

7

MessageDispatcherServlet は、デフォルトで EnpointExceptionResolver の独自の 2 つのインスタンス、つまり SoapFaultAnnotationExceptionResolver@1 と SimpleSoapExceptionResolver@2 を作成します。

また、例外を解決すると、SimpleSoapExceptionResolver@2 は、登録されている他の EnpointExceptionResolver を停止して、例外を処理します。

理解するのに恥ずかしいほど時間がかかりましたが、答えは非常に簡単です。サーブレットのコンテキストでは、これを行う必要があります。

<bean id="exceptionResolver" class="com.wdp.smm.ws.MyExceptionResolver">
    <property name="order" value="1"/>
</bean>
于 2013-01-28T20:41:08.923 に答える
5

私はあなたの質問をより詳しく調べましたが、何が起こっているのかを知っていると思います. 例外ハンドラは、SOAP 処理の上位レベルにあるため呼び出されません。ご覧のとおり、WebServiceMessageReceiverHandlerAdapter は、着信文字列を XML にデコードしてから、処理のためにマーシャラーに送信しようとします。XML が無効であるため、呼び出しは失敗します。また、WebServiceMessageReceiverHandlerAdapter は例外ハンドラーをサポートしていないため、例外 "SaajSoapMessageException" を再スローするだけです。

ここでできることは、WebServiceMessageReceiverHandlerAdapter を拡張する新しいクラスを作成することですが、例外がスローされたときに例外ハンドラーを使用する try/catch で handleConnection() もラップします。


ちなみに、この種の問題をデバッグするときの私のアプローチは、log4j にメソッド名と行番号の両方を出力することです。Spring ソースのダウンロードと同様に。

于 2010-08-20T12:18:10.873 に答える
3

@thierry-dimitri-roy の説明は正しいと思いますが、実際に実装するには多くの問題がありました。たとえば、noendpointfoundexception がスローされないため、handleconnection メソッドをラップするだけでは不十分です。これは例外を適切に処理するためのより一般的な問題であるため、将来の世代の負担を軽減するためにコードをここに置きます。これは、spring-ws 2.1.3 および JBoss AS7 でテストされています。

私のメッセージ ハンドラーは、すべての問題を応答コード 200 の SOAP エラーに変換します。

package fi.eis.applications.spring.soap.server.transport.http;

import java.io.IOException;
import java.net.URISyntaxException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringEscapeUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.Ordered;
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.ws.FaultAwareWebServiceMessage;
import org.springframework.ws.InvalidXmlException;
import org.springframework.ws.NoEndpointFoundException;
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.WebServiceMessageFactory;
import org.springframework.ws.context.DefaultMessageContext;
import org.springframework.ws.context.MessageContext;
import org.springframework.ws.support.DefaultStrategiesHelper;
import org.springframework.ws.transport.EndpointAwareWebServiceConnection;
import org.springframework.ws.transport.FaultAwareWebServiceConnection;
import org.springframework.ws.transport.WebServiceConnection;
import org.springframework.ws.transport.WebServiceMessageReceiver;
import org.springframework.ws.transport.context.DefaultTransportContext;
import org.springframework.ws.transport.context.TransportContext;
import org.springframework.ws.transport.context.TransportContextHolder;
import org.springframework.ws.transport.http.HttpServletConnection;
import org.springframework.ws.transport.http.HttpTransportConstants;
import org.springframework.ws.transport.http.MessageDispatcherServlet;
import org.springframework.ws.transport.http.WebServiceMessageReceiverHandlerAdapter;
import org.springframework.ws.transport.support.TransportUtils;

/**
 * Adapter to map XML parsing and other low-level errors to SOAP faults instead of
 * server standard error pages. Also, this class will always use return code HTTP_OK
 * (status 200) to requests, even if there are errors.
 *  
 */
public class MyWebServiceMessageReceiverHandlerAdapter
    extends WebServiceMessageReceiverHandlerAdapter
    implements HandlerAdapter, Ordered, InitializingBean, ApplicationContextAware {

    private ApplicationContext context;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        this.context = applicationContext;
    }

    @Override
    public void afterPropertiesSet() {
        DefaultStrategiesHelper defaultStrategiesHelper = new DefaultStrategiesHelper(MessageDispatcherServlet.class);
        WebServiceMessageFactory factory = defaultStrategiesHelper
                .getDefaultStrategy(WebServiceMessageFactory.class, context);
        setMessageFactory(factory);
    }

    public ModelAndView handle(HttpServletRequest httpServletRequest,
                               HttpServletResponse httpServletResponse,
                               Object handler) throws Exception {
        if (HttpTransportConstants.METHOD_POST.equals(httpServletRequest.getMethod())) {
            WebServiceConnection connection = new MyWebServiceConnection(httpServletRequest, httpServletResponse);
            try {
                overriddenHandleConnection(connection, (WebServiceMessageReceiver) handler);
            } catch (InvalidXmlException ex) {
                handleInvalidXmlException(httpServletRequest, httpServletResponse, handler, ex);
            } catch (Exception ex) {
                handleGeneralException(httpServletRequest, httpServletResponse, handler, ex);
            }
        }
        else {
            handleNonPostMethod(httpServletRequest, httpServletResponse, handler);
        }
        return null;
    }


    /**
     * Overridden version of handleConnection from WebServiceMessageReceiverObjectSupport to be able to handle
     * missing endpoint ourselves. That method is final, so we need to use another method here.
     * 
     * This has been reported as https://jira.springsource.org/browse/SWS-850
     * 
     * @param connection
     * @param receiver
     * @throws Exception
     */
    protected void overriddenHandleConnection(WebServiceConnection connection, WebServiceMessageReceiver receiver)
            throws Exception {
        logUri(connection);
        TransportContext previousTransportContext = TransportContextHolder.getTransportContext();
        TransportContextHolder.setTransportContext(new DefaultTransportContext(connection));

        try {
            WebServiceMessage request = connection.receive(getMessageFactory());
            MessageContext messageContext = new DefaultMessageContext(request, getMessageFactory());
            receiver.receive(messageContext);
            if (messageContext.hasResponse()) {
                WebServiceMessage response = messageContext.getResponse();
                if (response instanceof FaultAwareWebServiceMessage &&
                        connection instanceof FaultAwareWebServiceConnection) {
                    FaultAwareWebServiceMessage faultResponse = (FaultAwareWebServiceMessage) response;
                    FaultAwareWebServiceConnection faultConnection = (FaultAwareWebServiceConnection) connection;
                    faultConnection.setFault(faultResponse.hasFault());
                }
                connection.send(messageContext.getResponse());
            }
        }
        catch (NoEndpointFoundException ex) {
            if (connection instanceof EndpointAwareWebServiceConnection) {
                ((EndpointAwareWebServiceConnection) connection).endpointNotFound();
            }
            throw ex;
        }
        finally {
            TransportUtils.closeConnection(connection);
            TransportContextHolder.setTransportContext(previousTransportContext);
        }
    }

    private void logUri(WebServiceConnection connection) {
        if (logger.isDebugEnabled()) {
            try {
                logger.debug("Accepting incoming [" + connection + "] at [" + connection.getUri() + "]");
            }
            catch (URISyntaxException e) {
                // ignore
            }
        }
    }



    private void handleGeneralException(
            HttpServletRequest httpServletRequest,
            HttpServletResponse response, Object handler,
            Exception ex) throws IOException {
        writeErrorResponseWithMessage(response, ex.getClass().getName() + ": " + ex.getMessage());
    }

    /**
     * By default, sets SC_BAD_REQUEST as response in Spring, so overwritten to
     * provide HTTP_OK and reasonable SOAP fault response.
     */
    protected void handleInvalidXmlException(
            HttpServletRequest httpServletRequest,
            HttpServletResponse response, Object handler, InvalidXmlException ex)
            throws IOException {
        writeErrorResponseWithMessage(response, ex.getClass().getName() + ": " + ex.getMessage());
    }

    /**
     * By default, sets SC_METHOD_NOT_ALLOWED as response in Spring, so overwritten to
     * provide HTTP_OK and reasonable SOAP fault response.
     */
    protected void handleNonPostMethod(HttpServletRequest httpServletRequest,
                                       HttpServletResponse response,
                                       Object handler) throws Exception {
        writeErrorResponseWithMessage(response, "HTTP Method not allowed");
    }

    private void writeErrorResponseWithMessage(HttpServletResponse response, String message)
            throws IOException {
        String errorXml = String.format(
                 "<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\">%s</faultstring>"
                +"        </SOAP-ENV:Fault>"
                +"    </SOAP-ENV:Body>"
                +"</SOAP-ENV:Envelope>",
                StringEscapeUtils.escapeXml(message)
                );

        response.setStatus(HttpServletResponse.SC_OK);
        response.setContentType("text/xml");
        response.getWriter().write(errorXml);
        response.getWriter().flush();
    }
    @Override
    public int getOrder() {
        return 1;
    }

    /**
     * This class is needed as org.springframework.ws.transport.http.HttpServletConnection will throw an
     * exception if it is used outside Spring framework files. However, extending it and using the same
     * implementation seems to be fine.
     *
     */
    static class MyWebServiceConnection extends HttpServletConnection {
        protected MyWebServiceConnection(HttpServletRequest httpServletRequest,
                HttpServletResponse httpServletResponse) {
            super(httpServletRequest, httpServletResponse);
        }
    }    
}

これも正しく構成する必要があります。これは、春のコンテキストで必要なものです:

<!-- 'messageReceiverHandlerAdapter' is a magic name spring-ws
     org.springframework.ws.transport.http.MessageDispatcherServlet
     will bind to -->
<bean id="messageReceiverHandlerAdapter"
    class="fi.eis.applications.spring.soap.server.transport.http.MyWebServiceMessageReceiverHandlerAdapter">
</bean>
于 2013-09-26T09:21:25.320 に答える
2

私が思いついた解決策は、MessageDispatcherServlet の doService メソッドをオーバーライドし、例外をキャプチャしてから、カスタム レスポンスをレンダリングすることです。これはあなたにとって最善の解決策ではないかもしれませんが、私にとってはうまくいきます。

public class CustomMessageDispatcherServlet extends MessageDispatcherServlet {

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
    try {
        super.doService(request, response);
    } catch (Exception e) {
        String error = e.getMessage();
        String errorXml = "<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\"></faultstring>" + error +
                                        "</SOAP-ENV:Fault>" +
                                    "</SOAP-ENV:Body>" +
                                "</SOAP-ENV:Envelope>";
        response.setStatus(HttpServletResponse.SC_OK);
        response.setContentType("text/xml");
        response.getWriter().write(errorXml);
        response.getWriter().flush();
    }
}

}

処理したい例外だけをキャッチしたい場合があります。

web.xml ファイルで org.springframework.ws.transport.http.MessageDispatcherServlet を CustomMessageDispatcherServlet に置き換えます。

<servlet>
    <servlet-name>web-services</servlet-name>
    <servlet-class>CustomMessageDispatcherServlet</servlet-class>
</servlet>
于 2012-01-20T03:02:50.370 に答える
1

spring-security を使用しているときに、この種の問題に遭遇したことを覚えています。また、一部の条件下で例外リゾルバーが呼び出されないという問題もありました。

問題は、各リクエストを処理するフィルター チェーンがあることでした。例外リゾルバーを呼び出すコードは、このチェーンのフィルターですが、チェーンの終わりに近づいています。したがって、例外リゾルバーがフィルターを呼び出す前にフィルター内のどこかで例外が発生した場合、私のリゾルバーは決して呼び出されません。

ここでは、例外リゾルバーがフィルターを呼び出す前にエンベロープ解析エラーが発生するという、似たような問題に悩まされていると思います。その場合は、バニラ サーブレット フィルターなど、これらの例外を処理する別の方法に頼る必要があります。

于 2010-08-20T12:00:18.310 に答える
-1

Spring Bean で例外ハンドラーを「配線」(または「注入」) する必要があります。どの Spring Bean が実際に例外ハンドラーを必要としているかはわかりません。

個人的には、を使用しますdefault-autowire="byName"。これにより、例外ハンドラが Controller クラスに自動的に接続されます。あなたのアプローチは実際に手動配線を使用しています。そのため、どの Bean が実際に例外ハンドラーを使用する必要があるかを調べる必要があります。試しましたか(頭のてっぺん):

<bean class="org.springframework.ws.server.endpoint.adapter.MarshallingMethodEndpointAdapter">
    <constructor-arg ref="marshaller"/>
    <property name="exceptionHandler" ref="exceptionHandler" />
</bean>

または、Spring の自動配線メカニズムを追加して、Bean を自動的に配線させることもできます。:)

于 2010-08-18T15:07:06.543 に答える