gpt4 book ai didi

web-services - 如何从 Spring Boot Endpoint Service 返回自定义 SOAP 错误?

转载 作者:行者123 更新时间:2023-12-04 12:42:09 27 4
gpt4 key购买 nike

我已经设置了一个 Web 服务应用程序,它接收并只记录来自第三方的 SOAP 请求。记录后必须返回定义的响应。如果没有错误并且接收到的 SOAP 请求与 WSDL 匹配,这将毫无问题地工作。
不幸的是,当第三方发送无效内容甚至随机数据时,它也希望得到正确的 SOAP 响应。

如果请求包含随机数据(例如“zewrzasjkfklj”),我的服务将返回一个带有空正文的 HTTP/400 错误请求。
如果请求包含 XML 但不包含 Soap(例如“”),服务将返回带有 JSON 正文的 HTTP/500 服务器错误

{"timestamp":"2018-12-06T16:16:29.375+0000","status":500,"error":"Internal Server Error","message":"Could not create message from InputStream: Unable to create envelope from given source: ; nested exception is com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl: Unable to create envelope from given source: ","path":"/NotificationServicePort"}

这让我特别困惑,因为我在与 JSON 相关的项目中没有任何跟踪或配置。

端点是一个用@Endpoint 注释的类,它实现
...    @PayloadRoot(namespace = NAMESPACE_URI, localPart = "notify")
@ResponsePayload
public JAXBElement<NotifyResponse> notify(@RequestPayload Notify request) {
...}

(但在无效请求的情况下永远不会达到此方法)。

