REST API - Provider
The common used term for a REST API is a Provider. A REST Client is referred to as a Consumer.
Runtime Architecture
The runtime architecture a REST API/Provider is self contained on the IBM i using native components.
An HTTP server is created using the MDRST/MDRHTTPAPI command. This server instance is configured to receive requests from a REST client and call the MDRAPI controller program.
The Provider program is the program generated by the SDK and is called by MDRAPI.
MDRAPI
MDRAPI is an ILE program. It is the only program called as a CGi program by the HTTP server. Its roll is to control the flow of data between the HTTP server and the REST API program.
MDRAPI allocates a unique "handle", gathers all the inbound message data from a request, and sets this up in memory using pointers keyed by the "handle" for each request.
It then calls the REST API program(written by the developer), passing some of the inbound data(handle, method, body) in the initial call. The REST API program, then manipulates the data in memory using calls to the MDRFRAME service program procedures, passing the "handle" passed in by MDRAPI each time.
The REST API program then passes control back to MDRAPI, and MDRAPI returns the response to the REST Client request, via the HTTP server, releasing any CGi JOB resource allocation created with the initial "handle".
ScriptAliasMatch Directive
Because MDRAPI must always be called as a CGi script from the HTTP server instance, the minimum directive required in the httpd.conf is:
ScriptAliasMatch mdrapi /QSYS.LIB/MDRSTT14.LIB/MDRAPI.PGM
Using the above ScriptAliasMatch , these two different URIs would call the MDRSTAPI/MDRAPI CGi program: http://yourapiserver.com/mdrapi/callme
or http://yourapiserver.com/mdrapi/group/callmetoo
The command MDRHTTPAPI automatically adds this directive when creating an API HTTP instance.
Custom ScriptAliasMatch
It is important therefore to either explicitly specify "mdrapi" in the uri to invoke a REST API in V14, or use your own ScriptAliasMatch directive in the httpd.conf file if a different value is preferred. In both cases an httpd.conf server directive must be set to ensure MDRAPI is called. Here are some custom examples:
HTTPD.conf Directive | Request URI |
---|---|
ScriptAliasMatch v1 /QSYS.LIB/MDRSTV14T.LIB/MDRAPI.PGM | http://yourapiserver.com/v1/group/callmetoo |
ScriptAliasMatch ^/MDCMST86/AZURE/RELEASE/CALLBACK /QSYS.LIB/MDCMST86.LIB/MDRAPI.PGM | http://yourapiserver.com//MDCMST86/AZURE/RELEASE/CALLBACK |
URI to API/Provider Program Mapping
MDRAPI determines which API program to call by comparing the path from the URI request, and MDRDCFG.MDRRSC file.value.
Table MDRDCFG is located in the library list of the CGi job for the HTTP server. It maps the request URI and method to the MDRDCFG two primary keys.
MDRAPI first checks for the specific path and method provided in the request, and then if it doesn't find it, it looks for a the path and a blank method.
The path value it uses from the request is either a single element from the path, or a composite value. The control of hwo it matches these two is determined using the environment variables described below in section Provider-API URI Mapping Environment Variables.
MDRDCFG Table
Column | Description |
---|---|
Root Resource MDRRSC | This is user friendly resource name used to invoke the API. MDRAPI will resolve this and call the Program below |
Method METHOD | The HTTP request METHOD |
Program | Name of the API program called by MDRAPI |
Library | Library name used in the call. No value will cause MDRAPI to use *LIBL to make the call |
Maintaining MDRDCFG Records
The command MDRST/MDRSTCFG is used to maintain records in the MDRST/MDRDCFG table.
The MDRest4i SDK Provider generator, creates the MDRDCFG records when it generates the Provider source.
Provider-API URI Mapping Environment Variables
URL/URI and Path
The word "path" used in multiple places in the document, refers to the orange Path to the file section in the URL example below.
Ref: https://developer.mozilla.org/en-US/docs/Learn_web_development/Howto/Web_mechanics/What_is_a_URL
Example URL `http://myserver.com:2598/mdrapi/two/three/four/five?qryParm1=parmvalue1
MDRAPI uses four IBM i environment variables to establish what pattern to use when reading the MDRDCFG file with SQL. They can be set globally for the system, or in the httpd.conf for the HTTP instance used to handle the providers/apis.
There are essentially two patterns used to determine a match between the URI path and the value in MDRDCFG.MDRRSC.
- Individual Path Value - uses
MDREST4I_RESOURCE_COMPONENT
to extract a specific value in the path. - Composite Path Values - uses
MDREST4I_RESOURCE_MATCH
to extract all of part of the URL path.
Here are the descriptions of each of these and their related Environment Variables:
ENVVAR | Description |
---|---|
MDREST4I_RESOURCE_COMPONENT | This variable tells MDRAPI which part of the URI PATH (the part after the host and port number), contains the key to use when reading the MDRDCFG file to determine which program to call. If no value is set for MDREST4I_RESOURCE_COMPONENT, MDRAPI just uses the value of 2. |
MDREST4I_RESOURCE_MATCH | This variable forces MDRAPI to trigger a reverse, cascading search pattern when trying to find a record in MDRDCFG. The number provided in the MDREST4I_RESOURCE_MATCH value, specifies which part of the URI PATH to start the reverse, cascading search pattern at, when trying to find a match in the MDRDCFG.MDRRSC column. For example, if MDREST4I_RESOURCE_MATCH 4 is set, then MDRAPI takes the first 4 parts together to try and find a match in MDRDCFG.MDRRSC, and then the first 3 and so on. If no value is set for MDREST4I_RESOURCE_MATCH it uses only the MDREST4I_RESOURCE_COMPONENT described above. |
MDREST4I_RESOURCE_MATCH_ONLY | This variable tells MDRAPI to NOT USE MDREST4I_RESOURCE_COMPONENT if no match for the resource is found using the MDREST4I_RESOURCE_MATCH search pattern described above. This helps exposing programs being called in the LIBL, that are mistakenly found by MDRAPI when the resource does NOT actually match any resource value in MDRDCFG.MDRRSC. There are two possible values 1 for on and 0 for off. By default it is set to 0/off. |
MDREST4I_LOG_RESOURCE_LOOKUP Y | When this is set to Y , the paths that MDRAPI tries to do database lookups on will be printed to the job log, making it easier to determine why mapping might be failing. By default this value is not set. |
URL/URI Mapping Examples
Environment variables can be set globally for an LPAR or by CGi job.
To set an environment variable for a specific context on an HTTP server instance, use the SetEnvVar
directive within the context it must be applied. These contexts might include:
- directory
- location
- filesematch
In the two examples below, we look at how single and composite values are defined.
Single Path Value Example
<Directory /QSYS.LIB/MYAPILIB.LIB>
Require all granted
SetEnv MDREST4I_LOG_RESOURCE_LOOKUP Y
SetEnv MDREST4I_RESOURCE_MATCH 6
SetEnv MDREST4I_RESOURCE_MATCH_ONLY 1
DefaultNetCCSID 1208
CgiConvMode binary
ServerUserID mdowner
</Directory>
MDRSTCFGR Edit Config 21.07.25
QPADEV000M 15:05:20
Root Resource. . . : two/three/four
Method . . . . . . :
Program. . . . . . : MDRHELLO Library . :
Example Request URL `http://myserver.com:2598/mdrapi/two/three/four?qryParm1=parmvalue1
MDRAPI will look look for the first entry in MDRDCFG by checking for the following keys (in the sequence displayed below):
If any of these matches, it will stop searching and use the program and library defined in MDRDCFG.MDPGM and MDRDCFG.MDLIB respectively. If no match is found, the HTTP response will be:
MDREST4I_RESOURCE_COMPONENT always used
Unless ENVVAR MDREST4I_RESOURCE_MATCH_ONLY 1
is set, MDRAPI will ALWAYS attempt to match using the value of MDREST4I_RESOURCE_COMPONENT
Provider Program
MDRAPI calls the provider program passing three parameters:
Parameter | Description |
---|---|
handle | unique threadsafe memory key used by MDRFRAME to allocate and deallocate memory structures |
method | the HTTP method of the request |
body | the HTTP message request body (if applicable) |
The general flow of the provider program is:
- Handle the extraction of inbound of REST headers, parameters, attachments, parsing payloads etc.
- Call or run your business logic, DB/IFS IO, and calls to IBM i applications/Stored procedures etc.
- Set any error messages.
- Build REST headers and payloads.
- Set the HTTP status.
- Return control to MDRAPI which sends the response via the HTTP server to the REST Client.
This data extraction of the request, and writing of the response is executed by calling the appropriate MDRFRAME procedures (found in the MDRFRAME service program).
Examples
Below is an example of a simple REST Provider program that uses a POST method. The source of these examples can be found in MDRST/EXAMPLES.
Info
In the COBOL example below the MDR-DATAGEN SECTION is used to generate JSON. This function is unique to MDRFRAME, and overcomes the limitation that IBM's DATA-GEN procedure is only available in RPGLE or from V7R2 upwards. The Copybook "MAPIC001C" below shows how the MDR_DATAGEN and MDR_DATAINTO functions use a special 'schema' to parse or generate JSON via the MDRFRAME framework. The MDR_DATAINTO-MDR_DATAGEN section has the details about how to use this.
**free
// CRTBNDRPG PGM(&O/&ON) SRCFILE(&L/&F) OPTION(*EVENTF) DBGVIEW(*ALL) -
// USRPRF(*OWNER) TGTRLS(V7R2M0)
// RPGLE REST API Template Using DATA-GEN and DATA-INTO for JSON.
ctl-opt dftactgrp(*no) actgrp(*new);
ctl-opt BNDDIR('MDRFRAME');
/copy mdrframe
dcl-ds output qualified;
message char(50); // example only : change as required
end-ds;
dcl-ds input qualified;
message char(50); // example only : change as required
end-ds;
dcl-pi *n;
handle like(MDR_Handle_t);
method char(32) const;
body varchar(500000); // MAxSize: 16000000
end-pi;
dcl-s result varchar(1000);
// Logic to process request body
MDR_genParseOptions(handle: 'document_name=input');
data-into input %data('':'')
%parser('MDRFRAME(PARSER)':handle);
eval-corr output = input;
// Logic to process response
MDR_genParseOptions(handle: 'document_name=output');
data-gen output %data(result: '')
%gen('MDRFRAME(GENERATOR)':handle);
*Inlr = *On;
PROCESS varchar
apost
nomonoprc
nosync
nostdtrunc
IDENTIFICATION DIVISION.
PROGRAM-ID. MAPIC001.
AUTHOR. Midrange Dynamics.
*> CRTCBLMOD MODULE(&O/&ON) SRCFILE(&L/&F) DBGVIEW(&DV) -
*> OPTION(*SOURCE *EVENTF *IMBEDERR)
*> CRTPGM PGM(&O/&ON) MODULE(&O/&ON) BNDDIR(MDRFRAME) -
*> ACTGRP(*NEW)
*****************************************************************
ENVIRONMENT DIVISION.
CONFIGURATION SECTION.
SOURCE-COMPUTER. IBM-I.
OBJECT-COMPUTER. IBM-I.
SPECIAL-NAMES.
* MDRest4i Special Names for MDRFRAME Procedures
* Please insert any program specific SEPCIAL NAMES BEFORE this
* COPYBOOK, and the COPYBOOK delimits the divsion with a period
COPY MDRCBLSPC OF QLBLSRC.
*****************************************************************
DATA DIVISION.
*****************************************************************
WORKING-STORAGE SECTION.
* MDRest4i REQ/RSP Payload Schemas
COPY MAPIC001C OF QLBLSRC.
* MDRest4i Framework Variables
COPY MDRCBLWSC OF QLBLSRC.
* MDRest4i Large Framework Variables
COPY MDRCBLWSVS OF QLBLSRC.
* ===============================================================
LINKAGE SECTION.
COPY MDRCBLLSC OF QLBLSRC.
* MDR-HTTP-METHOD - To recive supplied HTTP method
******************************************************************
01 MDR-HTTP-METHOD PIC X(10).
* MDR-INPUT-DATA - To recive input JSON request.
******************************************************************
01 MDR-INPUT-DATA.
10 MDR-INPUT-DATA-LEN PIC 9(9) USAGE BINARY.
10 MDR-INPUT-DATA-BUFFER PIC X(5000000).
* MDR-BODY-TYPE - To recive Body type ( *CAR or *VARCHAR)
******************************************************************
01 MDR-BODY-TYPE PIC X(10).
PROCEDURE DIVISION USING MDR-HANDLE
MDR-HTTP-METHOD
MDR-INPUT-DATA
MDR-BODY-TYPE.
0000-MAIN-CONTROL SECTION.
BEG.
* Clear the MDR-SCHEMA and MDR-PAYLOAD before process the
* MDR-DATAGEN.
INITIALIZE MDR-SCHEMA.
INITIALIZE MDR-PAYLOAD.
* Logic to load response into response data structure.
MOVE 1 TO MDR-NUM-STOCK OF MDR-A00001-RSP.
MOVE 'MTB' TO MDR-DEPARTMENT OF MDR-A00001-RSP(1).
MOVE 'Bikes' TO MDR-MAINCATEGORY OF MDR-A00001-RSP(1).
MOVE 'MTB' TO MDR-SUB-CATEGORY OF MDR-A00001-RSP(1).
MOVE 1 TO MDR-NUM-SIZES OF MDR-A00001-RSP(1).
MOVE 55 TO MDR-SIZES OF MDR-A00001-RSP(1, 1).
MOVE 1 TO MDR-NUM-COLOURS OF MDR-A00001-RSP(1).
MOVE 'Red' TO MDR-COLOURS OF MDR-A00001-RSP(1, 1).
* Set the MDR_PAYLOAD from response data structure.
MOVE MDR-A00001-RSP TO MDR-PAYLOAD.
* Set response Schema into MDR-SCHEMA.
MOVE MDR-A00001-RSP-SCHEMA TO MDR-SCHEMA.
* Set Data options into MDR-DATA-OPTIONS.
MOVE 'countprefix=num- renameprefix=name-'
TO MDR-DATA-OPTIONS
* Generate JSON and send back to output response.
PERFORM MDR-DATAGEN.
GOBACK.
* Default Procedures for MDRest4i Framework
COPY MDRCBLPRC OF QLBLSRC.
*************************************************************************
*// Copybook generated by MDRest4i on 2024-03-07
* ***********************************************************************
*------------------------------------------------------------------------
* MDR-START-UUID : A00001
*------------------------------------------------------------------------
* Response Format UUID : A00001
01 MDR-A00001-RSP-SCHEMA.
10 MDR-A00001-RSP-LEN PIC 9(8) USAGE BINARY VALUE 720.
10 MDR-A00001-RSP-001 PIC X(256) VALUE 'hex:7B226E756D2D53746F
- '636B223A7B223D74797065223A22696E74222C223D6C656E223A313
- '07D2C2253746F636B223A7B223D64696D223A392C22446570617274
- '6D656E74223A7B223D74797065223A2263686172222C223D6C656E2
- '23A337D2C2243617465676F7279223A7B224D61696E43617465676F
- '7279223A7B223D'.
10 MDR-A00001-RSP-002 PIC X(256) VALUE '74797065223A2263686172
- '222C223D6C656E223A357D2C225375622D43617465676F7279223A7
- 'B223D74797065223A2263686172222C223D6C656E223A367D7D2C22
- '6E756D2D53697A6573223A7B223D74797065223A22696E74222C223
- 'D6C656E223A31307D2C2253697A6573223A7B223D74797065223A22
- '696E74222C223D'.
10 MDR-A00001-RSP-003 PIC X(208) VALUE '6C656E223A31302C223D64
- '696D223A397D2C226E756D2D436F6C6F757273223A7B223D7479706
- '5223A22696E74222C223D6C656E223A31307D2C22436F6C6F757273
- '223A7B223D74797065223A2263686172222C223D6C656E223A352C2
- '23D64696D223A397D7D7D'.
* Response Payload UUID : A00001
* Created: 2024-03-07-11.34.39.125000
* Path: /MDRTST14/json/bikes_new.json
01 MDR-A00001-RSP.
05 MDR-NUM-STOCK PIC S9(8) USAGE BINARY VALUE 0.
05 MDR-STOCK OCCURS 9.
10 MDR-DEPARTMENT PIC X(3).
10 MDR-CATEGORY.
15 MDR-MAINCATEGORY PIC X(5).
15 MDR-SUB-CATEGORY PIC X(6).
10 MDR-NUM-SIZES PIC S9(8) USAGE BINARY VALUE 0.
10 MDR-SIZES PIC S9(9) USAGE BINARY OCCURS 9.
10 MDR-NUM-COLOURS PIC S9(8) USAGE BINARY VALUE 0.
10 MDR-COLOURS PIC X(5) OCCURS 9.