Skip to main content

Server-Side Methods

Server-Side Methods

CSP offers two techniques for invoking server-side methods from an HTML client.

  • Using the HTTP Submit mechanism.

  • Using the hyperevents, either #server (synchronous) or #call (asynchronous). You can also use the HyperEventCall() method of %CSP.PageOpens in a new tab; for details and an example, see the class reference.

The advantages of using HTTP Submit are that client-side programming is simple and no client-side components are needed. Its disadvantages are that the page is repainted by the client after a method call and server-side programming is more difficult.

If you use hyperevents, #server and #call are implemented using XMLHttpRequest. #call is asynchronous: if you (as a user) enter a value on a web page, the page is not updated immediately; you may have moved to another page by the time it is updated. #server is synchronous; the page is updated immediately on the return from the call.

Note that synchronous XMLHttpRequest is deprecated by many browsers, and in general, movement is toward only supporting the asynchronous XMLHttpRequest.

HTTP Submit and hyperevents are described further in the sections below.

Caché and AJAX

The acronym AJAX is commonly used to refer to a set of technologies that allows you to update a client-side page's data from the server without having to request a new browser page. Caché hyperlinks allow AJAX interactions without requiring the programmer to handle all the messy communications with the server. Two ways Caché allows AJAX transactions:

  1. For CSP the use of the #server() and #call() commands allow direct calling of server-side methods from the client. (You can also use the HyperEventCall() method of %CSP.PageOpens in a new tab; for details and an example, see the class reference.)

  2. For Zen, the programmer may define ZenMethods which handle client-server interactions. These may be synchronous or asynchronous depending on the method’s signature:

Signature for a synchronous AJAX request (the method returns a %Status):

Method XYZ(arg) as %Status [ZenMethod]    

Signature for an asynchronous AJAX request:

Method XYZ() [ZenMethod]              

Parallel Processing with AJAX

AJAX requests to CSP are processed sequentially on the server because of locks on nodes of ^%cspSession global. To enable AJAX requests to be processed in parallel, if the application you are working with does not SET anything in the session global/object (so only reads), you can use the %CSP.Session.Unlock() method to unlock the CSP global for that session and %session.Lock at the end of processing.

For more information see %CSP.SessionOpens in a new tab in the class reference.

Calling Server-side Methods via HTTP Submit

Invoking server code with an HTTP Submit requires very little functionality from the browser. It is a good technique for applications that have a wide audience and must support a wide variety of browsers. When you use an HTTP Submit, the requested page is redisplayed each time that the user clicks a SUBMIT button.

You can handle HTTP submits with the following:

  1. Serve an HTML form containing a SUBMIT button:

    <form name="MyForm" action="MyPage.csp" method="GET">
    User Name: <input type="TEXT" name="USERNAME"><br>
    <input type="SUBMIT" name="BUTTON1" value="OK">
    </form>

    This defines a simple form with a text field called USERNAME and a SUBMIT button called BUTTON1. The ACTION attribute of the form specifies the URL to which the form is submitted. The METHOD attribute specifies which HTTP protocol is used to submit the form: POST or GET.

  2. When the user clicks BUTTON1, the SUBMIT button, the browser collects the values of all controls in the form and submits them to the URL specified by the form's ACTION attribute. (Note that a page can submit back to itself either by specifying its name with the ACTION attribute or by leaving the ACTION attribute blank.) Regardless of whether a form is submitted via POST or GET, CSP treats the submitted values as if they were URL parameters. In this case, submitting the form is equivalent to requesting the following URL:

    MyPage.csp?USERNAME=Elvis&BUTTON1=OK
    

    The name and value of the SUBMIT button is included. If there are multiple SUBMIT buttons on a form, only the data button that was actually pressed is included in the request. This is the key to detecting when a SUBMIT has occurred.

  3. The server code, in this case MyPage.csp, detects that a submit has occurred. It does this by testing for the name BUTTON1 in the %request object:

    <script language="Cache" runat="SERVER">
       // test for a submit button
       If ($Data(%request.Data("BUTTON1",1))) {
          // this is a submit; call our method
          Do ..MyMethod($Get(%request.Data("USERNAME",1)))
       }
    </script>
  4. After invoking the desired server-side logic, the server code continues and returns HTML for display by the browser. This could be a redisplay of the current form or a different page.

Calling Server-side Methods Using Hyperevents #server and #call