我已经尝试实现/提供拦截器、调度器、错误映射器……但结果没有改变。
似乎在后一种情况下(有效的 XML 但没有 SOAP),它在尝试在 SOAPPartImpl.lookForEnvelope() 处提取信封时失败并失败并显示
throw new SOAPExceptionImpl("无法从给定源创建信封,因为根元素未命名为\"信封\"");
该错误处的断点提供以下堆栈:
lookForEnvelope:153, SOAPPartImpl (com.sun.xml.internal.messaging.saaj.soap)
getEnvelope:121, SOAPPartImpl (com.sun.xml.internal.messaging.saaj.soap)
createEnvelope:110, EnvelopeFactory (com.sun.xml.internal.messaging.saaj.soap)
createEnvelopeFromSource:69, SOAPPart1_1Impl (com.sun.xml.internal.messaging.saaj.soap.ver1_1)
getEnvelope:128, SOAPPartImpl (com.sun.xml.internal.messaging.saaj.soap)
createWebServiceMessage:189, SaajSoapMessageFactory (org.springframework.ws.soap.saaj)
createWebServiceMessage:60, SaajSoapMessageFactory (org.springframework.ws.soap.saaj)
receive:92, AbstractWebServiceConnection (org.springframework.ws.transport)
handleConnection:87, WebServiceMessageReceiverObjectSupport (org.springframework.ws.transport.support)
handle:61, WebServiceMessageReceiverHandlerAdapter (org.springframework.ws.transport.http)
doService:293, MessageDispatcherServlet (org.springframework.ws.transport.http)
processRequest:974, FrameworkServlet (org.springframework.web.servlet)
doPost:877, FrameworkServlet (org.springframework.web.servlet)
service:661, HttpServlet (javax.servlet.http)
service:851, FrameworkServlet (org.springframework.web.servlet)
service:742, HttpServlet (javax.servlet.http)
internalDoFilter:231, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
doFilter:52, WsFilter (org.apache.tomcat.websocket.server)
internalDoFilter:193, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
doFilterInternal:246, AbstractRequestLoggingFilter (org.springframework.web.filter)
doFilter:107, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:193, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
filterAndRecordMetrics:158, WebMvcMetricsFilter (org.springframework.boot.actuate.metrics.web.servlet)
filterAndRecordMetrics:126, WebMvcMetricsFilter (org.springframework.boot.actuate.metrics.web.servlet)
doFilterInternal:111, WebMvcMetricsFilter (org.springframework.boot.actuate.metrics.web.servlet)
doFilter:107, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:193, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
doFilterInternal:90, HttpTraceFilter (org.springframework.boot.actuate.web.trace.servlet)
doFilter:107, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:193, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
doFilter:320, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
invoke:127, FilterSecurityInterceptor (org.springframework.security.web.access.intercept)
doFilter:91, FilterSecurityInterceptor (org.springframework.security.web.access.intercept)
doFilter:334, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:119, ExceptionTranslationFilter (org.springframework.security.web.access)
doFilter:334, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:137, SessionManagementFilter (org.springframework.security.web.session)
doFilter:334, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:111, AnonymousAuthenticationFilter (org.springframework.security.web.authentication)
doFilter:334, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:170, SecurityContextHolderAwareRequestFilter (org.springframework.security.web.servletapi)
doFilter:334, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:63, RequestCacheAwareFilter (org.springframework.security.web.savedrequest)
doFilter:334, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilterInternal:158, BasicAuthenticationFilter (org.springframework.security.web.authentication.www)
doFilter:107, OncePerRequestFilter (org.springframework.web.filter)
doFilter:334, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:116, LogoutFilter (org.springframework.security.web.authentication.logout)
doFilter:334, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilterInternal:66, HeaderWriterFilter (org.springframework.security.web.header)
doFilter:107, OncePerRequestFilter (org.springframework.web.filter)
doFilter:334, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilter:105, SecurityContextPersistenceFilter (org.springframework.security.web.context)
doFilter:334, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilterInternal:56, WebAsyncManagerIntegrationFilter (org.springframework.security.web.context.request.async)
doFilter:107, OncePerRequestFilter (org.springframework.web.filter)
doFilter:334, FilterChainProxy$VirtualFilterChain (org.springframework.security.web)
doFilterInternal:215, FilterChainProxy (org.springframework.security.web)
doFilter:178, FilterChainProxy (org.springframework.security.web)
invokeDelegate:357, DelegatingFilterProxy (org.springframework.web.filter)
doFilter:270, DelegatingFilterProxy (org.springframework.web.filter)
internalDoFilter:193, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
doFilterInternal:99, RequestContextFilter (org.springframework.web.filter)
doFilter:107, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:193, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
doFilterInternal:109, HttpPutFormContentFilter (org.springframework.web.filter)
doFilter:107, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:193, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
doFilterInternal:93, HiddenHttpMethodFilter (org.springframework.web.filter)
doFilter:107, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:193, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
doFilterInternal:200, CharacterEncodingFilter (org.springframework.web.filter)
doFilter:107, OncePerRequestFilter (org.springframework.web.filter)
internalDoFilter:193, ApplicationFilterChain (org.apache.catalina.core)
doFilter:166, ApplicationFilterChain (org.apache.catalina.core)
invoke:198, StandardWrapperValve (org.apache.catalina.core)
invoke:96, StandardContextValve (org.apache.catalina.core)
invoke:496, AuthenticatorBase (org.apache.catalina.authenticator)
invoke:140, StandardHostValve (org.apache.catalina.core)
invoke:81, ErrorReportValve (org.apache.catalina.valves)
invoke:87, StandardEngineValve (org.apache.catalina.core)
service:342, CoyoteAdapter (org.apache.catalina.connector)
service:803, Http11Processor (org.apache.coyote.http11)
process:66, AbstractProcessorLight (org.apache.coyote)
process:790, AbstractProtocol$ConnectionHandler (org.apache.coyote)
doRun:1468, NioEndpoint$SocketProcessor (org.apache.tomcat.util.net)
run:49, SocketProcessorBase (org.apache.tomcat.util.net)
runWorker:1149, ThreadPoolExecutor (java.util.concurrent)
run:624, ThreadPoolExecutor$Worker (java.util.concurrent)
run:61, TaskThread$WrappingRunnable (org.apache.tomcat.util.threads)
run:748, Thread (java.lang)

如果请求甚至没有到达 SOAP 处理逻辑,我将不胜感激任何提示或进一步的建议,我可以在其中找到有关如何设置默认 SOAP 响应(或具有 SOAP 消息文本内容的 HTML 响应)的更多信息.

最佳答案

最后,它必须是几个钩子(Hook)的组合,因为似乎没有一个可用的点或配置可以让与端点相关的所有错误/问题都通过并允许生成自定义响应。

以下是我最终想出的解决方案:

我可以捆绑生成自定义响应的主要位置是自定义的 MessageDispatcherServlet:

