WSO2 Api Manager Custom error messages - web-services

I want to customize the error messages sent by api manager, for instance when an access token is missing or expired. I've configured _auth_failure_handler_ to return messages in json as described here, and get responses like:
{"fault":{"code":"900902","message":"Missing Credentials","description":"Required OAuth credentials not provided"}}
I would like to modify the message description and remove the "code" altogether. Is there a way to do this? I've tried tweaking the sequence with no luck.

You need to target the error codes from https://docs.wso2.com/display/AM260/Error+Handling and update it to your custom JSON messages. For auth token related errors try modify _auth_failure_handler_ as below:
<sequence name="_auth_failure_handler_" xmlns="http://ws.apache.org/ns/synapse">
<property name="error_message_type" value="application/json"/>
<filter source="get-property('ERROR_CODE')" regex="405">
<then>
<sequence key="converter"/>
<drop/>
</then>
<else>
</else>
</filter>
<filter source="get-property('ERROR_CODE')" regex="900901">
<then>
<sequence key="invalidCredential"/>
<drop/>
</then>
<else>
</else>
</filter>
<filter source="get-property('ERROR_CODE')" regex="900902">
<then>
<sequence key="missingCredential"/>
<drop/>
</then>
<else>
</else>
</filter>
<sequence key="_cors_request_handler_"/>
For your case Missing Credential has a 900902 code , so it will match and need to define missingCredential.xml as below :
<?xml version="1.0" encoding="UTF-8"?>
<sequence xmlns="http://ws.apache.org/ns/synapse" name="missingCredential">
<payloadFactory media-type="json">
<format>{ "status": "Error","message": "Missing Credentials" }</format>
<!--Add your custom message and format here. This will be your output-->
</payloadFactory>
<property name="RESPONSE" value="true"/>
<header name="To" action="remove"/>
<property name="HTTP_SC" value="401" scope="axis2"/>
<property name="messageType" value="application/json" scope="axis2"/>
<send/>
</sequence>

It is not wise advice to modify the error code. Nevertheless, yes it is possible to modify the payload. Use the filter mediator and Json path and identify the data and use enrich mediator to modify the payload as you want.

Related

What is the best way to iterate inside WSO2 EI?

