Skip to Content
Author's profile photo Sergio Ferrari

BSP / HowTo: BACK Navigation – the nightmare of statefull Web applications

Introduction


Who never got the following message using the BACK Button to resume the last visited page?
<<I>”Warning: Page has Expired. The page you requested was created using information you submitted in a form.
This page is no longer available. As a security precaution, Internet Explorer does not automatically resubmit your information for you.

To resubmit your information and view this Web page, click the Refresh button. “

>  Users do not like that warning page and me too   <

I recently worked on the

BACK Navigation

in a project where a great B2B Portal has been created via a custom BSP application designed according to the MVC pattern.
I discovered some helpful settings, JavaScript statements and finally I implemented the new powerful BSP TAG &LT;zbn:DisableBackNavigation&GT;.
OK, now let’s go into technical details:


APPROACH 1 – SETTINGS – Tuning cache settings

Via SE80 it is possible to specify caching parameters on each controller.

An example:

image

The first thing to know is that the “Page has Expired” warning page is generated by the browser only if Browser or Server cache value is not set.
>  No cache, then get “Warning: Page has Expired. …”.   <

Are you working with

stateless

applications? If yes, set the Browser cache to 3600 and skip the rest of the Blog.
If not, it’s a pity. You are not so lucky. Wait before setting

statefull

applications to use cache.
You will solve the “Page has Expired” problem but you will you introduce even a worst problem.

>  Statefull application with cache, then get the client (Browser) to be NOT synchronized with the server (BSP application) <
Let me explain: let’s take a shopping cart application.
The collection of items in the cart will be maintained in an ABAP internal table binded in the cart view. Then, if a user perform a BACK Navigation he/she will see a previous cart; different items will be displayed in the browser then what are in the ABAP table.
I don’t want to think what will happen if an action is requested for an item that is no more in the cart (add_item(1),add_item(2),drop_item(1),BACK Navigation, BACK Navigation, update_item(1))…


APPROACH 2 – SETTINGS – Hiding BACK Button to the user


In order to avoid BACK Navigation problem, it would be nice to disable the BACK command on the browser.
Too easy. Life is not so.
You could start your application in a new window disabling the all the browser’s buttons.

>  Statefull application in a window without buttons – If your users like it… <
Please note that

right-clicking

on the page the Back command is still there..

In any case, in order to disable the Backspace Navigation,

>  do not forget to make use of the &LT;htmlb:document disableBackspaceNavigation=”TRUE”&GT; <


APPROACH 3 – JAVASCRIPT – An easy and powerful solution


A simple JavaScript function may be coded in your BSP in order to disable the BACK Navigation.

>  With <B>history.forward()</B>, BACK Navigation is no more a Nightmare <

image

Picture – BSP Extension – DisableBackspaceNavigation

image

Picture – BSP Extension – DisableBackspaceNavigation – Attributes

image

Picture – BSP Extension – Class – Attribute

image

Picture – BSP Extension – Class – Methods

image

DO_AT_BEGINNING source code:


Version improved by Mario Vangerven (it uses a cookie):


returnlinkgenerate.

    WHEN ‘TRUE’.

  •     Get returnPage

      DATA: l_return_page TYPE string.

      DATA: o_http_request_warning_page TYPE REF TO if_http_request.

      o_http_request_warning_page = me->m_page_context->get_request( ).

      l_return_page = o_http_request_warning_page->if_http_entity~get_form_field( name = ‘returnPage’ ).

  •     Render A HREF TAG

      CONCATENATE html cl_abap_char_utilities=>cr_lf ‘Server Step Id:  ‘ me->server_step_id ‘ ”);’
cl_abap_char_utilities=>cr_lf ” cl_abap_char_utilities=>cr_lf
INTO html.
CONCATENATE html cl_abap_char_utilities=>cr_lf

”’);’ cl_abap_char_utilities=>cr_lf
‘document.write(”

”);’ cl_abap_char_utilities=>cr_lf
‘document.write(”‘ me->returnlinktext ‘”);’ cl_abap_char_utilities=>cr_lf
‘document.write(”

”);’ cl_abap_char_utilities=>cr_lf
” cl_abap_char_utilities=>cr_lf
” cl_abap_char_utilities=>cr_lf
INTO html.
WHEN OTHERS.
CASE me->type.

  • Render client-side Java Script function history.forward()

        WHEN ‘CLIENT’.

          CONCATENATE html cl_abap_char_utilities=>cr_lf

          ‘Server Step Id:  ‘ me->server_step_id ‘ ”);’ cl_abap_char_utilities=>cr_lf
