Spring integration: Using cusom header for http:outbound-channel-adapter - rest

I have a situation to use a <int-http:outbound-channel-adapter ... /> to send an object with information stored in the header.
Following works when I call the <int-http:inbound-channel-adapter ... /> like follows:
public void openTicket(final Profile profile, final Ticket ticket) {
final HttpHeaders headers = new HttpHeaders();
headers.set("profile", profile.toString());
final HttpEntity<Ticket> entity = new HttpEntity<Ticket>(ticket, headers);
template.exchange(URL, HttpMethod.PUT, entity, Ticket.class);
}
This calls my inboung-channel-adapter successful with the given profile in the headers:
<int-http:inbound-channel-adapter
channel="api_app_integration_request_channel"
supported-methods="PUT"
path="/process/ticket"
request-payload-type="*.model.Ticket"
mapped-request-headers="profile"
error-channel="internal-client-rest-ticket-error-channel"
>
<int-http:request-mapping consumes="application/json" />
</int-http:inbound-channel-adapter>
What doesnt work is calling the service via outbound-channel-adapter, the call itself works, but my header 'profile' is gone.
<int-http:outbound-channel-adapter
channel="client_rest_ticket_outbound_channel"
http-method="PUT"
url="http://localhost:8080/process/ticket"
mapped-request-headers="profile"
/>
I am using Spring-Boot 1.3.6.RELEASE.

Custom headers are (currently) mapped with an X- prefix by default; to map them without the prefix you need to wire up a DefaultHttpHeaderMapper with userDefinedHeaderPrefix set to null (or "") as well as the outbound header name(s) you want to map.
See the documentation.
EDIT:
<bean class="org.springframework.integration.http.support.DefaultHttpHeaderMapper" id="headerMapper"
p:userDefinedHeaderPrefix=""
p:inboundHeaderNames="profile"
p:outboundHeaderNames="profile"
/>

Related

Owin - Initialize IdP using metadata file/url

I want to use Owin based SAML2 authentication without "hardcoding" any data. I have metadata file from IdP owner. Is there any way, how to just load this file (or point to url with metadata) and let provider initialize itself?
public void ConfigureAuth(IAppBuilder app)
{
...
app.UseSaml2Authentication(CreateSaml2Options());
}
private static Saml2AuthenticationOptions CreateSaml2Options()
{
var spOptions = CreateSpOptions();
var saml2Options = new Saml2AuthenticationOptions(false)
{
SPOptions = spOptions
};
var idp = new IdentityProvider(new EntityId("XXXXXXXXX"), spOptions)
{
AllowUnsolicitedAuthnResponse = true,
Binding = Saml2BindingType.HttpPost,
SingleSignOnServiceUrl = new Uri("XXXXXXXXX")
};
saml2Options.IdentityProviders.Add(idp);
return saml2Options;
}
How to get XXXXXXXXX values from metadata?
The XXX is the EntityId, it is listed at the top of the metadata. Consider e.g. this metadata excerpt from stubidp.sustainsys.com:
<EntityDescriptor xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"
xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
ID="_a1f8bba0-9d9f-4107-bbed-b61cd3d9c67f"
entityID="https://stubidp.sustainsys.com/Metadata" cacheDuration="PT15M"
validUntil="2018-12-19T18:51:08Z">
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
The XXX value you are looking for is the entityID attribute. In this case it is https://stubidp.sustainsys.com/Metadata.
In the current version of the library, it is unfortunately not possible to get the EntityID from the metadata. I'm planning to fix that in a future version but it requires quite a lot of work. The reason is that the EntityID is the key in a dictionary and things would get very confusing it would be lazily initialized or changed later.

node-soap adding namespace to envelope