A hyperevent is our CSP extension of a web browser event and a web development technique for creating an interactive web action. Using hyperevents, you can run methods of classes on a Caché server in response to events in a client web browser without reloading the HTML page on the client. This capability is commonly called AJAX in the wider world. Caché hyperevents are useful in a number of cases, especially database applications where you may want to perform validation or search functions against a database without having to reload and reformat an entire web page. Usually, hyperevents are implemented using XMLHttpRequest.

If you are developing CSP pages using classes, you need to call the ..HyperEventHead method during the output of the <head> section to load the JavaScript needed for hyperevents.

Calling Server-side Methods Using #server

Within a CSP file, you can invoke a server-side method using the #server directive. You can use this directive anywhere that JavaScript is allowed.

The syntax of the #server directive is:

#server(classname.methodname(args,...))#

where classname is the name of a server-side Caché class and methodname is the name of a method in the class. args is a list of client-side JavaScript arguments that are passed to the server-side method. Note that all code between the opening and closing #signs must all be on a single line.

For example, to invoke a server-side method named Test in the Caché class MyPackage, use:

<script language="JavaScript">
function test(value)
{
   // invoke server-side method Test
   #server(MyPackage.Test(value))#;
}
</script>

The CSP compiler replaces each occurrence of #server directive with JavaScript code that invokes the server-side method.

From a given CSP page, you can invoke a method belonging to the class generated for it using ..MethodName syntax. For example:

#server(..MyMethod(arg))#

Calling Server-side Methods Using #call

Synchronicity is determined by the method being called, #server or #call. #server is synchronous; #call is asynchronous.

Synchronous calls may result in a noticeable pause in the UI response (hanging). Asynchronous calls however require their own benefits and problems. For example, if the user enters a value on the web page, the page is not updated immediately; the user may have moved to another page by the time it is updated.

#server is synchronous. When you invoke a server-side method, #server returns a value to the client (since the client is waiting) *and* returns JavaScript for the client to execute

#call is asynchronous: When you invoke a server-side method, #call does not wait for a return value. Instead, your application relies on the JavaScript sent back by the server to perform any needed operations on the client.

When using asynchronous #call, you have to be careful when making multiple, successive calls. If you invoke a method via #call before the previous method is complete, the web server may decide to cancel your previous method call. Or the user might move to another page before the JavaScript executes, so the JavaScript executes on the wrong page, and result in an error. So, mostly, use #call when starting something running that could take a while, and provide a link to another page that shows the status of the long-running job.

As with #server, you can use the #call directive anywhere that JavaScript is allowed.

The syntax of the #call directive is:

#call(classname.methodname(args,...))#

where classname is the name of a server-side Caché class and methodname is the name of a method in the class. args is a list of client-side JavaScript arguments that are passed to the server-side method. Note that all code between the opening and closing #signs must all be on a single line.

For example, to invoke a server-side method named Test in the Caché class MyPackage, use:

<script language="JavaScript">
function test(value)
{
  // invoke server-side method Test
  #call(MyPackage.Test(value))#;
}
</script>

The CSP compiler replaces each occurrence of the #call directive with JavaScript code that invokes the server-side method.

From a given CSP page, you can invoke a method belonging to the class generated for it using ..MethodName syntax. For example:

#call(..MyMethod(arg))#

Hyperevent Examples

This section shows some examples of hyperevents; that is, where you use the #server and #call directives to perform server actions in response to client events. For instance: you have a form that is used to add a new customer to a database. As soon as the customer name is entered, the application checks to make sure that the customer is not already in the database. The following form definition calls a server-side Find method when the input contents are changed.

<form name="Customer" method="POST">
Customer Name:
<input type="Text" name="CName"
onChange=#server(..Find(document.Customer.CName.value))# >
</form>

In this case, the Find method could be defined in the same CSP file as:

<script language="Cache" method="Find" arguments="name:%String">
 // test if customer with name exists
 // use embedded SQL query

 New id,SQLCODE
 &sql(SELECT ID INTO :id FROM MyApp.Customer WHERE Name = :name)

 If (SQLCODE = 0) {
   // customer was found
   // send JavaScript back to client
   &js<alert('Customer with name: #(name)# already exists.');>
 }
</script>

This method communicates with the client by sending back JavaScript for execution.

Whenever a server-side method is invoked, any output it writes to the principal device is sent back to the client. There, it is converted into a JavaScript function and executed by, and in the context of, the client page.

For example, if a server-side method executes the following lines of code:

 Write "CSPPage.document.title = 'New Title';"