After reading of WSO2 EI References, I still confuse about how use iterators inside a EI sequence.
In my case I have a payload like this....
{
...
"array": [
{"cpf": "12345678911"},
{"cnpj":"12345678912346"}
]
}
So I have to iterate to check if those guys exist using another web services. in order to achieve that flow, I am using the iterate mediator to split the message and then building the logic to make those validations as follows..
The code that implements this image is following:
<iterate description="" expression="//jsonObject/array" id="myid">
<target>
<sequence>
<property expression="json-eval($.array.cpf)" name="tipoCPF" scope="default" type="STRING"/>
<filter description="" xpath="boolean(get-property('tipoCPF'))">
<then>
<property expression="json-eval($.array.cpf)" name="uri.var.cpf" scope="default" type="STRING"/>
<call>
<endpoint>
<http method="get" uri-template="http://endpoint/service/{uri.var.cpf}"/>
</endpoint>
</call>
<filter regex="200" source="get-property('axis2','HTTP_SC')">
<then/>
<else>
<payloadFactory description="" media-type="json">
<format>{
"code":"400",
"error":"CPF inexistente"
}</format>
<args/>
</payloadFactory>
<property name="HTTP_SC" scope="axis2" type="STRING" value="400"/>
<respond/>
</else>
</filter>
</then>
<else>
<property expression="json-eval($.array.cnpj)" name="tipoCNPJ" scope="default" type="STRING"/>
<filter xpath="boolean(get-property('tipoCNPJ'))">
<then>
<property expression="json-eval($.array.cnpj)" name="uri.var.cnpj" scope="default" type="STRING"/>
<header name="Authorization" scope="transport" value="Basic Y29yZS5jb25zdWx0aW5nOm8xNXRyZWI="/>
<call>
<endpoint>
<http method="get" uri-template="http://endpoint/service/cnpj/{uri.var.cnpj}"/>
</endpoint>
</call>
<filter regex="200" source="get-property('axis2','HTTP_SC')">
<then/>
<else>
<payloadFactory media-type="json">
<format>{
"code":"400",
"error":"CNPJ inexistente"
}</format>
<args/>
</payloadFactory>
<property name="HTTP_SC" scope="axis2" type="STRING" value="400"/>
<respond/>
</else>
</filter>
</then>
<else>
<call>
<endpoint>
<http method="get" uri-template="http://endpoint/service/info"/>
</endpoint>
</call>
</else>
</filter>
</else>
</filter>
</sequence>
</target>
</iterate>
This iterator work as part inside of the an 'insequence'. The 'Insequence' is deigned to allow to insert new contracts information inside the Database.
Problem: after add this iterator, the service starts to make duplicated insertions inside Database. It´s looks like the iteration don´t finish in the edge of tags 'iterator'. It´s like the iteration continues to the rest of insequence.
Try: In order to solve this problem i try to add an aggregator mediator after the iterator. But or doesn't have any effect end the duplicated insert persist, or I receive an error message.
So What is the correct whey to make this iterations inside WSO2 EI?
As you have mentioned, Iteration will occur even outside the iterate tag, until the Aggregate mediator is used. To resolve this issue, you need to add the aggregate mediator inside the iterate mediator. This will stop the iteration within the iterator tag itself.
For your use case, you may need to set continueParent="true" in the IterateMediator, so that the mediation will continue after the iterate mediator for the insertion Operation in database.
Thanks for the helping Arunan!
After your answer I try to add the Aggregator as follows
The config of aggregator is following:
...
<aggregate id="NIRO">
<completeCondition>
<messageCount max="-1" min="-1"/>
</completeCondition>
<onComplete expression="//jsonObject">
<log description="" level="full" separator=";">
<property expression="json-eval($.)" name="jsonObject"/>
</log>
</onComplete>
</aggregate>
</sequence>
</target>
</iterate>
As you sad I change the iterator property 'Continue Parent' to 'true'. But the problem persists....
As proposed in the other answer, you need to
use an Aggregate mediator to close the iteration
If and only if the Iterator is set to continueParent="true"
However, I am not sure that putting it inside the <iterate> works. Here is a working solution using an Aggregate mediator right after your Iterator.
<sequence>
<iterate continueParent="true" description="" expression="//jsonObject/array" id="myid">
<target>
<your_sequence />
</target>
</iterate>
<aggregate>
<completeCondition>
<messageCount max="-1" min="-1" />
</completeCondition>
<onComplete enclosingElementProperty="//jsonObject/array" expression="/whatever/you/want"/>
</aggregate>
</sequence>
Notice expression="//jsonObject/array" you used in your Iteration, you'll need to use it in the Aggregator's enclosingElementProperty. This is how your EI will know from which iterator it should aggregate from (not 100% sure about this point, more of an empirical consideration).

How to use filter for more than one parameter of a JSON message

I'm using ESB-4.9.0 version.
An ESB mediation flow has to be continued based on two filtering logic. JSON message is transformed in the mediation flow. Currently, I'm using two Filter mediators to achieve this. Is there any possibility to use a single filter mediator to fulfill the same scenario?
Input JSON message
{
"filterId": "CorrectId",
"approvalStatus": "approved",
"lifeCycleStatus": "BRANCH_READY",
"channelData": [
{
"status": "pending",
"indexId": "correctIndexId",
"description": "Test Description"
}
]
}
The used ESB Synapse part
<filter description="" regex="CorrectId" source="json-eval($.filterId)">
<then>
<filter description="" regex="correctIndexId" source="json-eval($.indexId)">
<then>
<!-- continue the mediation flow-1-->
</then>
<else>
<!-- continue the mediation flow-2-->
</else>
</filter>
</then>
<else>
<drop/>
</else>
</filter>
Yes, this is possible with a filter mediator and the xpath concat function:
<resource methods="POST" uri-template="/onefilter">
<inSequence>
<property name="filterId" expression="json-eval($.filterId)"/>
<property name="correctIndexId" expression="json-eval($.channelData[0].indexId)"/>
<property name="combinedID" expression="fn:concat($ctx:filterId, $ctx:correctIndexId)"/>
<filter source="$ctx:combinedID" regex="CorrectIdcorrectIndexId">
<then>
<log level="custom">
<property name="message" value="continue 1"/>
</log>
<drop/>
</then>
<else>
<log level="custom">
<property name="message" value="continue 2"/>
</log>
<drop/>
</else>
</filter>
</inSequence>
By the way your code snippet would NEVER go into the continue 1 route because your JSON path is invalid.

