Wednesday, December 21, 2016

SOAP, REST XML and JSON Custom Faults in OSB 12c

One of the amazing and useful feature in 12c is the automatic JSON exposure of REST based services. In 11g, we always had to write custom codes to convert JSON to XML and XML to JSON and pass to requested systems. But 12c makes it easy.

Most of the times, we get requirements to expose a service as SOAP and REST (including JSON), which is easy in 12c world. But how does a single service handle custom errors for all types of protocols.


In this blogpost, we will see how custom errors are returned in 12c for SOAP, REST XML and JSON. 


We will begin with WSDL and XSDs for the OSB service - PGRaiseErrorProject.


The request and response schemas have pretty simple elements. Request takes 2 double fields as input and Response gives a status as output.
































Following Custom Fault xsd will be used.





















The abstract and concrete wsdls looks as follows:

Abstract wsdl - ErrorServiceV1Abstract.wsdl


<?xml version= '1.0' encoding= 'UTF-8' ?>

<wsdl:definitions
     name="ErrorService"
     targetNamespace="http://pgservice.com/services/ErrorService/V1"
     xmlns:tns="http://pgservice.com/services/ErrorService/V1"
     xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
     xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
     xmlns:xsd="http://www.w3.org/2001/XMLSchema"
     xmlns:fault="http://pgservice.com/errortest/customfault/V1"
     xmlns:ser="http://pgservice.com/errortest/V1"
    >
    <wsdl:types>
        <xsd:schema targetNamespace="http://pgservice.com/errortest/V1">
            <xsd:include schemaLocation="../xsd/RequestSchema.xsd"/>
            <xsd:include schemaLocation="../xsd/ResponseSchema.xsd"/>
        </xsd:schema>
        <xsd:schema targetNamespace="http://pgservice.com/errortest/customfault/V1">
            <xsd:include schemaLocation="../xsd/CustomFault.xsd"/>
        </xsd:schema>
    </wsdl:types>
    <wsdl:message name="ErrorServiceRequestMsg">
        <wsdl:part name="parameters" element="ser:testRequest"/>
    </wsdl:message>
    <wsdl:message name="CustFaultMsg">
        <wsdl:part name="parameters" element="fault:custFault"/>
    </wsdl:message>
    <wsdl:message name="ErrorServiceResponseMsg">
        <wsdl:part name="parameters" element="ser:testResponse"/>
    </wsdl:message>
    <wsdl:portType name="ErrorServicePortType">
        <wsdl:operation name="checkStatus">
            <wsdl:input message="tns:ErrorServiceRequestMsg"/>
            <wsdl:output message="tns:ErrorServiceRequestMsg"/>
            <wsdl:fault name="custFault" message="tns:CustFaultMsg"/>
        </wsdl:operation>
    </wsdl:portType>

</wsdl:definitions>

Concrete wsdl - ErrorServiceV1SOAP.wsdl


<wsdl:definitions name="ErrorService" targetNamespace="http://pgservice.com/services/ErrorService/V1"

                  xmlns:tns="http://pgservice.com/services/ErrorService/V1" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
                  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
               >
  <wsdl:import location="ErrorServiceV1Abstract.wsdl" namespace="http://pgservice.com/services/ErrorService/V1"/>
  <wsdl:binding name="ErrorServiceSOAP" type="tns:ErrorServicePortType">
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
    <wsdl:operation name="checkStatus">
      <soap:operation soapAction="http://pgservice.com/services/ErrorService/V1/Test"/>
      <wsdl:input>
        <soap:body use="literal"/>
      </wsdl:input>
      <wsdl:output>
        <soap:body use="literal"/>
      </wsdl:output>
      <wsdl:fault name="custFault">
        <soap:fault use="literal" name="custFault"/>
      </wsdl:fault>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="ErrorService">
    <wsdl:port name="ErrorServicePort" binding="tns:ErrorServiceSOAP">
      <soap:address location="http://pgservice.com/services/ErrorServiceV1"/>
    </wsdl:port>
  </wsdl:service>

</wsdl:definitions>

Next we would start with the OSB project. All the above schemas and wsdls have been placed in xsd and wsdl folders under Resources in the project as follows - 


















In the xsd folder, you would have observed that there are two more schemas. SOAPEnvelope.xsd is the regular SOAP schema and MessageContext.xsd has the structure of the $fault variable in OSB. In order to create xqueries, these can be used.

The next step is to create SOAP proxy service. Based on ErrorServiceV1SOAP wsdl, create a SOAP proxy and pipeline.  Provide the Proxy service name and pipeline name.




















Once the proxy and pipeline are created. Right click on the pipeline. You will find an option to expose the service as Rest. Click on it.













You will now get a new window to enter the resource path and operation details. By default, the operation from the wsdl will be populated  in the operation bindings sections.  

