We are going to explain on how we can generate a custom HTTP Status code for a request which is addressed to a un-matching resource of an API.
Problem :
When an API exposed with resource "GET" , if the client invoke the API with "POST","PUT" or any other which is not "GET", By default API manager returns following.
{
"fault":{
"code":"900906",
"type":"Status report",
"message":"Runtime Error",
"description":"No matching resource found in the API for the given request"
}
}
In the RAW level you ll see it as follows
HTTP/1.1 403 Forbidden
Access-Control-Allow-Headers: authorization,Access-Control-Allow-Origin,Content-Type
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,PUT,POST,DELETE,OPTIONS
Content-Type: application/xml; charset=UTF-8
Date: Mon, 29 Jun 2015 14:46:29 GMT
Server: WSO2-PassThrough-HTTP
Transfer-Encoding: chunked
Connection: Keep-Alive
<ams:fault xmlns:ams="http://wso2.org/apimanager/security">
<ams:code>900906</ams:code>
<ams:message>No matching resource found in the API for the given request</ams:message>
<ams:description>Access failure for API: /sss, version: v1 with key: 4a33dc81be68d1b7a5b48aeffebe7e</ams:description>
</ams:fault>
Expected Solution :
We need to change this HTTP Response code 405 [1] with a custom error message.
Solution :
We need to create a sequence which builds the custom error message and the error code and deploy it in API manager's default sequences folder.
<?xml version="1.0" encoding="UTF-8"?>
<sequence xmlns="http://ws.apache.org/ns/synapse" name="converter">
<payloadFactory media-type="xml">
<format>
<am:fault xmlns:am="http://wso2.org/apimanager">
<am:message>Resource not found</am:message>
<am:description>Wrong http method</am:description>
</am:fault>
</format>
</payloadFactory>
<property name="RESPONSE" value="true"/>
<header name="To" action="remove"/>
<property name="HTTP_SC" value="405" scope="axis2"/>
<property name="messageType" value="application/xml" scope="axis2"/>
<send/>
</sequence>
You can save this as
converter.xml in
wso2am-1.8.0/repository/deployment/server/synapse-configs/default/sequences folder.
Then we need to invoke this sequence in
_auth_failure_handler_.xml which is located in the above sequences folder. In order to do that , we need to change it as follows.
<?xml version="1.0" encoding="UTF-8"?>
<sequence xmlns="http://ws.apache.org/ns/synapse" name="_auth_failure_handler_">
<property name="error_message_type" value="application/xml"/>
<filter source="get-property('ERROR_CODE')" regex="900906">
<then>
<sequence key="converter"/>
<drop/>
</then>
<else>
</else>
</filter>
<sequence key="_build_"/>
</sequence>
Once you done the above changes, save them. Then you can test your scenario. If you are successful with this , you ll be able see following response
HTTP/1.1 405 Method Not Allowed
Host: 10.210.1.202:8243
Content-Type: application/xml
Date: Mon, 29 Jun 2015 14:59:12 GMT
Server: WSO2-PassThrough-HTTP
Transfer-Encoding: chunked
Connection: Keep-Alive
<am:fault xmlns:am="http://wso2.org/apimanager">
<am:message>Resource not found</am:message>
<am:description>Wrong http method</am:description>
</am:fault>
Explanation :
By default, when we invoke an non-existing resource it will send the default 403 error code with the message "
No matching resource found in the API for the given request". If you check the log of the WSO2 AM, you can see that it has thrown following exception in the backend.
[2015-06-29 10:59:12,103] ERROR - APIAuthenticationHandler API authentication failure
org.wso2.carbon.apimgt.gateway.handlers.security.APISecurityException: Access failure for API: /sss, version: v1 with key: 4a33dc81be68d1b7a5b48aeffebe7e
at org.wso2.carbon.apimgt.gateway.handlers.security.oauth.OAuthAuthenticator.authenticate(OAuthAuthenticator.java:212)
at org.wso2.carbon.apimgt.gateway.handlers.security.APIAuthenticationHandler.handleRequest(APIAuthenticationHandler.java:94)
at org.apache.synapse.rest.API.process(API.java:284)
at org.apache.synapse.rest.RESTRequestHandler.dispatchToAPI(RESTRequestHandler.java:83)
at org.apache.synapse.rest.RESTRequestHandler.process(RESTRequestHandler.java:64)
at org.apache.synapse.core.axis2.Axis2SynapseEnvironment.injectMessage(Axis2SynapseEnvironment.java:220)
at org.apache.synapse.core.axis2.SynapseMessageReceiver.receive(SynapseMessageReceiver.java:83)
at org.apache.axis2.engine.AxisEngine.receive(AxisEngine.java:180)
at org.apache.synapse.transport.passthru.ServerWorker.processNonEntityEnclosingRESTHandler(ServerWorker.java:344)
at org.apache.synapse.transport.passthru.ServerWorker.processEntityEnclosingRequest(ServerWorker.java:385)
at org.apache.synapse.transport.passthru.ServerWorker.run(ServerWorker.java:183)
at org.apache.axis2.transport.base.threads.NativeWorkerPool$1.run(NativeWorkerPool.java:172)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:744)
When it throws above exception, the flow will hit the
_auth_failure_handler_.xml
sequence. So what we have done in this sequence, with using the filter mediator, we have filtered the error code "900906" and for that error code, we invoke our custom sequence and drop the message then.
In the custom sequence , we have used the payload factory mediator to create the payload and added required properties to make it as response. You can find the information further on each of those properties from [2][3][4]
Then after invoking the custom sequence, it will invoke the "
_build_" sequence in the same folder which invoke the message builders to build the message.
I have used resources [4] on creating this blog post.
[1]
http://www.checkupdown.com/status/E405.html#
[2]
https://docs.wso2.com/display/ESB481/Generic+Properties#GenericProperties-RESPONSE
[3]
https://docs.wso2.com/display/ESB481/Generic+Properties#GenericProperties-messageType
[4]
http://sanjeewamalalgoda.blogspot.com/2015/04/how-to-generate-custom-error-message.html