I am trying to consume this soap service: http://testws.truckstop.com:8080/v13/Posting/LoadPosting.svc?singleWsdl with node-soap, but the client is mangling the namespaces and I have been unable to find a working solution.
I believe the answer is to either add a namespace to the soap envelope, or overwrite the soap envelope.
Using Soap UI, the request should look like:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:v11="http://webservices.truckstop.com/v11"
xmlns:web="http://schemas.datacontract.org/2004/07/WebServices">
<soapenv:Header/>
<soapenv:Body>
<v11:GetLoads>
<v11:listRequest>
<web:IntegrationId>integrationId</web:IntegrationId>
<web:Password>password</web:Password>
<web:UserName>username</web:UserName>
</v11:listRequest>
</v11:GetLoads>
</soapenv:Body>
</soapenv:Envelope>
However, when I do:
client = soap.createClient(url);
let query = {
listRequest: {
Password: password,
UserName: username,
IntegrationId: integrationId
}
};
let results = client.GetLoads(query);
The client generates this xml:
<?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:msc="http://schemas.microsoft.com/ws/2005/12/wsdl/contract"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:tns="http://webservices.truckstop.com/v11"
xmlns:q1="http://schemas.datacontract.org/2004/07/WebServices.Posting"
xmlns:q2="http://schemas.datacontract.org/2004/07/WebServices.Objects"
xmlns:q3="http://schemas.datacontract.org/2004/07/WebServices.Posting"
xmlns:q4="http://schemas.datacontract.org/2004/07/WebServices.Objects"
xmlns:q5="http://schemas.datacontract.org/2004/07/WebServices.Posting"
xmlns:q6="http://schemas.datacontract.org/2004/07/WebServices.Objects"
xmlns:q7="http://schemas.datacontract.org/2004/07/WebServices.Posting"
xmlns:q8="http://schemas.datacontract.org/2004/07/WebServices.Objects"
xmlns:q9="http://schemas.datacontract.org/2004/07/WebServices.Posting"
xmlns:q10="http://schemas.datacontract.org/2004/07/WebServices.Objects"
xmlns:q11="http://schemas.datacontract.org/2004/07/WebServices.Posting"
xmlns:q12="http://schemas.datacontract.org/2004/07/WebServices.Objects">
<soap:Body>
<GetLoads xmlns="http://webservices.truckstop.com/v11">
<listRequest>
<ns1:IntegrationId>integrationId</ns1:IntegrationId>
<ns1:Password>password</ns1:Password>
<ns1:UserName>usernam</ns1:UserName>
</listRequest>
</GetLoads>
</soap:Body>
</soap:Envelope>
This fails because IntegrationId, Password and UserName need http://schemas.datacontract.org/2004/07/WebServices, but the namespace isn't referenced in the envelope.
I've tried updating the client to add the namespace as suggested here:
client.wsdl.definitions.xmlns.ns1 = "http://schemas.datacontract.org/2004/07/WebServices";
client.wsdl.xmlnInEnvelope = client.wsdl._xmlnsMap();
I can see the namespace in client.wsdl.xmlnInEnvelope, but it doesn't seem to change the actual generated xml.
Is there another step required to refresh the client to use the updated envelope?
I also tried overriding the root element as shown here:
var wsdlOptions = {
//namespaceArrayElements: "xmlns:ns1=http://schemas.datacontract.org/2004/07/WebServices"
"overrideRootElement": {
"namespace": "xmlns:tns",
"xmlnsAttributes": [{
"name": "xmlns:tns",
"value": "http://webservices.truckstop.com/v11"
}, {
"name": "xmlns:ns1",
"value": "http://schemas.datacontract.org/2004/07/WebServices"
}]
}
};
this.loadPostClient = soap.createClient(this.tsConfig.loadPostUrl, wsdlOptions);
This changes the root body element:
<soap:Body>
<xmlns:tns:GetLoads
xmlns:tns="http://webservices.truckstop.com/v11"
xmlns:ns1="http://schemas.datacontract.org/2004/07/WebServices">
<listRequest>
<ns1:IntegrationId>integrationId</ns1:IntegrationId>
<ns1:Password>password</ns1:Password>
<ns1:UserName>username</ns1:UserName>
</listRequest>
</xmlns:tns:GetLoads>
</soap:Body>
But the remote server doesn't understand.
Thank you for reading!
This answer was correct all along
It wasn't working for me due to autocomplete and similar fields
client.wsdl.xmlnInEnvelope = client.wsdl._xmlnsMap();
Should have been:
client.wsdl.xmlnsInEnvelope = client.wsdl._xmlnsMap();
I left out an s and was setting xmlnInEnvelope instead of xmlnsInEvelope
It's been a few years, but I ran into a similar need of adding custom attributes to the soap envelope and wanted to give an alternative.
As of this writing, that _xmlnsMap() is a private method on the WSDL class so you can use it at your own risk. I always take private methods as subject to change from the developer without any notice to the library consumers so I wanted to find another way and turns out its possible.
TL;DR - Create your own WSDL class instance and pass it to your own Client class instance.
Use the open_wsdl method to bring in your WSDL
Use the callback to build your own custom attributes in a concatenated string.
Assign the attributes to the public xmlnsInEnvelope property.
return the updated WSDL instance (I used a promise).
const fetchWSDL = new Promise<WSDL>((resolve, reject) => {
// method that returns a WSDL instance from a url/file path
open_wsdl(this.wsdl, (err: any, wsdl?: WSDL) => {
// Build custom attributes
if (wsdl && wsdl.definitions.xmlns) {
const xmlns: { [key: string]: string } = {
[your namespaces]: 'values',
};
// turn your custom attributes map into a single concatenated string
let str = '';
for (const alias in xmlns) {
const ns = xmlns[alias];
str += ' xmlns:' + alias + '="' + ns + '"';
}
// Leverage public attribute on WSDL instance to apply our custom attributes
wsdl.xmlnsInEnvelope = str;
resolve(wsdl);
}
reject(err);
});
});
Use the updated WSDL instance to create your own client.
NOTE: the createClient method is just a convenience wrapper for creating a WSDL instance and returning a new Client instance.
const ModifiedWSDL = await fetchWSDL;
// Create client with our modified WSDL instance
this.client = new Client(ModifiedWSDL)
// adjust your Client instance as needed
A bit more code that the OP, but hopefully more in line with node-soap types and safer to use if you plan to upgrade.

