human readable URLs in Oracle APEX – Part 2

In the first part of this Blogpost Series we touched some fundamentals and finally listed some options. If you haven’t read it, yet, please do now before continuing here.

The last post ended with me saying I found a way for hrURLs that works with APEX Listener, mod_plsql and EPG.

I haven’t tested the Thoth Gateway, but based on the documentation I’d say it works as well as the mod_plsql way.

Just to be fair: I haven’t invented any of this. I merely aggregated and refined information I found in several blogposts and forum entries. Most of those are linked in my previous post.

The Technique

mod_plsql and Embedded PL/SQL Gateway

EPG basically supports the same things that mod_plsql offers, so we don’t have to differentiate between those two.

The magic here lies in the configuration of the dad. This configuration object has an option which is perfect for us: Path Aliasing, the configuration of Direct Access URLs through PlsqlPathAlias and PlsqlPathAliasProcedure.

Example:

PlsqlPathAlias          ragtest
PlsqlPathAliasProcedure rag_url_mapping

This additional configuration in the DAD tells mod_plsql to invoke the procedure rag_url_mapping, whenever the keyword ragtest is encountered next to your DAD in the URL.

Lets assume your DAD is named apex, since that would be the default for most uses with Oracle APEX, the the procedure rag_url_mapping would be called for all URLs like myserver/pls/apex/ragtest/….

Whatever is specified in the URL after the Path Alias will be passed to the procedure in its one and only argument p_path.

PROCEDURE rag_url_mapping ( p_path IN VARCHAR2 )
IS
BEGIN
HTP.P('You passed the URL: '||p_path);
END;

APEX Listener

The APEX Listener is the only one of the available Weblistener options which is actively developed nowadays and therefor the only one supporting the new RESTful Webservices Option in Oracle APEX 4.2+.

In here we define a new Webservice, which will provide a GET method using PL/SQL

Since we haven’t specified any URI Prefix and use {path} as sole URI Template this Webservice will act in a catch-all way.

Lets assume the APEX Listener is deployed as apex and our workspace is named rag_demo, then this Webservice will will be called for all URLs looking like this: myserver/apex/rag_demo/….

Whatever is specified after the workspace-name in the URL will be passed to the bind-variable :path and can be used in your PL/SQL code.

Now instead of having inline PL/SQL code here, we could call a procedure:

BEGIN
rag_url_mapping(:path);
END;

See? We can actually call the same procedure, as we would do when using mod_plsql/EPG.

So What?

Right, I have explained that you can call a PL/SQL Procedure when using a certain URL-Pattern, but I didn’t tell you what to do with that. Above examples use HTP.P calls to output Text to the Browser, of course we don’t want to do that.

We want to invoke APEX and have APEX itself do all the work.

As you know (if not please go back to the documentation and read about APEX), APEX itself is just a PL/SQL Procedure, which is called via the URL. The name of that function is F and its most important argument is P. Looks familiar? myserver/apex/f?p=xxx

So all you need to do in your procedure is calling F and pass the argument P which APEX Application and Page you want to call, using Session Zero, of course.

Example:

PROCEDURE URL_DEMO 
  ( p_path IN VARCHAR2
  )
AS
    v_path APEX_APPLICATION_GLOBAL.VC_ARR2;
BEGIN
    v_path := APEX_UTIL.STRING_TO_TABLE(p_path||'/', '/');
    --
    IF UPPER(v_path(1)) = 'HOME'
    THEN
        F(P=>'700:1:0');
    ELSIF UPPER(v_path(1)) = 'CUSTOMER'
    THEN
        IF UPPER(v_path(2)) = 'EDIT'
        THEN
            F(P=>'700:3:0:::3:P3_CUSTOMER_ID:'||v_path(3));
        ELSE
            F(P=>'700:2:0::YES');
        END IF;
    ELSE
        HTP.P('URL_DEMO called!<br>');
        HTP.P('...path = '||p_path);
    END IF;
END;

Sourcecode Explained

This example handles the URL’s

  • …/home
  • …/customer
  • …/customer/edit/<id>

