virtualsciences
Published in

virtualsciences

A REST Service with what?

We all know REST services, right? And REST services work with JSON, don’t they? Well, …, not exactly. Web services based on SOAP (Simple Object Access Protocol) inherently use XML as a payload of request and response messages. SOAP is an XML-based standard where the service is elaborately defined in a service contract using WSDL (Web Service Definition Language).

SOAP Webservices normally use HTTP as a transport, although there are examples of SOAP over JMS. Despite the word Simple in the acronym, SOAP is considered to be complex. At least for mobile devices.

Nowadays, REST is mostly the standard of choice for web services. REST is based on the HTTP verbs (Get, Post, Put, Delete, etc.) and the HTTP query parameters in the URL. In contrast to SOAP which is XML based, REST services are not described as strictly. But for the response of a Get or the request-payload of a Post or Put you can use any format you like. It can be JSON, XML, fixed-record, CSV, binary, about anything the service understands.

One of those formats is the one that allows browsers to send a request on the submission on an HTML Form, which is a low-level HTTP message.

Let’s take a look at those messages and after that how to call such a REST Service from Red Hat Fuse/Camel.

Forms

You’re probably all familiar with forms in web pages. A form as above is based on the following HTML:

With this form, you can call a REST service on the action-URL “http://localhost:8000”, with method “post” and the “enctype” “multipart/form-data”. Having filled in the fields, clicking on the Submit button will cause the browser to send the content using the Post verb to the given URL. The HTTP message will consist of different parts for each field, hence multipart/form-data. It looks like:

Multipart/form-data request

In the screen dump above you’ll see the form fields with their respective values in the request. It’s a plain HTTP request with an Address to the service, and an “Http-Method”, in this case, PUT. You see a “Content-Type”, being “multipart/form-data”.

The fields are separated using a string like “-- uuid:f302ac64-afaa-4c07-ae16–23cfba2ac40e”.

You can upload files, which have a binary part such as:

A binary attachment

The server may respond with an HTTP message like:

HTTP Response message

This request above succeeded as can be derived from the Response-Code 200. You also see several HTTP headers, but in this case no message.

You probably won’t build such a service this way yourself, unless you would need to service an HTML form. Several platforms that support API calls may however generate a REST API this way, to easily support HTML/JavaScript web apps. Pega is an example of a platform that does this.

How to implement a service consumer with Fuse

Let’s get into it. First a disclaimer, I don’t have a service to test this against or an example project that you could run right away. In this article I’ll show snippets that I’ll anonymize for this purpose. These snippets are drawn from working code, but need to be adapted for your case if you need to use it.

We use the CXF framework to implement this, and the project is based on Fuse Karaf, so we use OSGi blueprint XML DSL. It should work in the same way using Spring XML DSL of course.

To start with we need a cxf:rsClient:

The rsClient needs an address and username and password, as well as a serviceClass.

The serviceClass is implemented as follows:

Not very exciting. Two particularities may be:

  1. It expects to consume MediaType.MULTIPART_FORM_DATA
  2. The createUpload method expects a MultipartBody. But we’ll get into that soon.

To support TLS and set the AllowChunking parameter (read all about that in my previous article) we need to provide an HTTP Configuration:

To complete the plumbing I have this property placeholder:

This route is quite extensive. It extracts a report from the incoming message. It saves the incoming message to a property (X_reportMessage) to be able to reuse it afterward in order to create a response from the request. In this case, I want to be able to respond with the filename(s) of the files processed and identifiers from the request.

It does a JAXB-unmarshal of the request to be able to get the fields for building up the request. I mentioned earlier that the REST Service expects a MultipartBody object that we’ll need to build up in Java. This is done in the reportToMultipart bean, with method reportToMultipart. The actual invoke of the REST Service is done in another route, with URI direct:sendRestUpload.

After that you’ll see an interpretation of the CamelHttpResponseCode header variable, to handle the response.

Notice the convertResponseToString() under the otherwise otherwiseSOAPSvcFault when the HTTP Response code is not 200. It extracts the actual HTTP Response message into the body. I’ll show it below in the ReportUpload bean.

The Send route is quite simple. Notice the setting of the Content-Type header as multipart/form-data and the Exchange.HTTP_METHOD header as PUT. Also important is to explicitly set the setExchangePattern to InOut. This is important to get the HTTP response code and the response message for the response handling. At the end, the CXF rsClient with id restUpload is invoked.

The key in this exercise is to create the MultipartBody object. This is done using the RestUpload bean, with the main entry the ReportExportToMultipart method.

ReportUpload.java

The method ReportExportToMultipart first puts all the relevant attributes of the ReportExport JAXB-bean into a HashMap. In my example, the file to upload is delivered as a Base64 encoded string in the request XML. The file has to be decoded into a byte array and then converted into an InputStream. With that, the genBody() method creates a MultipartBody object. For each field in the formData HashMap an Attachment is added an attachmentList (List<Attachment>). An Attachment needs the proper headers set, which are generated with a helper method genFieldHeaders().

The file to upload is added separately in the same way, but with other headers, as done using the genXlsxHeaders() helper function. In this case, the solution only supports one upload file. But using a HashMap like with the fields, the service could also handle multiple files. But notice that the ContentType is dependent on the type of file.

The last method of the file is convertResponseToString(). It took me a while to come up with this method. But this gets the response message in the HTTP Response, which I wanted to send back in case of a negative response (HTTP Response Code unequal to 200).

Conclusion

Earlier I wrote a few articles about the REST DSL. As you can see, the Multipart/Form-Data REST Service is a bit more complex to configure. The complexity is in the method to build up the MultipartBody. This is quite similar to the mime APIs used to create an SMTP mail message if I recall my endeavors in that area correctly.

I hope this helps in reproducing a CXF RSClient with this kind of payload. Or that you might find some other hints when working with CXF.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Martien van den Akker

Technology Architect at Oracle Netherlands. The views expressed on this blog are my own and do not necessarily reflect the views of Oracle