Then the following JavaScript is sent to the client and executed:

CSPPage.document.title = 'New Title';

In this case, this changes the title displayed in the browser to New Title. Any valid JavaScript can be sent back to the client in this fashion. Note that you must place a carriage return (using the ! character) at the end of each line of JavaScript or the browser cannot execute it.

To make it easier to return JavaScript from a server method, ObjectScript supports embedded JavaScript using the &js<> directive. This is a special language construct that lets you include lines of JavaScript in an ObjectScript method. When the method containing the embedded JavaScript is compiled, the contents of the &js<> directive is converted to the appropriate Write command statements. Embedded JavaScript can refer to ObjectScript expression using the #()# directive.

For example, a Caché method containing the following:

 Set count = 10
 &js<
   for (var i = 0; i &lt; #(count)#; i++) {
      alert('This is pleasing!');
      }
 >

is equivalent to:

 Set count = 10
 Write "for (var i = 0; i < ", count, "; i++) {",!
 Write "    alert('This is pleasing!');",!
 Write "}",!

When invoked from a client, this method displays a pleasing alert box 10 times.

Using #server in CSP Classes

To use hyperevents and Javascript within a CSP class, you must call hyperevent broker files explicitly. As in the example below, place #(..HyperEventHead())# just above the closing <head> tag.

