Recently, I was working on a project where I had a function that needed to handle GET, POST, and DELETE methods differently and I started searching on how could I know in my Function the request method.
While searching the internet, I stumbled upon the Fn Python FDK source code, and I saw some information about the request context in here.
Let’s look at some information available within a running function.
Function Handler
When working with OCI Functions using the following programming languages: Go, Java, Node.js, Python, or Ruby, a Function Development Kit (FDK) is available. In a couple of words, the FDKs are libraries that simplify the life of developers.
I’ll be using the Python FDK in my examples.
Let’s look at a hello-world sample Python Function:
import json import io from fdk import response def handler(ctx, data: io.BytesIO=None): name = "World" try: body = json.loads(data.getvalue()) name = body.get("name") except (Exception, ValueError) as ex: print(str(ex)) pass return response.Response( ctx, response_data=json.dumps( {"message": "Hello {0}".format(name)}), headers={"Content-Type": "application/json"} )
Note: if you want to see how to set up your development environment for OCI Functions, check this article.
This handler method is invoked when your Function is called by other services.
If we take a look at the handler, there are two parameters passed: ctx and data.
- ctx is a context object that provides information about the function and request-specific attributes
- data is an object passed by the trigger request containing the payload – the HTTP request body, when calling the function using HTTP
Data
When the function is triggered by an HTTP request, a payload can be sent within the request body – the payload can be up to 6 Mb.
As we see in the example above, the data inside the body can be read as follows:
body = json.loads(data.getvalue())
Context
The interesting part is in the ctx object which offers a handful of attributes that give a lot of useful information:
- app_id, type: str
- this is the OCID of the application
- fn_id, type: str
- this is the OCID of the function
- call_id, type: str
- unique identifier of the request
- content_type, type: str
- request content type
- deadline, type: str
- request deadline
- config, type: dict
- Configuration data about the application & function
- headers, type: str
- HTTP headers included with the request
- request_url, type: str
- The URL used for the request
- method, type: str
- The HTTP method used for the request
- fn_format, type: str
- Format of communication
Let’s look at an example:
import io import json import logging from fdk import response def handler(ctx, data: io.BytesIO=None): name = "World" try: body = json.loads(data.getvalue()) name = body.get("name") except (Exception, ValueError) as ex: print(str(ex)) return response.Response( ctx, response_data=json.dumps( {"Message": "Hello {0}".format(name), "ctx.AppID" : ctx.AppID(), "ctx.FnID" : ctx.FnID(), "ctx.CallID" : ctx.CallID(), "ctx.Config" : dict(ctx.Config()), "ctx.Headers" : ctx.Headers(), "ctx.Deadline" : ctx.Deadline(), "ctx.RequestURL": ctx.RequestURL(), "ctx.Method": ctx.Method(), "ctx.Format" : ctx.Format()}), headers={"Content-Type": "application/json"} )
I’m going to return the context information in the response.
I have exposed my function through an API Gateway, and this is the response I get when invoking that endpoint via Postman:
{ "Message": "Hello World", "ctx.AppID": "ocid1.fnapp.oc1.eu-frankfurt-1.aaaaaaaaagphdvq", "ctx.FnID": "ocid1.fnfunc.oc1.eu-frankfurt-1.aaaaaaaaabbfw6b", "ctx.CallID": "01E7G1XCBF1BT0E1GZJ000J0VY", "ctx.Config": { "PATH": "/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "HOSTNAME": "7b84d608d05e", "FN_LISTENER": "unix:/tmp/iofs/lsnr.sock", "FN_FORMAT": "http-stream", "FN_APP_ID": "ocid1.fnapp.oc1.eu-frankfurt-1.aaaaaaaaagphdv", "FN_CPUS": "100m", "FN_FN_ID": "ocid1.fnfunc.oc1.eu-frankfurt-1.aaaaaaaaabbfw6", "FN_MEMORY": "256", "FN_TYPE": "sync", "OCI_RESOURCE_PRINCIPAL_RPST": "/.oci-credentials/rpst", "OCI_RESOURCE_PRINCIPAL_PRIVATE_PEM": "/.oci-credentials/private.pem", "OCI_RESOURCE_PRINCIPAL_VERSION": "2.2", "OCI_RESOURCE_PRINCIPAL_REGION": "eu-frankfurt-1", "OCI_REGION_METADATA": "{\"realmDomainComponent\":\"eu-frankfurt-1.oraclecloud.com\",\"realmKey\":\"oc1\",\"regionIdentifier\":\"eu-frankfurt-1\",\"regionKey\":\"FRA\"}", "LANG": "C.UTF-8", "GPG_KEY": "0D96DF4D4110E5C43FBFB17F2D347EA6AA65421D", "PYTHON_VERSION": "3.6.10", "PYTHON_PIP_VERSION": "20.0.2", "PYTHON_GET_PIP_URL": "https://github.com/pypa/get-pip/raw/d59197a3c169cef378a22428a3fa99d33e080a5d/get-pip.py", "PYTHON_GET_PIP_SHA256": "421ac1d44c0cf9730a088e337867d974b91bdce4ea2636099275071878cc189e", "PYTHONPATH": "/function:/python", "HOME": "/home/fn" }, "ctx.Headers": { "host": "localhost", "user-agent": "PostmanRuntime/7.24.1", "transfer-encoding": "chunked", "content-type": "application/octet-stream", "date": "Mon, 04 May 2020 14:37:19 GMT", "fn-call-id": "01E7G1XCBF1BT0E1GZJ000J0VY", "fn-deadline": "2020-05-04T14:37:50Z", "accept": "*/*", "accept-encoding": "gzip", "authorization": "Signature version=\"1\",keyId=\"ocid1.tenancy.oc1..aaaaaaaa3ed2rov4/ocid1.user.oc1..aaaaaaaajw4xye/09:54:ba:d5:2b:36:5c\",algorithm=\"rsa-sha256\",headers=\"(request-target) date host\",signature=\"ep/Pa1oPng+MWYlODdQ32i/lYqIgEk5onO+zaUe1e1IYxyrQc/BEzeyIZ0wcYqbggZ2qA==\"", "cache-control": "no-cache", "connection": "keep-alive", "orwarded": "for=0.0.0.0", "ost": "gbs2yh2kfafgxz4a.apigateway.eu-frankfurt-1.oci.customer-oci.com", "ostman-token": "256b1-72f4-49ad-ab3e-2c6de462", "x-forwarded-for": "0.0.0.0", "x-real-ip": "0.0.0.0", "fn-http-method": "GET", "fn-http-request-url": "/v1/hello", "fn-intent": "httprequest", "fn-invoke-type": "sync", "oci-subject-id": "ocid1.apigateway.oc1.eu-frankfurt-1.amaaaaaaxycoxjy", "oci-subject-tenancy-id": "ocid1.tenancy.oc1..aaaaaaaa3ed2rov", "oci-subject-type": "resource", "opc-request-id": "/35B9F56E3F7CE314021DEFB1F6D38AC1/01E7G1XCAV1BT0E1GZJ000J0VX", "x-content-sha256": "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=" }, "ctx.Deadline": "2020-05-04T14:37:50Z", "ctx.RequestURL": "/v1/hello", "ctx.Method": "GET", "ctx.Format": "http-stream" }
In my use case, the ctx.Method was exactly what I was looking for – using this, I could know if the call was a GET, POST, or DELETE.
As OCI Functions is based on Fn Project which is an open-source tool, you can always found out more by browsing the code.
The Python FDK can be found here.
The code can be found on GitHub as well.