...
// this custom dispatcher is responsible for sending back a faked "SOAP" like response upon any type of
// misformatted request or error.
@Component
public class CustomSoapErrorMessageDispatcherServlet extends MessageDispatcherServlet {

@Override
protected void doService(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
throws Exception {
Exception thrownException = null;

try {
super.doService(httpServletRequest, httpServletResponse);
} catch (CustomSoapValidationException | SoapMessageCreationException e) {
LOG.warn("Processing resulted in exception: " + e.getMessage()); //
thrownException = e;
httpServletResponse.setStatus(400);
} catch (Exception e) {
LOG.warn("Processing resulted in generic exception: " + e.getMessage()); //
thrownException = e;
httpServletResponse.setStatus(500);
}

int responseStatus = httpServletResponse.getStatus();

// Response in HTTP OK Range? Do nothing.
if (responseStatus >= 200 && responseStatus <= 299) {
return;
}

/*
In any case of any error send a SOAP-like response.
*/
String errorCode, errorMessage;

// failure during SOAP interpretion? ie. XML received but not SOAP or invalid structure, ....
if(thrownException instanceof SoapMessageException) {
errorCode = "110";
errorMessage = "Generic SOAP Exception: " + thrownException.getMessage();
}
// did our structure validation fail?
else if (thrownException instanceof CustomSoapValidationException) {
errorCode = "110";
errorMessage = "Structure error in request: " + thrownException.getMessage();
}
// another exception unrelated to Soap Processing?
else if (thrownException != null) {
errorCode = "999";
errorMessage = "Internal error: " + thrownException.getMessage();
}
// generic internal error, but not throwing exception?
else if (responseStatus >= 400 && responseStatus <= 499) {
errorCode = String.valueOf(responseStatus);
errorMessage = "Generic unspecific request processing error.";
}
// something completely unexpected
else {
errorCode = "500";
errorMessage = "Unexpected condition.";
}

String responseBody = generateSoapErrorContent(errorCode, errorMessage);
ServletOutputStream outputStream = httpServletResponse.getOutputStream();
outputStream.print(responseBody);
outputStream.flush();
}
...
}
...

我通过我的配置类激活
...
@Autowired
private CustomSoapErrorMessageDispatcherServlet dispatcherServlet;

@Bean
public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
dispatcherServlet.setApplicationContext(applicationContext);
dispatcherServlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean(dispatcherServlet, "/NotificationServicePort/*");
}
...

仅此自定义调度程序就只能捕获包含(有效和无效)XML 但不完全是 SOAP 的请求或包含随机数据的请求。为了同时覆盖无效的 SOAP 请求,还需要一些更多的步骤。

首先是一个自定义拦截器,它执行模式验证并抛出一个自定义异常(而不是立即响应像 PayloadValidatingInterceptor 这样的 SOAP 错误):
...
public class CustomValidatingInterceptor extends PayloadValidatingInterceptor {

@Override
protected boolean handleRequestValidationErrors(MessageContext messageContext, SAXParseException[] errors)
throws TransformerException {

// if any validation errors, convert them to a string and throw on as Exception to be handled by CustomSoapErrorMessageDispatcherServlet
if (errors.length > 0) {
String validationErrorsString = Arrays.stream(errors)
.map(error -> "[" + error.getLineNumber() + "," + error.getColumnNumber() + "]: " + error.getMessage())
.collect(Collectors.joining(" -- "));
throw new CustomSoapValidationException("Validation Errors: " + validationErrorsString);
}
return true;
}
}
...

它在我的配置类中配置(现在必须从 WsConfigurerAdapter 扩展)通过
...
public class WebServiceConfig extends WsConfigurerAdapter {
...
@Override
public void addInterceptors(List<EndpointInterceptor> interceptors) {
// validate requests and responses
// cannot use PayloadValidatingInterceptor because that one would generate an unwanted/unavoidable SoapFault
CustomValidatingInterceptor validatingInterceptor = new CustomValidatingInterceptor();
validatingInterceptor.setValidateRequest(true);
validatingInterceptor.setValidateResponse(false);
validatingInterceptor.setXsdSchema(customApiSchema());
interceptors.add(validatingInterceptor);
}
...

其次,现在抛出的 CustomSoapValidationException 仍然会在端点解析逻辑中导致标准的 SOAP 错误,这就是我们还创建自定义 EndpointExceptionResolver 的原因。这在异常处理期间被调用,并将我们的拦截器验证错误再次修改为“实时”异常,然后可以从第一步将调用堆栈弹回到我们的 CustomSoapErrorMessageDispatcherServlet 中。
...
// class is automatically picked up by MessageDispatcher during request handling when an exception occurs after dispatching
@Component
public class CustomizedSoapFaultDefinitionExceptionResolver implements EndpointExceptionResolver {
public boolean resolveException(MessageContext messageContext, Object endpoint, Exception ex) {
if (ex instanceof CustomSoapValidationException) {
throw (CustomSoapValidationException) ex;
}
return false;
}
}
...

这不需要额外的配置,但现在由 Spring Boot MessageDispatcher 自动获取。

所有这些步骤都发生了错误/异常/失败/......最终在我们的 CustomSoapErrorMessageDispatcherServlet.doService() 中以一种或另一种方式结束,我们在那里获取异常或调查尚未发送的 HttpServletResponse 并可以构建自定义 SOAP-寻找回应来满足我们的要求。

关于web-services - 如何从 Spring Boot Endpoint Service 返回自定义 SOAP 错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53665662/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com