My following Javascript function uses the Node.js Library "soap" to generate and send an XML SOAP request:
async function handle(req, res) {
console.log('details...');
soap.createClientAsync(API.bahn.search, {wsdl_headers: {Authorization: BahnCreds.basic.hash()}}).then(client => {
client.setSecurity(new soap.BasicAuthSecurity(BahnCreds.basic.user, BahnCreds.basic.pwd));
client.addHttpHeader('Authorization', BahnCreds.basic.hash());
// console.debug(client.describe()); // Methodenliste
const args = {
RequestData: {
attributes: {
ot: fns.format(new Date(), 'yyyyMMddhhmmss'),
},
'WL5G3N3:TariffIdentificationH': {
attributes: {
t: req.body.ticketid,
c: req.body.class,
}
},
'WL5G3N3:TravelerList': {
Traveler: {
attributes: {
age: 35
}
}
},
'WL5G3N3:ScheduleH': {
attributes: {
sid: req.body.sid,
dt: req.body.date,
},
'WL5G3N3:TrainList': {
'WL5G3N3:Train': req.body.trains.map(train => {
return {attributes: train};
})
}
},
},
Header: header.get(),
};
console.log(JSON.stringify(args));
const callback = (error, result) => {
console.debug(client.lastRequest);
if(!error) {
return res.json({
result: result,
type: 'success',
})
}
return res.json(Object.assign({},
Errors.ERR__FEHLER_BEI_ABFRAGE,
{details: error}));
};
return client
.WSDReiseAuskunftInterface
.WSDReiseAuskunftPort
.Angebotsdetails(args, callback);
});
}
The generated XML looks like this:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:WL5G3N1="http://www.bahn.de/webservices/dritte/angebotssuche/request"
xmlns:WL5G3N2="http://www.bahn.de/webservices/dritte/angebotssuche/response"
xmlns:WL5G3N3="http://www.bahn.de/webservices/dritte/angebotsdetails/request"
xmlns:WL5G3N4="http://www.bahn.de/webservices/dritte/angebotsdetails/response"
xmlns:WL5G3N5="http://www.bahn.de/webservices/dritte/ping/request"
xmlns:WL5G3N6="http://www.bahn.de/webservices/dritte/ping/response"
xmlns:WL5G3N7="http://www.bahn.de/webservices/dritte/monitoring/request"
xmlns:WL5G3N8="http://www.bahn.de/webservices/dritte/monitoring/response"
xmlns:WL5G3N9="http://www.bahn.de/webservices/dritte"
xmlns:adreq="http://www.bahn.de/webservices/dritte/angebotsdetails/request"
xmlns:adres="http://www.bahn.de/webservices/dritte/angebotsdetails/response"
xmlns:asreq="http://www.bahn.de/webservices/dritte/angebotssuche/request"
xmlns:asres="http://www.bahn.de/webservices/dritte/angebotssuche/response"
xmlns:monreq="http://www.bahn.de/webservices/dritte/monitoring/request"
xmlns:monres="http://www.bahn.de/webservices/dritte/monitoring/response"
xmlns:pingreq="http://www.bahn.de/webservices/dritte/ping/request"
xmlns:pingres="http://www.bahn.de/webservices/dritte/ping/response">
<soap:Header></soap:Header>
<soap:Body>
<WL5G3N3:AngebotsdetailsRequest xmlns:WL5G3N3="http://www.bahn.de/webservices/dritte/angebotsdetails/request"
xmlns="http://www.bahn.de/webservices/dritte/angebotsdetails/request">
<WL5G3N3:RequestData ot="20201202052152">
<WL5G3N3:TariffIdentificationH t="TCK#13#0#0#0#S2#6290#" c="2"></WL5G3N3:TariffIdentificationH>
<WL5G3N3:TravelerList>
<db:Traveler age="35"
xmlns:db="http://www.bahn.de/webservices/dritte/datatypes/db"></db:Traveler>
</WL5G3N3:TravelerList>
<WL5G3N3:ScheduleH sid="0" dt="20201204">
<WL5G3N3:TrainList>
<ns1:Train arr="00d00:36:00" d="8000207" dep="00d00:14:00" dn="Köln Hbf" lt="Transport" s="8000085" sn="Düsseldorf Hbf" tid="0.0" tn="ICE 842"></ns1:Train>
<ns1:Train arr="00d01:25:00" d="8000044" dep="00d00:56:00" dn="Bonn Hbf" lt="Transport" s="8000207" sn="Köln Hbf" tid="0.1" tn="RB 25401"></ns1:Train>
</WL5G3N3:TrainList>
</WL5G3N3:ScheduleH>
</WL5G3N3:RequestData>
<WL5G3N3:Header l="DE" t="" us="" pwd="" sig="" tnr=""></WL5G3N3:Header>
</WL5G3N3:AngebotsdetailsRequest>
</soap:Body>
</soap:Envelope>
This request results in an error, because WL5G3N3:Train
has changed to ns1:Train
. I think, it's related to the fact that WL5G3N3:Train
is an array.
My question now is: why does this happen? Does anyone know, how I've to configure SOAP that arrays won't lose their namespace?
The solution is to explicitly override the namespace after the client is created.
client.wsdl.definitions.xmlns.ns1 = http://your-namespace-url...;
client.wsdl.xmlnsInEnvelope = client.wsdl._xmlnsMap()
Then we can also get rid of the hardcoded namespaces. The function now looks like this:
async function handle(req, res) {
console.log('details...');
soap.createClientAsync(API.bahn.search, {wsdl_headers: {Authorization: BahnCreds.basic.hash()}}).then(client => {
client.setSecurity(new soap.BasicAuthSecurity(BahnCreds.basic.user, BahnCreds.basic.pwd));
client.addHttpHeader('Authorization', BahnCreds.basic.hash());
// THIS WAS/IS IMPORTANT
client.wsdl.definitions.xmlns.ns1 = API.bahn.ns.details;
client.wsdl.xmlnsInEnvelope = client.wsdl._xmlnsMap()
const args = {
RequestData: {
attributes: {
ot: fns.format(new Date(), 'yyyyMMddhhmmss'),
},
TariffIdentificationH: {
attributes: {
t: req.body.ticketid,
c: req.body.class,
}
},
TravelerList: {
Traveler: {
attributes: {
age: 35
}
}
},
ScheduleH: {
attributes: {
sid: req.body.sid,
dt: req.body.date,
},
TrainList: {
Train: req.body.trains
}
},
},
Header: header.get(),
};
const callback = (error, result) => {
if(!error) {
return res.json({
result: result,
type: 'success',
})
}
return res.json(Object.assign({},
Errors.ERR__FEHLER_BEI_ABFRAGE,
{details: error}));
};
return client
.WSDReiseAuskunftInterface
.WSDReiseAuskunftPort
.Angebotsdetails(args, callback);
});
}