” cl_abap_char_utilities=>cr_lf
INTO html.
CONCATENATE html cl_abap_char_utilities=>cr_lf
” cl_abap_char_utilities=>cr_lf
” cl_abap_char_utilities=>cr_lf
” cl_abap_char_utilities=>cr_lf

  • Get AgentStepId

          INTO html.

        WHEN ‘SERVER’.

  • Get the cookie from the root path

  • => now we can even jump from one application to the other and still disable the back button

          DATA: o_http_request TYPE REF TO if_http_request.

          o_http_request = me->m_page_context->get_request( ).

          CALL METHOD o_http_request->if_http_entity~get_cookie

            EXPORTING

              name  = ‘AgentStepId’

              path  = ‘/’

            IMPORTING

  • Calculate ServerStepId

              value = me->agent_step_id.

          DATA: l_server_step_id_int TYPE i.

          DATA: l_server_step_id_str TYPE string.

          IF me->agent_step_id IS INITIAL.

            me->server_step_id = ‘0’.

          ELSE.

            IF me->agent_step_id > me->server_step_id.

              me->server_step_id = me->agent_step_id.

              ADD 1 TO me->server_step_id.

            ELSE.

              me->server_step_id = me->agent_step_id.

              ADD 1 TO me->server_step_id.

  • Render complex client-side Java Script function

            ENDIF.

          ENDIF.

  • me->agent_step_id = o_http_request->if_http_entity~get_form_field( name = ‘AgentStepId’ ).

          DATA: o_bsp_runtime TYPE REF TO if_bsp_runtime.

          o_bsp_runtime = me->m_page_context->get_runtime( ).

          CONCATENATE html cl_abap_char_utilities=>cr_lf

          ” cl_abap_char_utilities=>cr_lf

INTO html.
CONCATENATE html cl_abap_char_utilities=>cr_lf
‘Server Step Id:  ‘ me->server_step_id ‘ ”);’

  • Set the cookie from the root path

  • => now we can even jump from one application to the other and still disable the back button

cl_abap_char_utilities=>cr_lf
‘jsv_cookie = “AgentStepId=”+history.length+”; path=/”;’ cl_abap_char_utilities=>cr_lf

          ‘document.cookie=jsv_cookie’ cl_abap_char_utilities=>cr_lf

          ” cl_abap_char_utilities=>cr_lf
” cl_abap_char_utilities=>cr_lf
INTO html.
ENDCASE.
ENDCASE.
*
prev_out->print_string( html ).
*
rc = co_element_continue.

ENDMETHOD.

  • Replace &LT; with <

  • Replace &GT; with &GT;

  • that because in the weblog special characters are not fully supported in the text

Version without cookie:


*!!!!!!!! ATTENTION !!!!!!!!!*

********************************

