Sending XML Strings As POST Params - java

I'm building a service that sends a POST request to UPS track package API. In PostMan, this call returns a 200 with this URL and these POST params
URL: https://onlinetools.ups.com/ups.app/xml/Track
Body: form-data
Params:
UPSSecurity
<?xml version="1.0"?> <AccessRequest xml:lang="en-US"> <AccessLicenseNumber>REDACTED</AccessLicenseNumber> <UserId>REDACTED</UserId> <Password>REDACTED</Password> </AccessRequest>
Body
<?xml version="1.0"?> <TrackRequest xml:lang="en-US"> <Request> <TransactionReference> <CustomerContext>Your Test Case Summary Description</CustomerContext> <XpciVersion>1.0</XpciVersion> </TransactionReference> <RequestAction>Track</RequestAction> <RequestOption>activity</RequestOption> </Request> <TrackingNumber>REDACTED</TrackingNumber> </TrackRequest>
Our application defines clients using OpenFeign and Spring annotations. The solution I'm going to try is to define my FeignClient like this:
interface UPSClient {
#Headers({"SOAPAction: http://onlinetools.ups.com/webservices/TrackBinding/v2.0","Content-Type: application/xml"})
#PostMapping(
value = "/Track",
consumes = MediaType.TEXT_XML_VALUE,
produces = MediaType.TEXT_XML_VALUE
)
ResponseEntity<TrackResponse> getTrackResponse(#RequestBody UPSRequest upsRequest);
and define MyRequestObject as
public class UPSRequest {
String UPSSecurity; // UPSSecurity is a POJO generated from UPS wsdl and marshalled into an XML String
String Body; // Body is a POJO generated from UPS wsdl and marshalled into an XML String
}
Is there a cleaner approach that anyone could recommend? The TrackResponse returned from the UPS endpoint can be handled by a custom SOAPDecoder and shouldn't be an issue

Related

Return Data - Java

I have a front end that gives me a query and a type.
The types are JSON and XML. How can I create and send the download file to the ajax post?
#RestController
public class RestCtrl {
#Autowired
JdbcTemplate jdbcTemplate;
#GetMapping("/getData")
List<Map<String, Object>> getData(#RequestParam String type, #RequestParam String query) {
System.out.println(type + " " + query);
List<Map<String, Object>> data = jdbcTemplate.queryForList(query);
if(type.equals("JSON")) {
}
else if (type.equals("XML")){
}
}
}
Basically the XML would have the column names in <> for example <TITLE>This is a title</TITLE> I this is is SOAP, right?
Something like this: https://flowgear-wpengine.netdna-ssl.com/wp-content/uploads/2016/12/XML-vs-JSON1.png
Edit:nothing like that
From the Spring Web documentation:
https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-requestmapping-produces
You can narrow the request mapping based on the Accept request header
and the list of content types that a controller method produces.
https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-requestmapping-consumes
You can narrow the request mapping based on the Content-Type of the
request.
Request
Client application can control POST request data mime format (XML, JSON) using Content-Type HTTP header. For example:
Content-Type: application/json will define that request data is provided in the JSON format
Content-Type: text/xml will define that request data is provided in the XML format
The server-side application can control what mime format it can process using consumes attribute of the #PostMapping annotation:
#PostMapping(path = "/pets", consumes = "application/json")
public void addPet(#RequestBody Pet pet) {
// ...
}
If 'Content-Type' header value doesn't match the consumer attribute value, Spring will produce an exception that it can process input data.
Response
Client application can control response data mime format (XML, JSON) using Accept HTTP header. For example:
Accept: application/json will define that the client can accept data is provided in the JSON format
Accept: text/xml will define that the client can accept data in the XML format
The server-side application can control what mime format it can process using produces attribute of the #RequestMapping, #GetMapping and other annotations:
#GetMapping(path = "/pets/{petId}", produces = "application/json;charset=UTF-8")
#ResponseBody
public Pet getPet(#PathVariable String petId) {
// ...
}
If 'Accept' header value doesn't match the produces attribute value, Spring will produce an exception that it can process input data.
Spring defined useful constants for the widely used mime-types:
MediaType.APPLICATION_XML_VALUE
MediaType.APPLICATION_JSON_VALUE
For the full list of values, check documentation: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/MediaType.html
consumes and produces are multi-valued attributes. Multiple values can be specified as a list:
#GetMapping(path = "/pets/{petId}", produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
You don't have to worry about the returned media type, spring will handle that for you, you need to add 'produces' to your rest endpoint
#RequestMapping(value="getData", method = RequestMethod.GET , produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
List<Map<String, Object>> getData(#RequestParam String type, #RequestParam String query) {
you may need to add jackson dataformat for an implicit POJO to XML mapping
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>

Spring MVC Override Received Content Type

I'm working on a Spring MVC application and have a client that I have no control over. This client is POSTing JSON data but transmitting a application/x-www-form-urlencoded header. Spring naturally trusts this header and tries to receive the data but can't because its JSON. Has anyone had experience overriding the header that Spring receives or just specifying exactly what type of data is coming, regardless of the headers?
You can do two things;
Change the client to send the Content-Type:
application/json header
Write a Servlet Filter or Spring Interceptor which is on top of the Spring Controller and checks for the header Content-Type. If it is not application/json then it changes it to application/json.
Why don't you write a separate controller to handle application/x-www-form-urlencoded requests. If the request is a valid JSON, then you can parse it and forward it to to appropriate service.
This way you can also handle a case in future where you get request of same type which is not a valid JSON.
#RequestMapping(value = "/handleURLEncoded", method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public #ResponseBody Object handleURLEncoded(HttpEntity<String> httpEntity) {
String json = httpEntity.getBody();
//now you have your request as a String
//you can manipulate it in any way
if(isJSONValid(json)) {
JSONObject jsonObj = new JSONObject(json);
//forward request or call service directly from here
//...
}
//other cases where it's not a valid JSON
}
Note: isJSONValid() method copied from this answer

Unable to set SOAP Header while calling Web Service through Camel using dataFormat as POJO

I am using Camel in our project and requesting WebServices, the dataFormat is POJO. I was able to request when my SOAP message did not contain SOAP headers, but when it had Headers, I was unable to set those. I looked at the documentation but was not able to understand and have several questions.
I want to create a message like the below:
<soapenv:Envelope`enter code here`
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Header>
<platformMsgs:documentInfo
xmlns:platformMsgs="urn:messages_2015_1.platform.webservices.netsuite.com">
<platformMsgs:nsId>WEBSERVICES_3479023</platformMsgs:nsId>
</platformMsgs:documentInfo>
</soapenv:Header>
<soapenv:Body>
<addListResponse
xmlns="">
<platformMsgs:writeResponseList
xmlns:platformMsgs="urn:messages_2015_1.platform.webservices.netsuite.com">
<platformCore:status isSuccess="true"
xmlns:platformCore="urn:core_2015_1.platform.webservices.netsuite.com"/>
<platformMsgs:writeResponse>
<platformCore:status isSuccess="false"
xmlns:platformCore="urn:core_2015_1.platform.webservices.netsuite.com">
<platformCore:statusDetail type="ERROR">
<platformCore:code>DUP_ENTITY</platformCore:code>
<platformCore:message>This entity already exists.</platformCore:message>
</platformCore:statusDetail>
</platformCore:status>
</platformMsgs:writeResponse>
</platformMsgs:writeResponseList>
</addListResponse>`enter code here`
</soapenv:Body>
</soapenv:Envelope>
I will be able to send the message if there was only Body, but can someone give me a code snippet for including the header section? The dataFormat is POJO.
When using CXF endpoint with dataFormat as POJO, body in Camel Exchange object is an object of org.apache.cxf.message.MessageContentsList. It is an extension of java.util.ArrayList<Object> and it contains parts of SOAP Message in order as defined in WSDL and corresponding method in WebService class.
Element 0 there is a Body.
So, one way to do that with Java is to create a Processor class implementing org.apache.camel.Processor interface and in its process method set your SOAP header. Something like:
#Override
public void process(Exchange camelExchange) throws Exception {
MessageContentsList messageBody = (MessageContentsList) camelExchange.getIn().getBody();
DocumentInfo docInfoHeader = new DocumentInfo();
... set docInfoHeader properties ...
messageBody.add(docInfoHeader);
}
(sample is not tested. It is just an idea, how to handle that...)
Other answer on similar question you can find here: Setting Custom Soap Header-To Pojo Message In Camel Cxf
It describes how to use Camel Exchange headers as SOAP Headers.
I'm not sure for 100% which way will work for you and which one is better...
I guess, it depends on WSDL you use.
UPD: second choice is to use pure CXF solution by using CxfMessageSoapHeaderOutInterceptor custom implementation.
It may look like:
public class MyCxfInterceptor extends CxfMessageSoapHeaderOutInterceptor {
#Override
public void handleMessage( org.apache.cxf.binding.soap.SoapMessage message) {
org.apache.cxf.binding.soap.SoapHeader myCustomHeader = new org.apache.cxf.binding.soap.SoapHeader(new QName(
{custom name space}, {custom local name}), {Custom content object}));
myCustomHeader.setMustUnderstand(true);
message.getHeaders().add(myCustomHeader);
}
and set Interceptor in Camel Cxf Endpoint as :
<cxfEndpoint ...>
<outInterceptors>
<spring:bean class="MyCxfInterceptor"/>
</outInterceptors>
...
Well suppose I request the Web Service and it failed, a Fault message is generated. Will I get the Fault object at position 0 of MessageContentsList then too? Or will I get only the response object at position 0?

How to handle soap:mustUnderstand="1" in webservice

In an incoming soap request there is a soap:mustUnderstand="1" element in soap header ,how can I handle this in my web service . If soap:mustUnderstand="1" it throws exception when it is 0 (soap:mustUnderstand="0") it runs as expected .
this is my partial soap request is like this
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header xmlns="http://www.xxxxxxx/zzzzz-msg/schema/msg-header-1_0.xsd">
<MessageHeader ResponseRequested="true" version="1.0" Terminate="true" Reverse="true" id="0002P559C1" soap:mustUnderstand="1">
.......
......
I am using Apache CXF for web service .
Your service should explicitly tell CXF that the given header has been understood and processed.
One way of doing it is registering a subclass of SOAPHandler responsible for actual processing of you header. In that interface it's important to implement method Set<QName> getHeaders() and return a set of headers' names that your handler takes care about.
CXF will then treat all those headers as understood
Example:
in Spring context XML:
<jaxws:endpoint ...>
<jaxws:handlers>
<bean class="example.MySOAPHandler" />
</jaxws:handlers>
</jaxws:endpoint>
in Java code:
public class MySOAPHandler implements SOAPHandler<SOAPMessageContext> {
public static final String MY_NS_URI = "http://www.xxxxxxx/zzzzz-msg/schema/msg-header-1_0.xsd";
public static final String MY_HEADER_NAME = "MessageHeader";
#Override
public Set<QName> getHeaders() {
// This will tell CXF that the following headers are UNDERSTOOD
return Collections.singleton(new QName(MY_NS_URI, MY_HEADER_NAME));
}
// other handler methods here
}
If a header block is annotated with mustUnderstand="1" and the
receiver wasn't designed to support the given header, the message
shouldn't be processed and a Fault should be returned to the sender
(with a soap:MustUnderstand status code). When mustUnderstand="0" or
the mustUnderstand attribute isn't present, the receiver can ignore
those headers and continue processing. The mustUnderstand attribute
plays a central role in the overall SOAP processing model.
For details kindly refer to this link

Consuming JSON request and producing different output

Can you point me to article or explain me how to declare RESTful web service which consumes JSON request and based on parameter inside JSON produces output in different formats, meaning customer can get output in JSON but in pdf also. I'm using Java and RestEasy on JBoss 5.1.
You could map the request on a method returning a RestEasy Response object, using a ResponseBuilder to build your response, setting dynamically the mime type of the response depending on a parameter in your JSON.
#POST
#Path("/foo")
#Consumes("application/json")
public Response fooService(MyObject obj) {
MyResponseEntity entity = MyObjectService.retrieveSomethingFrom(obj);
return Response.status(200).entity(entity).type(obj.isXml() ? "text/xml" : "application/json").build();
}
This way if your MyObject domain object that represent incoming JSON has a parameter xml set to true, then the Response object is parameterized to produce text/xml otherwise it produces application/json. RestEasy should do the rest.
You can use this way
#Path("/")
public class Test {
#Path("/test")
#POST
#Consumes("application/json")
#Produces("text/plain")
public Response addOrderJSON(OrderDetails details) {...}
}

Categories

Resources