我知道在StackOverflow上已经多次发布了类似的问题,但在我的情况下,所有答案都没有帮助。
目标:我需要先将SOAP-Request XML记录到数据库中,然后再进行处理。
对于这个讨论,如果我能把它作为一个字符串并将其记录到控制台,我很高兴。
问题:请求XML始终为空。
环境: IBM WebSphere Application Server v8.5,EJB 3.1 Web服务(会话bean)
我实际上使用 javax.xml.soap.SOAPBody 和 SOAPMessage 生成了一个有效的解决方案,但是在生产中似乎有另一个组件会导致以下JAX-WS冲突:
ERROR org.apache.axis2.engine.AxisEngine receive - com.sun.xml.messaging.saaj.soap.ver1_1.Body1_1Impl incompatible with com.ibm.ws.webservices.engine.xmlsoap.SOAPBody
是的,StackOverflow和IBM在改变ClassLoader策略方面有多种解决方法,比如“本地类加载器优先(父级最后一个)”,但我们目前不能这样做。
因此,我当前的方法(正在处理不同的servlet)是获取 HTTPServletRequest ,得到它的 InputStream 并使用 IOUtils.toString将其转换为String ()即可。
我知道请求只能被使用一次,我找到了几种方法来避免它(比如使用 HTTPServletRequestWrapper ),但即使使用这些变通办法,请求XML也总是空的。
最终我想在适配器中进行日志记录,但出于测试原因,我还将我的尝试放入服务本身(它没有任何效果)。
奇怪的是:我可以从请求中读取所有标题和属性(使用 request.getHeader()和 request.getAttribute()!只有正文本身是空白的!
我正在使用 SoapUI 测试应用程序 Request-XML:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ping="http://services.axonactive.com/wsdl/cet/pingservice-v1.0">
<soapenv:Header/>
<soapenv:Body>
<ping:PingPingIn/>
</soapenv:Body>
</soapenv:Envelope>
响应实际上是无关紧要的,但正确工作。
控制台输出:
[ch.zek.ecls.EclsPingServiceImpl]: Initialization successful.
EclsPingServiceImpl ping - start ping()...
EclsPingServiceImpl ping - Body:
EclsPingServiceImpl ping - body2:
EclsUtil printRequestData - [H] Accept-Encoding: gzip,deflate
EclsUtil printRequestData - [H] Content-Type: text/xml;charset=UTF-8
EclsUtil printRequestData - [H] SOAPAction: ""
EclsUtil printRequestData - [H] Content-Length: 254
EclsUtil printRequestData - [H] Host: localhost:9443
EclsUtil printRequestData - [H] Connection: Keep-Alive
EclsUtil printRequestData - [H] User-Agent: Apache-HttpClient/4.1.1 (java 1.5)
EclsUtil printRequestData - [A] javax.servlet.request.key_size: 128
EclsUtil printRequestData - [A] javax.servlet.request.cipher_suite: SSL_RSA_WITH_AES_128_CBC_SHA
EclsUtil printRequestData - [A] com.ibm.websphere.servlet.uri_non_decoded: /NewZekEclsHTTPRouter/PingService
EclsPingServiceImpl ping - end ping()
修改:请注意内容类型和内容长度的标题[H]:
他们“知道”内容 - 如果我在请求XML中添加更多字符,那么Content-Length会相应更新
所以我得出结论,内容不会丢失,但不知何故无法访问....
web服务:
public class EclsPingServiceImpl{
@javax.annotation.Resource
WebServiceContext wsContext;
@javax.annotation.Resource
SessionContext sessionContext;
private Log log = LogFactory.getLog(EclsPingServiceImpl.class);
public PingOut ping(PingIn parameters) throws PingEntityNotFoundException, PingPermissionException, PingSystemException {
MessageContext messageContext = wsContext.getMessageContext();
HttpServletRequest request = (HttpServletRequest) messageContext.get(MessageContext.SERVLET_REQUEST);
// start Try 1
MultiReadHttpServletRequest multiReadHttpServletRequest = new MultiReadHttpServletRequest(request);
try {
InputStream bodyInputStream = multiReadHttpServletRequest.getInputStream();
String body = IOUtils.toString(bodyInputStream);
if (log.isDebugEnabled()) {
log.debug("Body: " + body);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// end Try 1
// start Try 2
try {
InputStream body2 = request.getInputStream();
String xml = IOUtils.toString(body2, "UTF-8");
if (log.isDebugEnabled()) {
log.debug("body2: " + xml);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// end Try 2
// Get Header data:
Enumeration<String> headerNames = request.getHeaderNames();
while(headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
if (log.isDebugEnabled()) {
log.debug("[H] " + headerName + ": " + request.getHeader(headerName));
}
}
// Get Attribute data:
Enumeration<String> attributeNames = request.getAttributeNames();
while(attributeNames.hasMoreElements()) {
String attributeName = attributeNames.nextElement();
if (log.isDebugEnabled()) {
log.debug("[A] " + attributeName + ": " + request.getAttribute(attributeName));
}
}
PingOut pingOut = new PingOut();
// some irrelevant stuff...
return pingOut;
}
}
MultiReadHttpServletRequestWrapper:
从StackOverflow复制:Http Servlet request lose params from POST body after read it once
public class MultiReadHttpServletRequest extends HttpServletRequestWrapper {
private ByteArrayOutputStream cachedBytes;
public MultiReadHttpServletRequest(HttpServletRequest request) {
super(request);
}
@Override
public ServletInputStream getInputStream() throws IOException {
if (cachedBytes == null)
cacheInputStream();
return new CachedServletInputStream();
}
@Override
public BufferedReader getReader() throws IOException{
return new BufferedReader(new InputStreamReader(getInputStream()));
}
private void cacheInputStream() throws IOException {
/* Cache the inputstream in order to read it multiple times. For
* convenience, I use apache.commons IOUtils
*/
cachedBytes = new ByteArrayOutputStream();
IOUtils.copy(super.getInputStream(), cachedBytes);
}
/* An inputstream which reads the cached request body */
public class CachedServletInputStream extends ServletInputStream {
private ByteArrayInputStream input;
public CachedServletInputStream() {
/* create a new input stream from the cached request body */
input = new ByteArrayInputStream(cachedBytes.toByteArray());
}
@Override
public int read() throws IOException {
return input.read();
}
}
}
LogHandler - 只是为了显示我尝试的内容......:
public class EclsSimpleLogHandler implements javax.xml.ws.handler.soap.SOAPHandler
{
private Log log = LogFactory.getLog(EclsSimpleLogHandler.class);
@Override
public boolean handleMessage(MessageContext context) {
boolean success = false;
if (log.isDebugEnabled()) {
log.debug("handleMessage()...");
}
Boolean outboundProperty = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
// check if handler is called for inbound (request) or outbound (response) message
if (outboundProperty.booleanValue()) {
success = handleResponse(context);
} else {
//success = handleRequest(context);
success = true;
}
return success;
}
private boolean handleRequest(MessageContext messageContext) {
if(log.isDebugEnabled()) {
log.debug("handling request (inbound)");
}
// Initially logging planned here, moved out for testing reasons
boolean success = false;
return success;
}
private boolean handleResponse(MessageContext messageContext) {
if(log.isDebugEnabled()) {
log.debug("handling response (outbound)");
}
boolean success = false;
ByteArrayOutputStream outputStream = null;
SOAPMessageContext context = (SOAPMessageContext) messageContext;
SOAPMessage soapMessage = (SOAPMessage) context.getMessage();
try {
/*
Initial solution, but sometimes causing:
ERROR org.apache.axis2.engine.AxisEngine receive - com.sun.xml.messaging.saaj.soap.ver1_1.Body1_1Impl
incompatible with com.ibm.ws.webservices.engine.xmlsoap.SOAPBody
// SOAPBody soapBody = soapMessage.getSOAPBody();
// String soapBodyXml = soapBody.toString();
*/
// This is working - but I want to avoid using SOAPMessage:
outputStream = new ByteArrayOutputStream();
soapMessage.writeTo(outputStream);
String soapBodyXml = new String(outputStream.toByteArray(), "UTF-8");
if (log.isDebugEnabled()) {
log.debug("responseXml:\n" + soapBodyXml);
}
success = true;
} catch (SOAPException e) {
if (log.isErrorEnabled()) {
log.error("Error while accessing SOAPMessage: " + e.getMessage());
}
} catch (IOException e) {
if (log.isErrorEnabled()) {
log.error("IOException for soapMessage.writeTo(): " + e.getMessage());
}
e.printStackTrace();
}
return success;
}
// Another method, but also resulting in an empty body:
static String extractPostRequestBody(HttpServletRequest request) {
if ("POST".equalsIgnoreCase(request.getMethod())) {
Scanner s = null;
try {
s = new Scanner(request.getInputStream(), "UTF-8").useDelimiter("\\A");
} catch (IOException e) {
e.printStackTrace();
}
return s.hasNext() ? s.next() : "";
}
return "";
}
// Another method, but also resulting in an empty body:
private String getRequestBody(HttpServletRequest request) {
HttpServletRequestWrapper requestWrapper = new HttpServletRequestWrapper(request);
StringBuilder stringBuilder = new StringBuilder();
BufferedReader bufferedReader = null;
try {
InputStream inputStream = requestWrapper.getInputStream();
if (inputStream != null) {
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
char[] charBuffer = new char[128];
int bytesRead = -1;
while ((bytesRead = bufferedReader.read(charBuffer)) != -1) {
stringBuilder.append(charBuffer, 0, bytesRead);
}
}
} catch (IOException ex) {
log.error("Error reading the request payload", ex);
} finally {
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException iox) {
// ignore
}
}
}
return stringBuilder.toString();
}
}