METHOD if_bsp_element~do_at_beginning .

  DATA: html     TYPE string.

  DATA: prev_out TYPE REF TO if_bsp_writer.

  •     Get returnPage

  prev_out = me-&GT;get_previous_out( ).

  CASE me-&GT;returnlinkgenerate.

    WHEN ‘TRUE’.

      DATA: l_return_page               TYPE string.

  •     Render A HREF TAG

      DATA: o_http_request_warning_page TYPE REF TO if_http_request.

      o_http_request_warning_page = me-&GT;m_page_context-&GT;get_request( ).

      l_return_page = o_http_request_warning_page-&GT;if_http_entity~get_form_field( name  =  ‘returnPage’ ).

      CONCATENATE html cl_abap_char_utilities=&GT;cr_lf

            ‘&LT;script&GT;’ cl_abap_char_utilities=&GT;cr_lf

             ‘document.write(”&LT;B&GT;Server Step Id: &LT;/B&GT; ‘ me-&GT;server_step_id ‘&LT;/br&GT;”);’ cl_abap_char_utilities=&GT;cr_lf

            ‘&LT;/script&GT;’ cl_abap_char_utilities=&GT;cr_lf

             INTO html.

      CONCATENATE html cl_abap_char_utilities=&GT;cr_lf

        ‘&LT;!– DisableBackNavigation Extension – Begin –!&GT;’

        ‘&LT;script language=JavaScript&GT;’ cl_abap_char_utilities=&GT;cr_lf

         ‘document.write(”&LT;form method=”POST” name=”formWBN” action=”‘  l_return_page ‘”&GT;”);’ cl_abap_char_utilities=&GT;cr_lf

         ‘document.write(”&LT;input name=”AgentStepId” type=”HIDDEN” value=””+history.length+””/&GT;”);’ cl_abap_char_utilities=&GT;cr_lf

         ‘document.write(”&LT;A HREF=”javascript:void(document.formWBN.submit())”&GT;’ me-&GT;returnlinktext ‘&LT;/A&GT;”);’ cl_abap_char_utilities=&GT;cr_lf

         ‘document.write(”&LT;/form&GT;”);’ cl_abap_char_utilities=&GT;cr_lf

        ‘&LT;/script&GT;’ cl_abap_char_utilities=&GT;cr_lf

        ‘&LT;!– DisableBackNavigation Extension – End   –!&GT;’ cl_abap_char_utilities=&GT;cr_lf

             INTO html.

  •         Render client-side Java Script function history.forward()

    WHEN OTHERS.

      CASE me-&GT;type.

        WHEN ‘CLIENT’.

          CONCATENATE html cl_abap_char_utilities=&GT;cr_lf

                ‘&LT;script&GT;’ cl_abap_char_utilities=&GT;cr_lf

                 ‘document.write(”&LT;B&GT;Server Step Id: &LT;/B&GT; ‘ me-&GT;server_step_id ‘&LT;/br&GT;”);’ cl_abap_char_utilities=&GT;cr_lf

                ‘&LT;/script&GT;’ cl_abap_char_utilities=&GT;cr_lf

                 INTO html.

          CONCATENATE html cl_abap_char_utilities=&GT;cr_lf

            ‘&LT;!– DisableBackNavigation Extension – Begin –!&GT;’ cl_abap_char_utilities=&GT;cr_lf

            ‘&LT;script language=JavaScript&GT;’ cl_abap_char_utilities=&GT;cr_lf

            ‘history.forward();’ cl_abap_char_utilities=&GT;cr_lf

            ‘&LT;/script&GT;’ cl_abap_char_utilities=&GT;cr_lf

  •         Get AgentStepId

            ‘&LT;!– DisableBackNavigation Extension – End   –!&GT;’ cl_abap_char_utilities=&GT;cr_lf

                 INTO html.

        WHEN ‘SERVER’.

  •         Calculate ServerStepId

          DATA: o_http_request TYPE REF TO if_http_request.

          o_http_request = me-&GT;m_page_context-&GT;get_request( ).

          me-&GT;agent_step_id = o_http_request-&GT;if_http_entity~get_form_field( name  =  ‘AgentStepId’ ).

          DATA: l_server_step_id_int TYPE i.

          DATA: l_server_step_id_str TYPE string.

          IF me-&GT;agent_step_id IS INITIAL.

            me-&GT;server_step_id = ‘0’.

          ELSE.

            IF me-&GT;agent_step_id &GT; me-&GT;server_step_id.

              me-&GT;server_step_id = me-&GT;agent_step_id.

              ADD 1 TO me-&GT;server_step_id.

            ELSE.

              me-&GT;server_step_id = me-&GT;agent_step_id.

  •         Render complex client-side Java Script function

              ADD 1 TO me-&GT;server_step_id.

            ENDIF.

  •          me-&GT;agent_step_id = o_http_request-&GT;if_http_entity~get_form_field( name  =  ‘AgentStepId’ ).

          ENDIF.

          DATA: o_bsp_runtime TYPE REF TO if_bsp_runtime.

          o_bsp_runtime = me-&GT;m_page_context-&GT;get_runtime( ).

          CONCATENATE html cl_abap_char_utilities=&GT;cr_lf

            ‘&LT;!– DisableBackNavigation Extension – Begin –!&GT;’ cl_abap_char_utilities=&GT;cr_lf

            ‘&LT;script language=JavaScript&GT;’ cl_abap_char_utilities=&GT;cr_lf

            ‘ if (‘ me-&GT;server_step_id ‘&GT;0)’ cl_abap_char_utilities=&GT;cr_lf

            ‘  {‘ cl_abap_char_utilities=&GT;cr_lf

            ‘   if (history.length&LT;’ me-&GT;server_step_id ‘)’ cl_abap_char_utilities=&GT;cr_lf

            ‘    history.forward();’ cl_abap_char_utilities=&GT;cr_lf

            ‘   else ‘ cl_abap_char_utilities=&GT;cr_lf

            ‘    {‘ cl_abap_char_utilities=&GT;cr_lf

            ‘     if (history.length&GT;’ me-&GT;server_step_id ‘)’ cl_abap_char_utilities=&GT;cr_lf

            ‘      {‘ cl_abap_char_utilities=&GT;cr_lf

            ‘       location.href=”’ me-&GT;warningpage ‘?returnPage=’ o_bsp_runtime-&GT;page_name ”’;’ cl_abap_char_utilities=&GT;cr_lf

            ‘      }’ cl_abap_char_utilities=&GT;cr_lf

            ‘    }’ cl_abap_char_utilities=&GT;cr_lf

            ‘  }’ cl_abap_char_utilities=&GT;cr_lf

            ‘&LT;/script&GT;’

                 INTO html.

          CONCATENATE html cl_abap_char_utilities=&GT;cr_lf

            ‘&LT;script language=JavaScript&GT;’ cl_abap_char_utilities=&GT;cr_lf

             ‘document.write(”&LT;B&GT;Server Step Id: &LT;/B&GT; ‘ me-&GT;server_step_id ‘&LT;/br&GT;”);’ cl_abap_char_utilities=&GT;cr_lf

             ‘document.write(”&LT;input name=”AgentStepId” type=”HIDDEN” value=””history.length””/&GT;”);’ cl_abap_char_utilities=&GT;cr_lf

            ‘&LT;/script&GT;’ cl_abap_char_utilities=&GT;cr_lf

            ‘&LT;!– DisableBackNavigation Extension – End   –!&GT;’ cl_abap_char_utilities=&GT;cr_lf

                 INTO html.

      ENDCASE.

  ENDCASE.

