WebMethods are divided in two parts, a browser and a server part. In truth, a WebMethod’s logic can be mostly implemented on the browser side or on the server side. It is usual, however, for them to have a bit of code on both sides, with the bulk of the work being handled on the server. Also usually, the code on the browser side is mostly related to processing the reply sent by the server, i.e. doing something meaningful with return parameters from the WebMethod call.
Creating a WebMethod
As with all high level objects in Morfik, there are two ways you can create a new WebMethod in your application: by going through a wizard or directly to WebMethod design view. If you choose to go through the Wizard, it will ask you to specify what parameters will be passed on calling this WebMethod.
When you choose to create a WebMethod you will get a High Level object composed of two Common Object Classes, one for the browser and one for the server side of your application, just as with Forms. In order to implement your WebMethod all you have to do is write the code for two methods, one for each side of the application.
On the server side you will have the Execute method and on the browser side you will have the HandleResponse method. The way to have information channeled from one of these sides of the application to the other is to use the WebMethod Parameters.
WebMethod Parameters
WebMethod parameters are very much like function parameters. You specify the values for the parameters when you call the WebMethod and from that moment on, parameters values are known and kept updated whenever you can access them, be it on the browser or on the server side of your code. Note that changes to "in" parameters are not reflected back to the browser. When an "in" parameter is accessed from browser side code, its value will be the original value specified when the WebMethod was called.
It is also important to note that you do not need to specify any values for parameters that have default values.
In Figure 1 you can see the Editor for the WebMethod Parameters in Morfik.
|
| Figure 1 Web Method parameter editor in the Morfik development environment. |
You can also create the parameters though a Wizard, if you choose to create the WebMethod itself using the Create WebMethod Wizard. You can see in Figure 2, the Create WebMethod Wizard’s window along with a small window which is used for the developer to input information on each of the desired parameters.
|
| Figure 2 Creating a WebMethod and its parameters through the Create Web Method Wizard. |
The process of creating the WebMethod’s parameters is quite straightforward and should be quite simple to complete. In order to give you a quick look at how the WebMethods work I have written two examples: one which uses a WebMethod to perform simple mathematical operations, and another which uses a WebMethod to calculate the price of a given number of books in the BookCollector sample project.
Server Side Code
As previously mentioned, usually the server side code of a WebMethod does most of its heavy work.
In our first example, math operations are performed on the server side and sent back to the browser side of the application. In Listing 1 you can see the server-side code of the webmethod.
Listing 1 – Server side code of the MathProcessor WebMethod.
FX Code
Unit MathProcessor; Interface Type MathProcessor = Class(WebMethod) Operand1 : Double; Operand2 : Double; theOperator : String; theResult : Double; Status : Integer; Private { Private declarations } Public { Public declarations } Procedure Execute; override; End; ['Published=False']; Implementation Procedure MathProcessor.Execute; Begin Status := 0; if theOperator = 'ADD' then theResult := Operand1 + Operand2 else if theOperator = 'SUB' then theResult := Operand1 - Operand2 else if theOperator = 'DIV' then theResult := Operand1 / Operand2 else if theOperator = 'MUL' then theResult := Operand1 * Operand2 else Status := 1; End; End.
| BX Code |
|---|
/* %MA DYNAMIC */ Namespace MathProcessor Public Class MathProcessor Inherits Macro Public operand1 As Double Public operand2 As Double Public theOperator As String Public theResult As Double Public Status As Integer ' Public declarations Public Overrides Sub Execute Status = 0 If theOperator = "ADD" Then theResult = operand1 + operand2 ElseIf theOperator = "SUB" Then theResult = operand1 - operand2 ElseIf theOperator = "DIV" Then theResult = operand1 / operand2 ElseIf theOperator = "MUL" Then theResult = operand1 * operand2 Else Status = 1 End If End Sub End Class End Namespace |
| CX Code |
|---|
/* $MA DYNAMIC */ namespace MathProcessor { public class MathProcessor : Macro { public Double operand1; public Double operand2; public String theOperator; public Double theResult; public Integer Status; //' Public declarations public override void Execute() { Status = 0; if (theOperator == "ADD") { theResult = operand1 + operand2; } else { if (theOperator == "SUB") { theResult = operand1 - operand2; } else { if (theOperator == "DIV") { theResult = operand1 / operand2; } else { if (theOperator == "MUL") { theResult = operand1 * operand2; } else { Status = 1; } } } } } } } |
Our second example is based on the BookCollector sample project. In Form2 suppose we wish to add a feature that allows the user to type in the number of copies of the book that he/she wishes to purchase, and have the total price displayed in a TextLabel. We write a webmethod GetTotalPrice is written for this purpose. GetTotalPrice takes three parameters: "MFK$GUID" (the value of the MFK$GUID field in the Books table), NumberOfCopies (the number of copies the customer wishes to purchase) and BandIndex (the index of the current form band; this is needed so that when the webmethod returns, it knows in which band to display the result). GetTotalPrice returns the result in the parameter TotalPrice. In Listing 2 you can see the server-side code of the webmethod.
Listing 2 – Server side code of the GetTotalPrice WebMethod.
FX Code
Unit GetTotalPrice; Interface Type GetTotalPrice = Class(WebMethod) "MFK$GUID" : String; NumberOfCopies : Integer; BandIndex : Integer; TotalPrice : Currency; Private { Private declarations } Public { Public declarations } Procedure Execute; override; End; Implementation Procedure GetTotalPrice.Execute; Var SQL : String; RecordSet : TRecordSet; Begin TotalPrice := 0.0; SQL := 'SELECT "UnitPrice" FROM "Books" WHERE "MFK$GUID"=:"MFK$GUID"'; RecordSet := DefaultDBConnection.CreateRecordSet(SQL); Try RecordSet.Prepare(); RecordSet.ParamByName('MFK$GUID').AsString := "MFK$GUID"; RecordSet.First; If Not RecordSet.EOF Then TotalPrice := NumberOfCopies * RecordSet.FieldByName('UnitPrice').AsCurrency; Finally DefaultDBConnection.DestroyRecordSet(RecordSet); End; End; End.
| BX Code |
|---|
/* %MA DYNAMIC */ Namespace GetTotalPrice Public Class GetTotalPrice Inherits WebMethod Public MFK$GUID As String Public NumberOfCopies As Integer Public BandIndex As Integer Public TotalPrice As Currency Public Overrides Sub Execute Dim SQL As String Dim RecordSet As TRecordset TotalPrice = 0.0 SQL = "SELECT ""UnitPrice"" FROM ""Books"" WHERE ""MFK$GUID""=:""MFK$GUID""" RecordSet = DefaultDBConnection.CreateRecordSet(SQL) Try RecordSet.Prepare() RecordSet.ParamByName("MFK$GUID").AsString = MFK$GUID RecordSet.First() If Not RecordSet.Eof Then TotalPrice = NumberOfCopies * RecordSet.FieldByName("UnitPrice").AsCurrency Finally DefaultDBConnection.DestroyRecordSet(RecordSet) End Try End Sub End Class End Namespace |
| CX Code |
|---|
/* $MA DYNAMIC */ namespace GetTotalPrice { public class GetTotalPrice : WebMethod { public String MFK$GUID; public Integer NumberOfCopies; public Integer BandIndex; public Currency TotalPrice; public override void Execute() { String SQL; TRecordset RecordSet; TotalPrice = 0.0; SQL = "SELECT \"UnitPrice\" FROM \"Books\" WHERE \"MFK$GUID\"=:\"MFK$GUID\""; RecordSet = DefaultDBConnection.CreateRecordSet(SQL); try { RecordSet.Prepare(); RecordSet.ParamByName("MFK$GUID").AsString = MFK$GUID; RecordSet.First(); if (!RecordSet.Eof) TotalPrice = NumberOfCopies * RecordSet.FieldByName("UnitPrice").AsCurrency; } finally { DefaultDBConnection.DestroyRecordSet(RecordSet); } } } } |
Browser Side Code
The browser side of a WebMethod involves getting the result of the operation from the server and then doing something with it. Now, an interesting question is: what does the browser side of the WebMethod do with the results it just received from the server? Since the call to execute a WebMethod is asynchronous, as we will discuss shortly, there is nothing to return the results to. The browser side HandleResponse method is responsible for displaying the results.
The MathProcessor webmethod displays its results in a textlabel in the Index form. You can see the code that does this in Listing 3.
Listing 3 – Browser side code of the MathProcessor WebMethod.
FX Code
Unit MathProcessor; Interface Type MathProcessor = Class(WebMethod) Operand1 : Double; Operand2 : Double; theOperator : String; theResult : Double; Status : Integer; Private { Private declarations } Public { Public declarations } Procedure HandleResponse; override; End; Implementation uses Index, SystemControls; Procedure MathProcessor.HandleResponse; Begin Index(xApp.Forms['Index']).ResultLabel.Caption := FloatToStr(theResult); End; End.
| BX Code |
|---|
/* %MA DYNAMIC */ Using Index Namespace MathProcessor Public Class MathProcessor Inherits Macro Public operand1 As Double Public operand2 As Double Public theOperator As String Public theResult As Double Public Status As Integer ' Public declarations Public Overrides Sub HandleResponse Ctype(XApp.Forms("Index"), Index).ResultLabel.Caption = FloatToStr(theResult) End Sub End Class End Namespace |
| CX Code |
|---|
/* $MA DYNAMIC */ using Index; namespace MathProcessor { public class MathProcessor : Macro { public Double operand1; public Double operand2; public String theOperator; public Double theResult; public Integer Status; //' Public declarations public override void HandleResponse() { (Index)(XApp.Forms["Index"]).ResultLabel.Caption = FloatToStr(theResult); } } } |
In order to test this WebMethod I created a very small test application with a single form. You can see in Figure 3, this application at runtime within Internet Explorer.
|
| Figure 3: MathProcessor WebMethod test application at run time. |
The GetTotalPrice webmethod displays its results in TotalPriceTextLabel in Form2. Because Form2 is a data-bound continuous form, the HandleResponse method must make sure the result is displayed in the correct band. You can see how it does this in Listing 4.
Listing 4 – Browser side code of the GetTotalPrice WebMethod.
FX Code
Unit GetTotalPrice; Interface Type GetTotalPrice = Class(WebMethod) "MFK$GUID" : String; NumberOfCopies : Integer; TotalPrice : Currency; BandIndex : Integer; Private { Private declarations } Public { Public declarations } Procedure HandleResponse; override; End; Implementation Uses Form2; Procedure GetTotalPrice.HandleResponse; Var f : Form2; Begin f := Form2(XApp.Forms['Form2']); If f <> Nil Then Begin f.TotalPriceTextLabel.BandIndex := BandIndex; f.TotalPriceTextLabel.Caption := CurrencyToLocaleString(TotalPrice, 'c'); End; End; End.
| BX Code |
|---|
/* %MA DYNAMIC */ Using Form2 Namespace GetTotalPrice Public Class GetTotalPrice Inherits WebMethod Public MFK$GUID As String Public NumberOfCopies As Integer Public TotalPrice As Currency Public BandIndex As Integer Public Overrides Sub HandleResponse Dim f As Form2 f = Ctype(XApp.Forms("Form2"), Form2) If f <> Nothing Then f.TotalPriceTextLabel.BandIndex = BandIndex f.TotalPriceTextLabel.Caption = CurrencyToLocaleString(TotalPrice, "c") End If End Sub End Class End Namespace |
| CX Code |
|---|
/* $MA DYNAMIC */ using Form2; namespace GetTotalPrice { public class GetTotalPrice : WebMethod { public String MFK$GUID; public Integer NumberOfCopies; public Currency TotalPrice; public Integer BandIndex; public override void HandleResponse() { Form2 f; f = (Form2)(XApp.Forms["Form2"]); if (f != null) { f.TotalPriceTextLabel.BandIndex = BandIndex; f.TotalPriceTextLabel.Caption = CurrencyToLocaleString(TotalPrice, "c"); } } } } |
Browser or Server Side?
WebMethods are a construct for running specific sets of commands on the server side of your application. The browser side of your code should just process the response it received from the server side, even though you can write code within the HandleResponse method to do other things, for example, you could achieve the same results we have with the WebMethod shown in Listings 1 and 3 could be achieved, with all the logic on the Browser side, if we changed the browser side to be as is shown in Listing 5. This practice, however, is not encouraged since it offers no benefit over the creation of an ordinary function, which will accomplish the same results. This kind of implementation will, also, be useless when you want to publish Web Services.
Listing 5 – WebMethod’s logic implemented in the Browser side.
FX Code
Unit MathProcessor; Interface Type MathProcessor = Class(WebMethod) Operand1 : Double; Operand2 : Double; theOperator : String; theResult : Double; Status : Integer; Private { Private declarations } Public { Public declarations } Procedure HandleResponse; override; End; Implementation uses Index, SystemControls; Procedure MathProcessor.HandleResponse; Begin if theOperator = 'ADD' then theResult := Operand1 + Operand2 else if theOperator = 'SUB' then theResult := Operand1 - Operand2 else if theOperator = 'DIV' then theResult := Operand1 / Operand2 else if theOperator = 'MUL' then theResult := Operand1 * Operand2 else Status := 1; Index(xApp.Forms['Index']).ResultLabel.Caption := FloatToStr(theResult); End; End.
| BX Code |
|---|
/* %MA DYNAMIC */ Using Index Namespace MathProcessor2 Public Class MathProcessor2 Inherits Macro Public Operand1 As Double Public Operand2 As Double Public theOperator As String Public theResult As Double Public Status As Integer ' Public declarations Public Overrides Sub HandleResponse If theOperator = "ADD" Then theResult = Operand1 + Operand2 ElseIf theOperator = "SUB" Then theResult = Operand1 - Operand2 ElseIf theOperator = "DIV" Then theResult = Operand1 / Operand2 ElseIf theOperator = "MUL" Then theResult = Operand1 * Operand2 Else Status = 1 End If Ctype(XApp.Forms("Index"), Index).ResultLabel.Caption = FloatToStr(theResult) End Sub End Class End Namespace |
| CX Code |
|---|
/* $MA DYNAMIC */ using Index; namespace MathProcessor2 { public class MathProcessor2 : Macro { public Double Operand1; public Double Operand2; public String theOperator; public Double theResult; public Integer Status; //' Public declarations public override void HandleResponse() { if (theOperator == "ADD") { theResult = Operand1 + Operand2; } else { if (theOperator == "SUB") { theResult = Operand1 - Operand2; } else { if (theOperator == "DIV") { theResult = Operand1 / Operand2; } else { if (theOperator == "MUL") { theResult = Operand1 * Operand2; } else { Status = 1; } } } } (Index)(XApp.Forms["Index"]).ResultLabel.Caption = FloatToStr(theResult); } } } |
Notice that in this case, all the actual mathematical operations were transferred to the browser side, within the HandleResponse method.
Using a WebMethod
Up to this point we have seen how to create a new WebMethod and how to write some code for both its browser and server side parts, but how to we use this code? How do we invoke a WebMethod?
Invoking a WebMethod
In order to invoke a WebMethod you need to make a call to the RunWebMethod function, passing its name and the parameters which are required by the WebMethod. The parameters for the invoked WebMethod are passed as a single string, composed of a series of pairs in the following pattern:
parameter_name=parameter_value
These pairs are enclosed in double quotes and separated by commas. Whether the string itself is involved in single or double quotes depends on the string delimiters of the developer’s language syntax of choice.
In Listing 6 you can see the source code for the simple test Form, which invokes the MathProcessor WebMethod on the click of a button.
Listing 6 – Invoking the MathProcessor WebMethod from Pascal, Basic and C# language Forms.
FX Code
Unit Index; Interface Type Index = Class(Form) OpTxt1 : TextEdit; OpTxt2 : TextEdit; OperatorCombo : ComboBox; EqualsButton : Button; PaintBox1 : PaintBox; ResultLabel : TextLabel; Procedure EqualsButtonClick(Event: TDOMEvent); Message; Private { Private declarations } Public { Public declarations } End; Implementation Procedure Index.EqualsButtonClick(Event: TDOMEvent); Begin RunWebMethod('MathProcessor', '"Operand1=' + OpTxt1.Text +'",' + '"Operand2=' + OpTxt2.Text +'",' + '"theOperator=' + OperatorCombo.Text +'"'); End; End.
| BX Code |
|---|
Namespace Index Public Class Index Inherits Form Published OpTxt1 As TextEdit Published OpTxt2 As TextEdit Published OperatorCombo As ComboBox Published EqualsButton As Button Published PaintBox1 As PaintBox Published ResultLabel As TextLabel Published Message Sub EqualsButtonClick(Event As TDOMEvent) RunWebMethod("MathProcessor", """Operand1=" & OpTxt1.Text & """," & _ """Operand2=" & OpTxt2.Text & """," & _ """theOperator=" & OperatorCombo.Text _ & """") End Sub End Class End Namespace |
| CX Code |
|---|
namespace Index { public class Index : Form { published TextEdit OpTxt1; published TextEdit OpTxt2; published ComboBox OperatorCombo; published Button EqualsButton; published PaintBox PaintBox1; published TextLabel ResultLabel; published message void EqualsButtonClick(TDOMEvent Event) { RunWebMethod("MathProcessor", "\"Operand1=" + OpTxt1.Text + "\"," + "\"Operand2=" + OpTxt2.Text + "\"," + "\"theOperator=" + OperatorCombo.Text + "\""); } } } |
In Listing 7 you can see the source code for Form2, which invokes the GetTotalPrice WebMethod on the click of a button.
Listing 7 – Invoking the GetTotalPrice WebMethod from Pascal, Basic and C# language Forms.
FX Code
Unit Form2; Interface Type Form2 = Class(Form) Container1 : Container; Container4 : Container; TextLabel3 : TextLabel; TextLabel9 : TextLabel; TextLabel10 : TextLabel; TextLabel12 : TextLabel; TextLabel23 : TextLabel; TextLabel24 : TextLabel; TextLabel2 : TextLabel; TextLabel7 : TextLabel; TextLabel4 : TextLabel; Image3 : Image; Container3 : Container; TextLabel5 : TextLabel; TextLabel6 : TextLabel; TextLabel11 : TextLabel; Image2 : Image; PriceLabel : TextLabel; TextEdit1 : TextEdit; Button1 : Button; TotalPriceTextLabel : TextLabel; TextLabel8 : TextLabel; Procedure WebFormBeforeDBNavigate(Action: TDBNavigateAction; Var Cancel: Boolean); Message; Procedure Button1Click(Event: TDOMEvent); Message; Private { Private declarations } Public { Public declarations } End; Implementation Procedure Form2.WebFormBeforeDBNavigate(Action: TDBNavigateAction; Var Cancel: Boolean); Begin If Action = naNextPage Then Cancel := StartingOffset + PageSize >= RecordCount; End; Procedure Form2.Button1Click(Event: TDOMEvent); Var Params : String; Begin TextLabel8.BandIndex := Button1.BandIndex; TextEdit1 .BandIndex := Button1.BandIndex; Params := '"MFK$GUID=' + TextLabel8.Caption + '",' + '"NumberOfCopies=' + TextEdit1.Text + '",' + '"BandIndex=' + Button1.BandIndex.ToString() + '"'; RunWebMethod('GetTotalPrice', Params); End; End.
| BX Code |
|---|
/* %MA DYNAMIC */ Namespace Form2 Public Class Form2 Inherits Form Published Container1 As Container Published Container4 As Container Published TextLabel3 As TextLabel Published TextLabel9 As TextLabel Published TextLabel10 As TextLabel Published TextLabel12 As TextLabel Published TextLabel23 As TextLabel Published TextLabel24 As TextLabel Published TextLabel2 As TextLabel Published TextLabel7 As TextLabel Published TextLabel4 As TextLabel Published Image3 As Image Published Container3 As Container Published TextLabel5 As TextLabel Published TextLabel6 As TextLabel Published TextLabel11 As TextLabel Published Image2 As Image Published PriceLabel As TextLabel Published TextEdit1 As TextEdit Published Button1 As Button Published TotalPriceTextLabel As TextLabel Published TextLabel8 As TextLabel Published Message Sub WebFormBeforeDBNavigate(Action As TDBNavigateAction, ByRef Cancel As Boolean) If Action = nanextpage Then Cancel = (StartingOffset + PageSize) >= RecordCount End Sub Published Message Sub Button1Click(Event As TDOMEvent) Dim Params As String TextLabel8.BandIndex = Button1.BandIndex TextEdit1.BandIndex = Button1.BandIndex Params = """MFK$GUID=" & TextLabel8.Caption & """," & _ """NumberOfCopies=" & TextEdit1.Text & """," & _ """BandIndex=" & Button1.BandIndex.ToString() & """" RunWebMethod("GetTotalPrice", Params) End Sub End Class End Namespace |
| CX Code |
|---|
/* $MA DYNAMIC */ namespace Form2 { public class Form2 : Form { published Container Container1; published Container Container4; published TextLabel TextLabel3; published TextLabel TextLabel9; published TextLabel TextLabel10; published TextLabel TextLabel12; published TextLabel TextLabel23; published TextLabel TextLabel24; published TextLabel TextLabel2; published TextLabel TextLabel7; published TextLabel TextLabel4; published Image Image3; published Container Container3; published TextLabel TextLabel5; published TextLabel TextLabel6; published TextLabel TextLabel11; published Image Image2; published TextLabel PriceLabel; published TextEdit TextEdit1; published Button Button1; published TextLabel TotalPriceTextLabel; published TextLabel TextLabel8; published message void WebFormBeforeDBNavigate(TDBNavigateAction Action, ref Boolean Cancel) { if (Action == nanextpage) Cancel = (StartingOffset + PageSize) >= RecordCount; } published message void Button1Click(TDOMEvent Event) { String Params; TextLabel8.BandIndex = Button1.BandIndex; TextEdit1.BandIndex = Button1.BandIndex; Params = "\"MFK$GUID=" + TextLabel8.Caption + "\"," + "\"NumberOfCopies=" + TextEdit1.Text + "\"," + "\"BandIndex=" + Button1.BandIndex.ToString() + "\""; RunWebMethod("GetTotalPrice", Params); } } } |
For an example of how this WebMethod could be used in modules written in other language syntaxes, look at the code in Listings 6 and 7. You can see the code necessary to invoke the same MathProcessor WebMethod from a Form in Morfik Basic syntax.
Due to Morfik’s ability to work with multiple language projects, the code you see in Listing 6 and 7 will work perfectly with the Web Methods we have seen in this chapter, even if they are written in different languages.
Beware: Asynchronous Platform
As you might have noticed navigating on the Internet, when you open a page in the browser, several things seem to happen at the same time. As the text is downloaded, so are the images which are shown as their download completes.
All action within the browser happens in an asynchronous manner. No action blocks another from taking place simultaneously. So while picture A is being downloaded, so are pictures B, C and D. Whichever picture has its download concluded first, will then be shown immediately.
It is very important to keep in mind that though a call to the RunWebMethod function will return immediately, the desired results might not have yet been achieved since what really happened is that an asynchronous call was dispatched to the server part of your application. As soon as that asynchronous call is returned by the server-side execute method, then the browser side HandleResponse method will be invoked, closing the full cycle of a WebMethod's invocation.
WebMethods in Action
So far, we have gone through how to calling a very simple WebMethod. In order to get a better idea of what a WebMethod can do it will be interesting to look at a more complex example.
Writing Code for Data Manipulation
Morfik allows you to write code on the server side so that you can handle direct data manipulation. This is very useful in several different situations: such as when you want to update information stored in t more than one table at the same time and when you need to have a recurring process which is not started in response to direct end user action. All server side code that can be invoked from the browser in a Morfik application is implemented as a Web Method. This can also be quite useful for the implementation of published Web Service interfaces to the logic of your application. Read more...
Publishing WebMethods for External Consumption
When you create a WebMethod in your application, by default, it will not be included in the WSDL (Web Service Description Language) file that Morfik generates at compile type. This essentially means that only your application will know how to use that method, since its interface definition will not be made available in the WSDL file. If you wish to make a WebMethod available for external consumption all you need to do is change the value of the Published metadata attribute for the WebMethod derived class you are creating. If you look closely at listings 1 and 8, you will see this metadata attribute appearing right after the class declaration.
If you change from the default value:
['WSPublished=False'];
to
['WSPublished=True'];
This WebMethod will be included in the WSDL file which Morfik tools generate at compile time. Always look for your .wsdl file in the same folder where your executable is being generated.
Creating a Web Services API with Web Methods
While the visually created Web Method is quite useful, and can meet most needs of the developer, if an application will publish a large quantity of Web Methods it can become troublesome to manage. Also, visually created Web Methods are restricted to having parameters of the basic data types such as String, Integer and double.
When planning to create a full fledged API for offering services through a Web Services interface the developer can opt to create the Web Methods in code, in any server side module of the application. An experienced developer should be able to take advantage of the fact that with all the required classes in the same module it becomes easier to reuse some code that is created for one Web Method, in another.
The following code listings show examples of how to declare Web Methods including some special metadata tags that must be used when declaring them in a regular module. No implementation is present for any of the Web Methods, only the class declarations or with their method headers.
Listing 8 - Sample declaration of Web Methods in a regular module
FX Code
Unit Module1; Interface Uses SystemSerialiser, SystemWebMethod; Type Function1 = Class(WebMethod) s : String; ['WSPublished=true','WSFieldKind=in/out']; k : String; ['WSPublished=true','WSFieldKind=in']; z : String; ['WSPublished=true','WSFieldKind=out']; Procedure Execute; Override; End; ['WSPublished=true']; Function2 = Class(WebMethod) pHeaderParam: String; ['WSPublished=true','WSFieldKind=in/out','WSHeader=true']; Procedure Execute; Override; End; ['WSPublished=true']; Function3 = Class(WebMethod) StrLst : List of String; ['WSPublished=true','WSFieldKind=in/out','WSHeader=false']; Procedure Execute; Override; End; ['WSPublished=true']; TStr = Class(TSerializable) S : String; Constructor Create(a : String); End; Function4 = Class(WebMethod) StrLst : List of TStr; ['WSPublished=true','WSFieldKind=in/out','WSHeader=false']; Procedure Execute; Override; End; ['WSPublished=true'];
| BX Code |
|---|
/* %MA STATIC */ Imports SystemSerialiser Imports SystemWebMethod Namespace Module1 Public Class Function1 Inherits WebMethod Public s As String :{"WSPublished=true", "WSFieldKind=in/out"} Public k As String :{"WSPublished=true", "WSFieldKind=in"} Public z As String :{"WSPublished=true", "WSFieldKind=out"} Public Overrides Sub Execute ' ... End Sub End Class :{"WSPublished=true"} Public Class Function2 Inherits WebMethod Public pHeaderParam As String :{"WSPublished=true", "WSFieldKind=in/out", "WSHeader=true"} Public Overrides Sub Execute ' ... End Sub End Class :{"WSPublished=true"} Public Class Function3 Inherits WebMethod Public StrLst As List Of String :{"WSPublished=true", "WSFieldKind=in/out", "WSHeader=false"} Public Overrides Sub Execute ' ... End Sub End Class :{"WSPublished=true"} Public Class TStr Inherits TSerializable Public S As String Public Sub New(a As String) ' ... End Sub End Class Public Class Function4 Inherits WebMethod Public StrLst As List Of TStr :{"WSPublished=true", "WSFieldKind=in/out", "WSHeader=false"} Public Overrides Sub Execute ' ... End Sub End Class :{"WSPublished=true"} End Namespace |
| CX Code |
|---|
/* $MA STATIC */ imports SystemSerialiser; imports SystemWebMethod; namespace Module1 { public class Function1 : WebMethod { public String s; ["WSPublished=true", "WSFieldKind=in/out"] public String k; ["WSPublished=true", "WSFieldKind=in"] public String z; ["WSPublished=true", "WSFieldKind=out"] public override void Execute() { // ... } } ["WSPublished=true"] public class Function2 : WebMethod { public String pHeaderParam; ["WSPublished=true", "WSFieldKind=in/out", "WSHeader=true"] public override void Execute() { // ... } } ["WSPublished=true"] public class Function3 : WebMethod { public list of String StrLst; ["WSPublished=true", "WSFieldKind=in/out", "WSHeader=false"] public override void Execute() { // ... } } ["WSPublished=true"] public class TStr : TSerializable { public String S; public TStr(String a) { // ... } } public class Function4 : WebMethod { public list of TStr StrLst; ["WSPublished=true", "WSFieldKind=in/out", "WSHeader=false"] public override void Execute() { // ... } } ["WSPublished=true"] } |
Metadata Tags for Coding Web Methods
When you are creating a Web Method in code, without the benefit of the Visual editor for Web Methods, which lets you specify all parameters, their types and behaviors, you need to enter that information directly in your code.
There are two ways to apply tags: to the class and to specific member of the class. The only tag you need to apply to the class as whole is the WSPublished tag which must be set to True for the class to be included in the .WSDL file of the project that is being created, as previously shown and as can be seen in the listing immediately above.
There are several tags that can be applied to each member variable of a Web Method class, some of which are mandatory for variables which will function as parameters. Each variable that will function as a parameter must have the WSPublished and WSFieldKind tags associated with them. Again, the WSPublished tag must be set to True and the WSFieldKind tag must be set to one of three possible values: in, out, in/out.
In addition to these two tags you can add a third tag: WSHeader. If WSHeader is set to True the associated parameter will be added to the Web Method call's header. By default, if WSHeader is not informed in a parameter's associated metadata tags it is assumed to be False.
Bellow is an example of what the metadata tags that should be associated to any member variable of a Web Method class that will function as a parameter for the Web Method.
['WSPublished=true','WSFieldKind=in/out','WSHeader=false'];
Wrapping it up
As we have just seen, WebMethods can be used to provide simple functionality which takes processing from the browser to the server or they can be used to lookup information from a database, returning such information to Forms in the browser side of the application. These are, however, just a few possibilities. WebMethods are a very interesting construct within Morfik tools and can be used for very different purposes.
By creating WebMethods you can make available, to 3rd parties, certain functionalities of your online system or create services which will be centrally available for use by several different applications.
Related Topics
- What are Web Services and why do I need to use them?
- Understanding the way Web Services are used in Morfik
- Importing and Consuming external Web Services
- Non-visual Web Methods