Line 7 takes the URL and splits the string up at the / and stores the result in an array, using the convenient APEX_UTIL function String_To_Table.

Line 9-11 handle /home and call APEX Application 700 with Page 1.

Line 12 handles /customer and checks further if /edit follows. In case of EDIT Page 3 is called and gets passed the customer-id into P3_CUSTOMER_ID.

Line 18 gives us the customer report on Page 2 (in debug mode, apparently).

Lines 21/22 are outputting any other unhandled URL combination.

Database Sample App using hrURL

I set up another demo and modified the Database Sample Application on apex.oracle.com to use above described techniques to show nice URLs.

 

In the next blogpost I’m going to tell you what you need to change in your application to support hrURLs. Stay tuned.

14 thoughts on “human readable URLs in Oracle APEX – Part 2

  1. Thanks, Roel, this could be a nice exercise for you :-)

    I don’t really see the need for that, since friendly URLs are more about accessing a page, than saving data.

  2. Nice post.
    Can’t wait too see how you have integrated it in the apex application

    What about the other parameters of the APEX URL?
    Session:Request:Debug:ClearCache:Params:ParamValues:PrinterFriendly

    Would you store those values in an application context?
    So it is available when you navigate to other pages or appilcation?
    I am especially concerned about the session id.

    You have hardcoded the URL’s in the URL_DEMO procedure.
    Wouldn’t it be wiser to create a table where you can define the parameters linked to certain applications, pages and other APEX URL settings?

    In a production environment this would be great, I think.
    The end user has the friendly URL that for example links to appid= 111 in the URL_table
    When a new version of the application is deployed with appid=112, you only have to do an update on the URL_table when then new application should be come available to the end user.
    It means you can have two versions of the application available, while the end user only seems one of them.

    Somehow it sounds to me that APEX could provide this out-of-the-box (table, procedure and application builder page for the configuration of the table) , don’t you agree?.

  3. Mathieu,

    the possibilities are endless. You can use a table to configure an apex page to URL mapping or do it programatically, as shown in the example.

    The other APEX URL Parameters could be coded into the URL as well, its just a matter of what you actually want to see in the URL. Session will always be zero, since this solution will work for public pages only (for now).

    And yes, one day APEX will provide nice/friendly/readable URLs out of the box.

  4. Pingback: Understanding the APEX url – passing variables to items and (interactive) reports | Dimitri Gielis

  5. good morning Peter,

    Thank you for sharing this information.

    I tried to reproduce their http://apex.oracle.com/pls/apex/hrurl/home example and do not get the redirect occurs.
    I created a small test application and when I try to access via the new URL, a browser window with the message “status: 302” appears.
    This is the application you’ve created: https://apex.oracle.com/pls/apex/f?p=CLO:HOME
    And this would be the new url: https://apex.oracle.com/pls/apex/ccv/clo/home

    The plsql code I used is very simple and is based on his example:

    declare
    ur varchar2(4000);
    begin
    ur := :app || ‘:’ || :page;
    f(p=>ur);
    end;

    What can be wrong?
    Thank you very much for your time.
    regards
    Juan Perez

  6. Juan,

    “302” would be a HTTP redirect message, strange that it is printed rather than executed. can you try passing 0 as session id in your plsql code?

  7. This works good to make my friendly URLs. However, none of my css files/images are displaying now. Do I need to change something in order to CSS and image links to work, now that the URL contains another “level”? thanks.

  8. I am getting too many redirects error. it happens when i link webportal to other portal in apex..
    kindly help

  9. Hello.

    Is this possible in combination with glassfish and ords?
    Thanks.

    BR,
    Dejan

  10. Hi ,
    Is this applicable only for MOD_PLSQL AND EMBEDDED PL/SQL GATEWAY
    if yes then how we can achieve this for ORDS or Apache

  11. @Rajan: this is for ORDS. Please start reading the posts from the first part of the series to the last. Everything should be explained in there.

Leave a Reply

Your email address will not be published. Required fields are marked *