How to customize error XML response from api manager to a simple JSON message?

When invoking a published API with invalid access token, a XML response was got.
900901
Invalid Credentials
Access failure for API: /sit/zxq/oapi/ut/1.0, version: 1.0 with key: b645348f2ca7fea5a9cf498e4085a471. Make sure your have given the correct access token
How could we customize such response to following JSON format?
{
"req_id": "REQ_ENT_1356985018299_9678",
"err_resp": {
"code": "28001",
"msg": "Invalid access token"
}
}
Thanks and looking forward to your expertise.
We've recently done something similar with our API Manager implementation. You can find the fault sequences in /repository/deployment/server/synapise-configs/default/sequences, match on the error code, and provide your own JSON content. You can also use a switch mediator (the fault sequences are just mediation sequences) to return the correct content type for various Accept header values. Just replace the payload written in the fault.xml file with the equivalent JSON content (or as suggested, write a switch to allow the support of both content types).
You need to target the error codes from https://docs.wso2.com/display/AM260/Error+Handling and update it to your custom JSON messages. For auth token related errors try modify _auth_failure_handler_ as below:
<sequence name="_auth_failure_handler_" xmlns="http://ws.apache.org/ns/synapse">
<property name="error_message_type" value="application/json"/>
<filter source="get-property('ERROR_CODE')" regex="405">
<then>
<sequence key="converter"/>
<drop/>
</then>
<else>
</else>
</filter>
<filter source="get-property('ERROR_CODE')" regex="900901">
<then>
<sequence key="invalidCredential"/>
<drop/>
</then>
<else>
</else>
</filter>
<filter source="get-property('ERROR_CODE')" regex="900902">
<then>
<sequence key="missingCredential"/>
<drop/>
</then>
<else>
</else>
</filter>
<sequence key="_cors_request_handler_"/>
For your case Invalid Credential has a 900901 code , so it will match and need to define invalidCredential.xml as below :
<?xml version="1.0" encoding="UTF-8"?>
<sequence xmlns="http://ws.apache.org/ns/synapse" name="invalidCredential">
<payloadFactory media-type="json">
<format>{ "req_id": "REQ_ENT_1356985018299_9678", "err_resp": { "code": "28001", "msg": "Invalid access token" } </format>
<!--Add your custom message and format here. This will be your output-->
</payloadFactory>
<property name="RESPONSE" value="true"/>
<header name="To" action="remove"/>
<property name="HTTP_SC" value="401" scope="axis2"/>
<property name="messageType" value="application/json" scope="axis2"/>
<send/>
</sequence>

WSO2 mediator return error message to user

I'm currently trying to write a java mediator in WSO2 API manager to perform some processing before sending the message to the proxy.
The normal use cases work fine, but I'm having some trouble with exceptions.
I would like to be able to send a message back to the user from the mediator, with a message and a HTTP status code, but I can't see a way to do it.
import org.apache.synapse.mediators.transform.PayloadFactoryMediator;
public class MessageMediator extends PayloadFactoryMediator
{
#Override
public boolean mediate(org.apache.synapse.MessageContext synapseMessageContext)
{
boolean success = true;
try{
.... some processing
}
catch(Exception e)
{
success = false;
handleException(e.getMessage(), e, synapseMessageContext);
//write message back to user
}
return success;
}
}
This is my proxy:
<?xml version="1.0" encoding="UTF-8"?>
<proxy xmlns="http://ws.apache.org/ns/synapse"
name="AMQPProxy"
transports="https http"
startOnLoad="true"
trace="enable">
<description/>
<target>
<endpoint>
<default />
</endpoint>
<inSequence>
<sequence key="MessageMediator"/>
</inSequence>
<outSequence>
<send/>
</outSequence>
</target>
</proxy>
What is the correct procedure to return an error to the user?
Thanks
If you are returning the error from the insequence itself without going out of wso2 esb calling another service you can do it as :
<payloadFactory media-type="json">
<format>{"Error":{"errorType":"BusinessError","details":"some details"}}</format>
<args/>
</payloadFactory>
<header name="To" scope="default" action="remove"/>
<property name="RESPONSE" value="true" scope="default" type="STRING"/>
<property name="HTTP_SC" value="400" scope="axis2" type="STRING"/>
<property name="messageType" expression="$trp:Accept" scope="axis2" type="STRING"/>
<send/>
You can do similarly with media-type as xml see payload factory mediator
If you are going to return error from outsequence you can use makefault see here Fault mediator you can try using fault mediator in insequence as well i haven't tried yet but when sending in insequence you need to use
<header name="To" scope="default" action="remove"/>
in my case, if the username is empty(you can use any http code(in my sample 406)):
<filter source="json-eval($.loginStatic.request.username)" regex="^null|$">
<then>
<property name="HTTP_SC" value="406" scope="axis2"/>
<property name="messageType" value="application/json" scope="axis2" type="STRING"/>
<payloadFactory media-type="json">
<format>{ "status": "ERROR!"}</format>
<args/>
</payloadFactory>
<send/>
</then>
</filter>

WSO2 ESB If Then Else, authenticate or Error

This may be a basic question, I am just getting used to the WSO2 lingo. I have two services that I can deploy independently with WSDLs and pass the proper SOAP request, and return information accordingly. Now I want to combine them into an 'If then, else' statement sort of deal. This would be set up in a sequence of some sort I believe, just not sure how with the filters.
Send in request with authentication request and info request
Do authentication request – continue if passes, 401 on failure
Do info request – get info
Return the info
If you have a sample I could follow or point me to one of the hundreds WSO2 has, I just haven't been able to pull much from them. XML source example for the config could work also. Thanks for the help, and for my ignorance of WSO2 lingo, and workflow.
You can have a look at filter mediator to filter messages based on conditions Entitlement Mediator. You can find samples here as a reference which will be helpful for your use case.
So I ended up with something very similar to this. If someone down the road comes across this and looking for the wso2 configurations.
<proxy name="name"
transports="https http"
startOnLoad="true"
trace="disable">
<description/>
<target>
<inSequence>
<property xmlns:ns1="ns1"
xmlns:ns="ns"
name="userID"
expression="//ns:AuthenticateRequest/ns:Credentials/ns1:userID"
scope="default"
type="STRING"/>
<property xmlns:ns1="ns1"
xmlns:ns="ns1"
name="password"
expression="//ns:AuthenticateRequest/ns:Credentials/ns1:password"
scope="default"
type="STRING"/>
<log>
<property name="userID" expression="get-property('userID')"/>
<property name="password" expression="get-property('password')"/>
</log>
<header name="Action"
value="http://services.com:port/AuthenticateSecureCredential"/>
<send receive="AuthRecvSequence">
<endpoint>
<address uri="http://server.com:port/DefaultAuthenticationService"/>
</endpoint>
</send>
</inSequence>
</target>
</proxy>
<sequence name="AuthRecvSequence">
<filter xmlns:ns="ns"
source="//ns:AuthenticateSecureCredentialResponse/ns:isAuthenticated"
regex="false">
<then>
<makefault version="soap11">
<code xmlns:soap11Env="http://schemas.xmlsoap.org/soap/envelope/"
value="soap11Env:VersionMismatch"/>
<reason value="Not Authenticated"/>
<role/>
</makefault>
<header name="To" action="remove"/>
<property name="RESPONSE" value="true" scope="default" type="STRING"/>
<send/>
<drop/>
</then>
<else>
<payloadFactory>
<format>
<ns:INFO xmlns:ns="ns"
xmlns:ns1="ns1">
<ns:secureCredentials>
<ns1:userID>$1</ns1:userID>
<ns1:password>$2</ns1:password>
</ns:secureCredentials>
</ns:INFORequest>
</format>
<args>
<arg expression="get-property('userID')"/>
<arg expression="get-property('password')"/>
</args>
</payloadFactory>
<header name="Action"
value="http://services.com/GetINFO"/>
<send receive="INFOrRecvSeq">
<endpoint>
<address uri="http://server:port/INFOService"/>
</endpoint>
</send>
</else>
</filter>
</sequence>
<sequence name="INFORecvSeq">
<send/>
</sequence>
<sequence name="main">
<description>The main sequence for the message mediation</description>
</sequence>