*

  prev_out-&GT;print_string( html ).

*

  rc = co_element_continue.

*

ENDMETHOD.

A SAMPLE APPLICATION

Create a statefull BSP application:

Picture – Web application

image

Create following controllers:

Picture – Controller for the client implementation

image

Picture – Controller for the Server implementation

image

Picture – Controller for the warning Page

image

Create following views:

mainClientimplementation.htm

That’s all.

Assigned Tags

      5 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Former Member
      Former Member
      Grats Sergio, really enjoyed reading this - I not only like the approaches you described, but also the way you implemented it.
      Looking forward to try this out when things have settled down here a bit (first user test for our main application coming up).
      Author's profile photo Former Member
      Former Member
      Hi Sergio,

      Thanks a million for this weblog. I just got to showcase my (teeny-weeny) talent of how I know about caching after reading this weblog to a bunch of "expert" BSP programmers.

      Regards,
      Subramanian V.

      Author's profile photo Former Member
      Former Member
      Great Extension Sergio, it works fine in 90% of the cases. However lately I am messing around in the 10% borders.

      I have a huge application where from time to time I navigate to another page using the navigation->goto_page() method. In this case your extension doesn't retain the ServerStepID.

      To solve this I changed your DO_AT_BEGINNING() method to use client side cookies in stead of a hidden input field. This seems to do the trick.

      Here is the modified method
      *==============================================
      *!!!!!!!! ATTENTION !!!!!!!!!*
      * Replace < with <<br/>* Replace > with >
      * that because in the weblog special characters are not fully supported in the text
      ********************************
      METHOD if_bsp_element~do_at_beginning .

      DATA: html TYPE string.
      DATA: prev_out TYPE REF TO if_bsp_writer.

      prev_out = me->get_previous_out( ).
      CASE me->returnlinkgenerate.
      WHEN 'TRUE'.
      * Get returnPage
      DATA: l_return_page TYPE string.
      DATA: o_http_request_warning_page TYPE REF TO if_http_request.
      o_http_request_warning_page = me->m_page_context->get_request( ).
      l_return_page = o_http_request_warning_page->if_http_entity~get_form_field( name = 'returnPage' ).
      * Render A HREF TAG
      CONCATENATE html cl_abap_char_utilities=>cr_lf
      'Server Step Id:  ' me->server_step_id '
      '');' cl_abap_char_utilities=>cr_lf
      '' cl_abap_char_utilities=>cr_lf
      INTO html.
      CONCATENATE html cl_abap_char_utilities=>cr_lf
      ''
      ''');' cl_abap_char_utilities=>cr_lf
      'document.write(''

      '');' cl_abap_char_utilities=>cr_lf
      'document.write(''' me->returnlinktext ''');' cl_abap_char_utilities=>cr_lf
      'document.write(''

      '');' cl_abap_char_utilities=>cr_lf
      '' cl_abap_char_utilities=>cr_lf
      '' cl_abap_char_utilities=>cr_lf
      INTO html.
      WHEN OTHERS.
      CASE me->type.
      WHEN 'CLIENT'.
      * Render client-side Java Script function history.forward()
      CONCATENATE html cl_abap_char_utilities=>cr_lf
      'Server Step Id:  ' me->server_step_id '
      '');' cl_abap_char_utilities=>cr_lf
      '' cl_abap_char_utilities=>cr_lf
      INTO html.
      CONCATENATE html cl_abap_char_utilities=>cr_lf
      '' cl_abap_char_utilities=>cr_lf
      '' cl_abap_char_utilities=>cr_lf
      '' cl_abap_char_utilities=>cr_lf
      INTO html.
      WHEN 'SERVER'.
      * Get AgentStepId
      DATA: o_http_request TYPE REF TO if_http_request.
      o_http_request = me->m_page_context->get_request( ).
      * Get the cookie from the root path
      * => now we can even jump from one application to the other and still disable the back button
      CALL METHOD o_http_request->if_http_entity~get_cookie
      EXPORTING
      name = 'AgentStepId'
      path = '/'
      IMPORTING
      value = me->agent_step_id
      .

      * Calculate ServerStepId
      DATA: l_server_step_id_int TYPE i.
      DATA: l_server_step_id_str TYPE string.
      IF me->agent_step_id IS INITIAL.
      me->server_step_id = '0'.
      ELSE.
      IF me->agent_step_id > me->server_step_id.
      me->server_step_id = me->agent_step_id.
      ADD 1 TO me->server_step_id.
      ELSE.
      me->server_step_id = me->agent_step_id.
      ADD 1 TO me->server_step_id.
      ENDIF.
      ENDIF.
      * Render complex client-side Java Script function
      DATA: o_bsp_runtime TYPE REF TO if_bsp_runtime.
      o_bsp_runtime = me->m_page_context->get_runtime( ).
      * me->agent_step_id = o_http_request->if_http_entity~get_form_field( name = 'AgentStepId' ).
      CONCATENATE html cl_abap_char_utilities=>cr_lf
      '' cl_abap_char_utilities=>cr_lf
      ''
      INTO html.
      CONCATENATE html cl_abap_char_utilities=>cr_lf
      'Server Step Id:  ' me->server_step_id '
      '');' cl_abap_char_utilities=>cr_lf
      'jsv_cookie = "AgentStepId="+history.length+"; path=/";' cl_abap_char_utilities=>cr_lf
      * Set the cookie from the root path
      * => now we can even jump from one application to the other and still disable the back button
      'document.cookie=jsv_cookie' cl_abap_char_utilities=>cr_lf
      '' cl_abap_char_utilities=>cr_lf
      '' cl_abap_char_utilities=>cr_lf
      INTO html.
      ENDCASE.
      ENDCASE.
      *
      prev_out->print_string( html ).
      *
      rc = co_element_continue.

      ENDMETHOD.
      *==============================================

      To test this modification implent the method
      DO_HANDLE_EVENT in the controller
      METHOD do_handle_event .
      IF htmlb_event->server_event = 'myCmd'.
      navigation->goto_page( 'mainServerImpementation.do' ).
      ENDIF.
      ENDMETHOD.

      Author's profile photo Sergio Ferrari
      Sergio Ferrari
      Blog Post Author
      Thanks so much Mario,
        this is a pleasure example of open-source.
        Coding becomes better and better thanks to a community...
       
        It would be nice if anybody will help me in discovering how to make the BSP extension working also when the Delta Handling is activated...

      Sergio

      Author's profile photo Daniel Humberg
      Daniel Humberg
      You mentioned an open issue in approach 3.
      You want to do the history.foward() only, if the user is not coming from an external page.
      Have you tried reading the header field 'referrer'? (like this: request->get_header_field( 'referer' ). If you put the result into an hidden input field, you might be able to read it via JavaScript.