Error "Cannot set null or blank methods on request." in WCF CustomBehavior

I'm working on a WCF CustomBehavior that plugs into a BizTalk 2010 SendPort in order to call a JSON/REST service. The SendPort is WCF/Custom with Binding Type=webHttpBinding.
I need to add the four headers to the request.
When I include this code in my BeforeSendRequest method:
var newHttpRequestMessageProperty = new HttpRequestMessageProperty
{
Method = request.Headers.Action,
QueryString = string.Empty,
SuppressEntityBody = false,
};
newHttpRequestMessageProperty.Headers.Add("X-ST-PartnerID", partnerIDFromSSO);
newHttpRequestMessageProperty.Headers.Add("X-ST-Token", tokenIDFromSSO);
newHttpRequestMessageProperty.Headers.Add("X-ST-AuthType", "TOKEN");
newHttpRequestMessageProperty.Headers.Add("Accept-Language", "EN");
request.Properties[HttpRequestMessageProperty.Name] = newHttpRequestMessageProperty;
It gives me this error:
System.ArgumentException: Cannot set null or blank methods on request.
Parameter name: value
Server stack trace:
at System.Net.HttpWebRequest.set_Method(String value)
at System.ServiceModel.Channels.HttpOutput.WebRequestHttpOutput.PrepareHttpSend(Message message)
at System.ServiceModel.Channels.HttpOutput.BeginSendCore(HttpResponseMessage httpResponseMessage, TimeSpan timeout, AsyncCallback callback, Object state)
at System.ServiceModel.Channels.HttpChannelFactory`1.HttpRequestChannel.HttpChannelAsyncRequest.SendWebRequest()
at System.ServiceModel.Channels.HttpChannelFactory`1.HttpRequestChannel.HttpChannelAsyncRequest.OnGetWebRequestCompleted(IAsyncResult result)
at System.ServiceModel.Channels.HttpChannelFactory`1.HttpRequestChannel.HttpChannelAsyncRequest.BeginSendRequest(Message message, TimeSpan timeout)
at System.ServiceModel.Channels.RequestChannel.BeginRequest(Message message, TimeSpan timeout, AsyncCallback callback, Object state)
at System.ServiceModel.Dispatcher
When I leave that code out, I get a 415 back from the partner's web service, so I know that I'm getting there at least.
What I'm doing is similar to this post: How to add a custom HTTP header to every WCF call?
From a co-worker, I changed:
//Method = request.Headers.Action,
Method = "POST",
and it worked...
The reason was, it turns out that when you have an invalid "OperationName", then request.Headers.Action had the entire CustomAction string from the BizTalk sendport:
<BtsActionMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Operation Name="DELETE" Action="DELETE" />
<Operation Name="GET" Action="GET" />
<Operation Name="POST" Action="POST" />
</BtsActionMapping>
I'm modeling my code after sample here:
https://biztalkrest.codeplex.com/discussions/657185
After the above fix, it dawned on me to match the "Operation Name" in the orchestration's logical port, and sure enough, I had "Post" instead of "POST".
Ughhh... So apparently when the OperationName doesn't match the CustomAction operations, BizTalk just sends the entire CustomAction XML block on through... Amazing...
In picture below is where I changed from "Post" to "POST".

How to set url encoded form entity and add params to form entity in rest assured?

The below sample code is in http client , But I want to write the same in Rest Assured. I know we can use the http lib in rest assured as well, But I want to have in Rest assured
HttpPost pst = new HttpPost(baseUrl, "j_spring_security_check"))
pst.setHeader("Content-Type", "application/x-www-form-urlencoded")
ArrayList<NameValuePair> postParam = new ArrayList<NameValuePair>()
postParam .add(new BasicNameValuePair("j_username",username))
postParam .add(new BasicNameValuePair("j_password",password))
UrlEncodedFormEntity formEntity23 = new UrlEncodedFormEntity(postParam)
pst.setEntity(formEntity23 )
HttpResponse response = httpclient.execute(pst);
For Rest Assured you can use below code snippet.
Response response = RestAssured
.given()
.header("Content-Type", "application/x-www-form-urlencoded")
.formParam("j_username", "uName")
.formParam("j_password", "pwd")
.request()
.post(url);
As, your application is using form url-encoded content type you can set the Header type to this as mentioned above.
Hope, this helps you.
#Test
public void postRequestWithPayload_asFormData() {
given().contentType(ContentType.URLENC.withCharset("UTF-8")).formParam("foo1", "bar1").formParam("foo2", "bar2").log().all()
.post("https://postman-echo.com/post").then().log().all().statusCode(200)
.body("form.foo1", equalTo("bar1"));
}
Add content type of URLENC with charaset as UTF-8. It works will latest rest assured.

SpringWS - Logging SoapRequest and SoapResponse in a table

I have a SpringWS inplementation with below enpoint implementation
#PayloadRoot(namespace="http://college.com/schema/get_XML_Request/v2",localPart="get_XML_Request")
#ResponsePayload
public JAXBElement<GetStudentResponseType> handleStudentXML(#RequestPayload JAXBElement<GetStudentXMLRequestType> SoapRequest)throws Exception
{
String xmlResponse = "";
com.college.get_student_xml_response.v2.ObjectFactory objectFactory = new com.company.schema.get_student_xml_response.v2.ObjectFactory();
com.college.schema.get_student_xml_response.v2.GetResponseType resType = objectFactory.createGetResponseType();
return objectFactory.createGetStudentResponse(resType);
}
Here my objective is to log the request which coming to my webservice and response which the web service sent back in a table. Is it possible to get the SoapRequest/Soapresponse (In Soapformat) from the above method as a String.Here am able to get the payload, but i need to log with entire SoapRequest(with soapenvelope,body) Please anyone advice on this.
Have a look at the SoapEnvelopeLoggingInterceptor which logs the whole SOAP
Envelope including headers. So basically you can extend it to add the saving to the database functionality.