Class esl.csptest Extends %CSP.Page [ ProcedureBlock ]
{

ClassMethod OnPage() As %Status
{

   &html<<html>
   <head>
   <script language=javascript>
   function onServer()
   {
      alert(#server(..ServerMethod())#);
   }
   </script>
   #(..HyperEventHead())#
   </head>
   <body>
   <input type=button value="click here" onclick='onServer()' />
   </body>
   </html>>
   Quit $$$OK
}

ClassMethod ServerMethod()
{
   quit "from server"
}

}

Tips for Using Server-Side Methods

The ability to invoke server-side methods from a web page is a powerful feature. There are, however, some things that you need to keep in mind when using server-side methods in an application.

Note:

Within this section, anything mentioned for #server applies to #call as well unless noted otherwise.

Either the #server or #call directive can call a method on the Caché server from JavaScript in the web browser. This makes CSP able to do things such as validate a field when you move off it rather than waiting for the submission of the form and, so, give the user immediate feedback. There are several factors with using #server syntax that you should be aware of — otherwise it is possible to produce applications that perform very slowly, or, in some cases, do not work at all.

There are two basic rules to keep in mind when using #server:

  1. Never use #server in the onload event of a web page. This can fail and it is faster and easier to generate the data when generating the web page in Caché.

  2. Do not use #server in the onunload event of a web page. Use as few #server calls as possible and do as much work inside each call as you can because they are expensive, involving a round trip from the browser to the server.

The reason this is not a good idea is because any code that you need to run inside the onload event can be run faster and more easily when the page is generated from Caché. For example, suppose that you wish to setup an initial value for a JavaScript variable that can be used later in the page (maybe in #server calls). So you are currently doing this:

<html>
<head>
<script language="JavaScript">
function LoadEvent()
{
   var value=#server(..GetValue())#;
}
</script>
</head>
<body onload=LoadEvent();>
</body>
</html>
<script language="Cache" method="GetValue" returntype="%String">
   Quit %session.Data("value")
</script>

However there is absolutely no need to call #server because the value of the JavaScript variable is already known in %session.Data("value") when you are generating the page. Hence, it is much better to write:

<html>
<head>
<script language="JavaScript">
function LoadEvent()
{
   var value='#(%session.Data("value"))#';
}
</script>
</head>
<body onload=LoadEvent();>
</body>
</html>

The same trick applies no matter what you are doing, if you are updating the value of a form element when the document loads then change this to put the value in when the page is generated, for example:

<input type="text" name="TextBox" value='#(%request.Get("Value"))#'>

There is never any need to use a #server in the onload event of a page.

Because the page is unloading, it is difficult to know if the JavaScript returned from Caché will be executed or not and the actual behavior depends on the browser. Also, if the user turns off the machine, you never get an onunload event. Your application needs to be able to cope with this possibility in any case, probably by using timeouts on the %session object. You can move the onunload #server logic code to, for example, the start of the next CSP page that the user clicks.

Use as Few #server and #call Calls as Possible

#server and #call work by making the browser issue an HTTP request for a page with a special encrypted token in it that tells Caché the method name to run. Caché runs this method and any output it sends back is executed as JavaScript on the browser, in addition the #server call can also return a value. Because these calls both use an HTTP request, they are both roughly as expensive in network packets, CPU on the server, and so on as a normal CSP page request. If you use a lot of #server requests, then it dramatically reduces the scalability of your application because each #server call is asking for a new CSP page from the Caché server. It means that, instead of a conventional web page where you go to the URL and generate the page once, a CSP page with 10 #server calls in it is as costly as generating ten CSP pages; if you can reduce the number of #server calls you could increase the number of users the application can support by a factor of ten.

The way to reduce the number of #server calls is to make sure that each #server call you use is really needed by the application and, if it is, then make sure that this #server call is doing as much work as possible on the server. For example, below is a block of JavaScript that update a form with some new values from the server.

Note that this block of code uses the CSP keyword CSPPage to refer to the page itself, rather than the Javascript keyword self. In this example, the two keywords work identically. We recommend using CSPPage as self can act unexpectedly in different contexts.

<script language="JavaScript">
function UpdateForm()
{
   CSPPage.document.form.Name.value = #server(..workGet("Name",objid))#;
   CSPPage.document.form.Address.value = #server(..workGet("Address",objid))#;
   CSPPage.document.form.DOB.value = #server(..workGet("DOB",objid))#;
}
</script>

The server code is shown here. (Normally it would use an object or SQL but here we are using a global to keep the code small.)

<script language="Cache" method="workGet"
 arguments="type:%String,id:%String" returntype="%String">
   Quit $get(^work(id,type))
</script>

This single update is making three calls for new web pages from the Caché server! This can be converted to a single #server call that updates all the values at once, the JavaScript becomes:

<script language="JavaScript">
function UpdateForm()
{
   #server(..workGet(objid))#;
}
</script>

The method definition is:

<script language="Cache" method="workGet"
 arguments="id:%String" returntype="%String">
   &js<CSPPage.document.form.Name.value = #($get(^work("Name",objid)))#;
      CSPPage.document.form.Address.value = #($get(^work("Address",objid)))#;
      CSPPage.document.form.DOB.value = #($get(^work("DOB",objid)))#;>
</script>

So, instead of multiple calls, you just pass the data once and then make Caché do all the work. If you have a more complex JavaScript example, such as:

<script language="JavaScript">
function UpdateForm()
{
   CSPPage.document.form.Name.value = #server(..workGet("Name",objid))#;
   if (condition) {
      CSPPage.document.form.DOB.value = #server(..workGet("DOB",objid))#;
   }
   else {
       CSPPage.document.form.DOB.value = '';
   }
}
</script>

then this should still only ever need one #server call. You just embed the whole if condition into the JavaScript returned by the #server call, so the code workGet method ends up looking like:

<script language="Cache" method="workGet"
 arguments="id:%String" returntype="%String">
   &js<CSPPage.document.form.Name.value = #(^work("Name",objid))#;
      if (condition) {
         CSPPage.document.form.DOB.value = #(^work("DOB",objid))#;
      }
      else {
         CSPPage.document.form.DOB.value = '';
      }
   >
</script>

Creating Custom HyperEvent Error Handler for #server and #call

If you call something with a hyperevent (#server or #call) and, on execution, it fails to communicate with the server for some reason, then generates an error, CSP’s default behavior is to display the error in an alert box. If you want to handle the error separately, such as log it or display a different message to the user, then write a cspRunServerMethodError JavaScript function. The following example displays the error in an alert box like the default behavior:

function cspRunServerMethodError(errortext,error)
{
   //alert('cspRunServerMethodError - cspHyperEventErrorHandler\n\nerrortext:' + errortext + '\n\nerror-object:\n' + JSON.stringify(error, null, 4) );
    
   if (error.code == '401') {
      document.location.href = '#(..Link(%request.URL))#'; //reloads the page
   }
   else {
      //...
   }

   return null;
  
}

cspHyperEventError object type has the following properties and values:

  • code: corresponds to an HTTP response code or a response code from the XMLHttpRequest object in use. XMLHttpRequest codes may be browser-specific.

  • text: a free text field which corresponds to the current text returned to the cspRunServerMethodError() callback function.

  • serverCode: corresponds to the error number on the server, if available. This value may be null.

  • serverText: the error message from the server, if available. This value defaults to the empty string, that is “”.

  • exception: an exception which triggered the error. This value may be null.

  • arguments: the list of arguments to the function where an exception was trapped. This value may be null and is only populated if exception is defined.

FeedbackOpens in a new tab