Click on the first + symbol and create a new resource path - /checkStatus - here.

Then select the operation from Operation bindings and select the pencil symbol to edit.
Rest Binding


This will show the Request payload section. Only XML is selected by default. Select the JSON check box as well. 
JSON Option















Similarly in the response as well, select the JSON checkbox. 
In the response you would see that the custFault variable from the wsdl has got auto-populated in the fault bindings, which means REST proxy will allow faults of custFault type to be returned. 


Select the fault and click on edit, it will allow you to select JSON output for fault as well.


















Once the above changes are done, the project looks as follows.
SOAP and REST Proxy














Now, lets write the logic in the pipeline. Double click the pipeline.
Logic goes as follows - 
If the difference between totalAmount and paidAmount is less than 1000, a success response with message is returned.
If the difference is greater than 1000, a raise error is done.
The pipeline looks as follows after implementing the above logic.
Pipeline


The following code and message will be returned for the business error.
Raise Error
















The next step is to handle the error in a way that it abides by the SOAP wsdl contract and also follows REST XML and JSON convention in the same way.

For error handling, we will add an error handler for the Pipeline pair. 


1. First step would be to add a Reply activity and check the option as "With Failure". This will allow error response to be sent.













2. Second step is to populate the Custom Fault that we have in the wsdl. I added an assign activity in the error handler and populated the custFault var using $fault as follows.




















3. CustFaultVar now has the Custom Fault populated. How do we return it in a way that the consuming system gets appropriate message for SOAP, REST XML and JSON?


As we all know any fault for a SOAP service should be returned in SOAP Fault format. Else, it is not considered to be valid. So, here as well, we are supposed to populate the SOAPFault and return as response body.

Any custom fault mentioned in the wsdl should be placed under detail tag of the SOAPFault.

4. I have added a replace activity and replaced the body with SOAP fault as below. The faultcode has been hardcoded. You can populate any other code.

The detail tag has the CustFaultVar embedded.























Rest adapter is smarter than http adapter. When the above fault response is returned from Pipeline to the Rest adapter, the adapter does multiple validations.


1. It checks if the Fault received from pipeline is SOAP fault or not ?


2. Then it checks if the fault structure mentioned in the wsdl (here custFault) and the fault structure in the detail tag of SOAPFault are same. 


3. If yes, Rest adapter will internal extract the structure present in the detail tag and return as response in XML/JSON formats.


4. It checks if the Custom Fault populated in the detail section of response is adhering to the Custom Fault XSD like mandatory fields, field types, etc.

  
If any of the above mentioned items fail, Rest adapter will throw the following error.
<err:RestError xmlns:err="http://www.bea.com/wli/sb/errors"/>

Note that, they http adapter will still show SOAPFault if the above validations fail. But Rest will not work.


The above highlighted part is very important for custom faults to work.


Now I will show how the response looks like when an error occurs. I am going to provide totalAmount as 34325 and paidAmount as 123. The result is going to be >1000. Hence, "Remaining amount is more than allowed limit" error should be returned.  



SOAP proxy - 


SOAP Proxy returns a SOAP Fault, where the Custom Fault is embedded in the detail tag. This is exactly what the pipeline returns to any proxy.






















Rest proxy with application/xml as Media and Accept type - 

The error response contains just the custFault that we saw in the detail section of SOAP Fault. 
As mentioned earlier, Rest adapter just reads and validates the detail tag structure.

Rest proxy with application/json as Media and Accept type -

The error response here is the JSON format of above Rest xml error response.













Thus, custom errors are handled in OSB 12c for SOAP and REST. 

I hope you find this blog useful.


