ORDS tips
XML over ORDS
Turns out you can emit XML via ORDS. I'll assume you have a working ORDS install and the schema is already enabled
BEGIN
ORDS.DEFINE_MODULE(
p_module_name => 'reporting',
p_base_path => '/reporting/',
p_items_per_page => 0,
p_status => 'PUBLISHED',
p_comments => NULL);
ORDS.DEFINE_TEMPLATE(
p_module_name => 'reporting',
p_pattern => 'hello_xml',
p_priority => 0,
p_etag_type => 'HASH',
p_etag_query => NULL,
p_comments => NULL);
ORDS.DEFINE_HANDLER(
p_module_name => 'reporting',
p_pattern => 'hello_xml',
p_method => 'GET',
p_source_type => 'plsql/block',
p_items_per_page => 0,
p_mimes_allowed => '',
p_comments => NULL,
p_source =>
'DECLARE
l_clob CLOB;
BEGIN
SELECT
''<xml>hello world</xml>''
INTO
l_clob
FROM
dual;
owa_util.mime_header(
ccontent_type => ''text/xml'',
bclose_header => true,
ccharset => ''ISO-8859-4''
);
htp.print(l_clob);
END;'
);
COMMIT;
END;
$ curl -v http://oracle.rocks:8080/ords/scott/reporting/mime_works
* About to connect() to oracle.rocks port 8080 (#0)
* Trying 1.2.3.4... connected
* Connected to oracle.rocks (1.2.3.4) port 8080 (#0)
> GET /ords/scott/reporting/mime_works HTTP/1.1
> User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.27.1 zlib/1.2.3 libidn/1.18 libssh2/1.4.2
> Host: oracle.rocks:8080
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: text/xml; charset=ISO-8859-4
< ETag: "WaloI8WDL3PY2G0ZN6+I8C+c0FxVaUBuDc/v7LKXTpE6dTuJR1s2bLF/0hqW2fVzaXNYpr9TFXqucyoq6dO2Xw=="
< Transfer-Encoding: chunked
<
<xml>hello world</xml>
* Connection #0 to host oracle.rocks left intact
* Closing connection #0
Important item of note is the mime header, it's important that bclose_header
is always true
because this is literally the last HTTP header before the response body. Setting this to false will result in no data.
References
Emitting JSON via refcursor
Earlier this year Tim Hall did an excellent talk called, "Make the RDBMS Relevant Again with RESTful Web Services and JSON" (youtube) at Oracle Code 2017. The big takeaway for me was to not throw away your existing investment and rewrite everything to fit the new technology (ORDS). Instead you should use ORDS to wrap around the existing objects.
At the day job we use strongly typed reference cursor as an out parameter (this helps interoperability with Microsoft's SSRS). Here's the example "reporting" package.
CREATE OR REPLACE PACKAGE foo_pkg AS
/* strongly typed reference cursors or gtfo */
TYPE bar_reftype IS RECORD (
object_name user_objects.object_name%TYPE,
object_type user_objects.object_type%TYPE
);
TYPE bar_refcur IS REF CURSOR RETURN bar_reftype;
PROCEDURE bar_get (p_recordset IN OUT bar_refcur);
END foo_pkg;
/
CREATE OR REPLACE PACKAGE BODY foo_pkg AS
PROCEDURE bar_get ( p_recordset IN OUT bar_refcur )
AS
BEGIN
OPEN p_recordset FOR
SELECT
uo.object_name,
uo.object_type
FROM
user_objects uo
WHERE
ROWNUM < 5;
EXCEPTION
WHEN OTHERS THEN
IF
p_recordset%isopen
THEN
CLOSE p_recordset;
END IF;
RAISE;
END bar_get;
END foo_pkg;
/
Here's the ORDS wrapper, yes unfortunately you're going to need APEX_JSON
, but the example should be easy enough to understand. It would be great if one day we didn't need this boilerplate, but for now this is a handy way of reusing code.
BEGIN
ORDS.DEFINE_MODULE(
p_module_name => 'foo',
p_base_path => '/foo/',
p_items_per_page => 0,
p_status => 'PUBLISHED',
p_comments => NULL);
ORDS.DEFINE_TEMPLATE(
p_module_name => 'foo',
p_pattern => 'bar/',
p_priority => 0,
p_etag_type => 'HASH',
p_etag_query => NULL,
p_comments => NULL);
ORDS.DEFINE_HANDLER(
p_module_name => 'foo',
p_pattern => 'bar/',
p_method => 'GET',
p_source_type => 'plsql/block',
p_items_per_page => 0,
p_mimes_allowed => '',
p_comments => NULL,
p_source =>
'DECLARE
l_cursor SYS_REFCURSOR;
BEGIN
foo_pkg.bar_get(l_cursor);
apex_json.open_object;
apex_json.write(''bar'', l_cursor);
apex_json.close_object;
END;'
);
COMMIT;
END;
/