32 comments:

  1. Hi Prasanna
    Do you have the sample project available for download?
    Thanks

    ReplyDelete
    Replies
    1. Hi,

      You can download the sample project from here.

      Delete
  2. Congratulations for your blog! Its really helpfull.
    Im calling my service by SOUAPUI whitout pass the accept with application/json. My custon fault doesent has xml type even those the erro is returning xml format.
    I've already tried to set the accept in the inbound variable manualy but its not worked as well.
    If I send the accept from the client its work well returning a json error. But I want to answer the error in json format in any cases

    ReplyDelete
    Replies
    1. Hi,

      You dont need to set the accept header in the inbound variable. In order to make sure that the error is always returned json format, all you need to do is, make sure that, in the fault bindings of response, the fault has only JSON payload enabled. I tried it for the same example and it worked for me.

      Check the below screen shots for reference.

      JSONResponse
      SOAPUITestResponse

      Delete
  3. Hi prasanna,
    Do you have any example in osb how to pass the Json request to DB?
    please share below mail id venkat.kurma@gmail.com

    ReplyDelete
    Replies
    1. Hi Venkat,

      Are you looking for an example where you get JSON as input to the OSB and you transform it and map to the input of DB?

      or

      Accept JSON request as-is from proxy and update as CLOB in DB?

      Delete
  4. Top stuff! I've been trying for weeks to figure out the trick to custom faults in OSB REST services.

    ReplyDelete
  5. Hi Prasanna,

    how to do xml to json conversion in soa 11g,please provide some code for that.

    thanks
    Damodhar

    ReplyDelete
    Replies
    1. The only way to convert xml to json in 11g is to through Java services. The code to convert can be written in Java and those Java jars can be used in SOA. I used these java jars in OSB. Same should work in SOA as well.

      Delete
  6. This comment has been removed by the author.

    ReplyDelete
  7. Hi Prasanna.

    What should i do if I want to add custom fault by taking the fault I received from target service calling via BS.

    Thanks,
    Sugandha

    ReplyDelete
    Replies
    1. You can make sure that the fault schema from target service is added in the custom fault section of the proxy service wsdl and returned.

      Delete
  8. Hi Prasanna,

    Is it necessary to add custom fault in the WSDL or can I send it as part of response ?

    Thanks,
    Sandeep

    ReplyDelete
    Replies
    1. For a wsdl based service, you need add the custom fault in the wsdl. If you dont add in the wsdl, SOAP Fault will still get returned, but JSON fault will not work.

      Delete
  9. This comment has been removed by the author.

    ReplyDelete
    Replies
    1. Can you the post the exact error coming from business service and the error you are getting from proxy?

      Delete
  10. This comment has been removed by the author.

    ReplyDelete
  11. Hi Prasanna,
    I have a issue with rest service in 12C. I am getting a proper response structure as defined in wsdl for fault. But when calling it through rest proxy I am n0t getting the same structure. I have verified that response return through pipeline is same as structure in WSDL and is present in detail tag. And for success response I am getting proper json response structure. Please help.

    ReplyDelete
  12. Hi Prasanna,
    I have a SOA application that gets JSON as input. I need to store this JSON object in a CLOB field in the database. But this object is written in XML format and not in JSON by SOA. It is possible to receive this JSON object to invoke the database and write it. Or how do I convert XML to JSON to pass to Invoke?
    SOA 12c
    Thanks

    ReplyDelete
    Replies
    1. Sorry for the late reply. One of the options is as follows -
      1. Before calling the data for insertion, convert the xml into json through translate activity. This activity gives you a response of json string, which you can as-is push to database field.

      Delete
  13. Hi Prasanna great post!

    This is for SOAP-REST based services (using WSDL), do you know how to implemented it in native REST (using only the WADL)?

    I'm asking because I have a target REST Service that returns its specific JSON message when it fails, but then the OSB return its own message (OSB-xxxxxx), I've tried different approaches but nothing at the moment.

    Obviously if I cannot find the way I will refactor to SOAP-REST based but I really want to know the way to do it for native REST if you know.

    Thank you in advance.

    Kind regards,
    Javier

    ReplyDelete
    Replies
    1. Hi Javier,

      In $fault, you might see that OSB-xxxxx message. Please check $body in the error handler, you would be getting the exact error message as sent by your target REST service.

      Delete
  14. Awesome blog. Resolved my issue.

    ReplyDelete
  15. Hi Prasanna, thanks for your reply,

    I can see both, but in the body I see the JSON payload (not serialized or parsed to XML) and if I try to change the $fault detail is still showing the OSB-xxxxx error, not the body payload.

    What would be the best approach here in your opinion? what would be the $fault/xxx value to change in order to see the right message, I tried to put it in the fault "details" field but without success.

    Thank you again,
    Javier

    ReplyDelete
    Replies
    1. Can you paste what exactly you are getting $body and $fault. I would be able to help better if I know the payload coming.

      Delete
  16. I feel SOAP and REST are the best ways of looking for more and more information and solving more complex IT problems.

    Powerbi Read Soap

    ReplyDelete
  17. This comment has been removed by the author.

    ReplyDelete
  18. This blog is from 2016, and still helping people with the same energy when it was created.
    Thanks for helping me today.

    I didn't knew about wrapping the custom fault message to Soap Fault in case of rest fault, and rather I was trying to match the failure response with the fault schema from xsd and was getting the usual 'rest error'.

    ReplyDelete

  19. I feel really happy to see your webpage here and i spend a lot of time reading here.Same as your blog i found another one Oracle OSB.Actually I was looking for the same information on internet for Oracle WebCenter Sites and came across your blog. I am impressed by the information that you have on this blog. Thanks once more for your creative ideas.

    ReplyDelete
  20. nicely explained! I am new to this with limited previous knolwdge. Can I catch this error from SOA BPEL?

    ReplyDelete