Delphi Clinic C++Builder Gate Training & Consultancy Delphi Notes Weblog Dr.Bob's Webshop
Dr.Bob's Delphi Notes Dr.Bob's Delphi Clinics Dr.Bob's Delphi Courseware Manuals
 Dr.Bob Examines... #109
See Also: Dr.Bob's Delphi Papers and Columns

ASP.NET Web Services with Delphi Prism 2009
Delphi Prism supports ASP.NET Web Services, which is important because the SOAP Server support in Delphi 2009 is marked as "under consideration", so for future SOAP and Web Services, we should mainly look at Delphi Prism for .NET solutions (at least for the server side).
As you can see from the first screenshot below, after you do File | New - Web Site, the ASP.NET Web Service target is an option here.If you select this choice you must enter some information at the bottom of the dialog.

Change the Location from File System to HTTP (the other alternative is FTP), and change the textbox to http://localhost/MyWebService.Leave the .NET Framework 3.5 option set if you want to use the .NET 3.5 features (like LINQ), otherwise you can move back to .NET Framework 3.0 or even 2.0 (useful if you re unsure that the deployment web server will support these version of the .NET Framework).

When you click on OK, Delphi Prism will generate a new Web Service project at the specified location (on my machine, http://localhost/MyWebService corresponds to a physical path which can be found in C:\Inetpub\wwwroot\MyWebService).

You now have a solution, with App_Code node containing Service.pas, an empty App_Data node (which can contain data - more on that later), a Service.asmx file, and a web.config file.
The contents of Service.pas is as follows:

  namespace;

  interface

  uses
    System,
    System.Web,
    System.Collections,
    System.Web.Services,
    System.Web.Services.Protocols;


  type
    [WebService(&Namespace := 'http://tempuri.org/')]
    [WebServiceBinding(ConformsTo := WsiProfiles.BasicProfile1_1)]
    Service = public class(System.Web.Services.WebService)
    public
      method HelloWorld: string;
    end;

  implementation

  [WebMethod]
  method Service.HelloWorld: string;
  begin
    result := 'Hello World';
  end;

  end.
There are a number of interesting things you can note from this source file already.First of all, in order to define a Web Service in .NET, you only need to derive from the built-in System.Web.Services.WebService class.

[WebService]
You should also specify the WebService attribute, specifically in order to define a namespace for the web service.Right now, the Namespace it set to the default http://tempuri.org, which is not a good idea because this is the default namespace for all web services.And since you service itself it still called "Service", there's a very good chance some other web service called Service in the http://tempuri.org namespace exists already.Which is not necessarily a bad thing, until you try to use both of them together in one system.
Regardless of the fact if that may happen or not, it's considered good practice to specify your own unique namespace to distinguish the web service from others. Fortunately, if you accidentally forget to change the namespace, the ASP.NET testing environment will tell you that you re still using http://tempuri.org as namespace, and should change that:

The help provided by the ASP.NET Framework, with the recommendated syntax to fix it, is using C# and VB only by the way, but it's not easy to change it for Delphi Prism.
Although it looks like one, the namespace does not have to be a URL, but merely a URI (universal resource identifier), so nothing need to exist when you enter the namespace in a browser.For my web services, I always use the http://eBob42.org namespace, which happens to show something when entering in the browser as well, but that's not required.

  type
    [WebService(&Namespace := 'http://eBob42.org/')]
Note that Namespace is a reserved word in Delphi Prism, and that's why we have to prefix it with a & character here.
Apart from the Namespace property, the WebService attribute also allows us to set the Description property with a string that explains in human-readable text what this Web Service is all about.The description is not used by compilers that parse and/or use the Web Service, but can be useful to include some general instructions on how to use the web service, or who to contact in order to get permission or support.
  type
    [WebService(&Namespace := 'http://eBob42.org/',
      Description := 'This web service is a first demo Web Service<br>' +
        'generated by <b>Delphi Prism</b> for ASP.NET 2.0 or higher')]
Note that if you want to specify a long Description, you may want to include HTML tags like <br> or <p>.You can also use HTML tags to decorate the description (as can be seen in the snippet above).We will see the result of this a bit later inside the browser.

[WebMethod]
The generated Web Service class Service already contains one method called HelloWorld.Note that from the definition, it's not quite clear that this is a so-called web method, exposed by the service.However, if you look at the implementation of the HelloWorld method, you see that it's being preceeded by the [WebMethod] attribute.This is a requirement to mark a method as being a web method.You can use the [WebMethod] attribute at the declaration level (just before the method HelloWorld in the Service class), or at the implementation level.The generated Web Service specifies [WebMethod] at the implementation level.However, I consider this less useful than specifying it at the declaration level, since that will give you a quick overview of all web methods inside a class (compared to the normal methods).
So my recommentation would be to move the [WebMethod] above from the implementation to the declaration of HelloWorld, resulting in the following contents of Service.pas:

  namespace;
  interface
  uses
    System,
    System.Web,
    System.Collections,
    System.Web.Services,
    System.Web.Services.Protocols;

  type
    [WebService(&Namespace := 'http://eBob42.org/',
      Description := 'This web service is a first demo Web Service<br>' +
        'generated by <b>Delphi Prism</b> for ASP.NET 2.0 or higher')]
    [WebServiceBinding(ConformsTo := WsiProfiles.BasicProfile1_1)]
    Service = public class(System.Web.Services.WebService)
    public
      [WebMethod]
      method HelloWorld: string;
    end;

  implementation

  method Service.HelloWorld: string;
  begin
    result := 'Hello World';
  end;

  end.

WebMethod properties
The WebMethod attribute has six (optional) named parameters that we can use, namely: Description, MessageName, BufferResponse, CacheDuration, TransactionOption, and EnableSession.
The Description named parameter is used to add a short description to the associated web method, just like the Description named parameter of the WebService attribute specifies the description of the entire web service.Note that the descriptions are only used by developers who care to read them - they play no role in the actual importing or consuming of the web service methods at all.But since they can be read by developers, you can use it to explain parameter usage and other important details.

MessageName, BufferResponse, CacheDuration and TransactionOption are beyond the scope of this text, but EnableSession is interesting enough to cover.

EnableSession
EnableSession is by default set to False, which is the most efficient setting.You should only set the EnableSession named property to True if you want to enable sessions for your ASP.NET web service.
The ASP.NET Application and Session objects are made available as similar named properties through the WebService base class.This also means that the ASP.NET Web Service needs to be derived from the System.Web.Services.WebService class - which is also the case in the ASP.NET Web Service projects generated by Delphi Prism.The Application object is a global object that is available for all clients - sharing the same data among all clients, while there will be one unique instance of the Session object for each client that is connected to the web service.As a result, the Application object can be used to share global data, while the Session object can be used to store client specific data.
Because the use of ASP.NET sessions will make the web service less scalable, this option is disabled by default, and needs to be enabled on a method-by-method basis, using the EnableSession=true property of the WebMethod attribute.Check out my Delphi Prism courseware manual for more details on ASP.NET WebMethod properties.

First Test
Without the need to write a further single line of code, we ve already created a working ASP.NET Web Service.If you Build the solution, you can view the Web Service in a browser using the following local URL: http://localhost/MyWebService/Service.asmx

We can even click on the link for the HelloWorld method in order to test it in the local browser, but watching a simple Hello, World string is not very interesting, so let's add another web method with some more functionality. As a real-world example, I ve written a numbers to words method, often used in legal documents or banking cheques to avoid fraud.The definition of the Service class changes as follows (note that I ve also placed the WebMethod attribut at the declaration level of both web method):

  type
    [WebService(&Namespace := 'http://eBob42.org/',
      Description := 'This web service is a first demo Web Service<br>' +
        'generated by <b>Delphi Prism</b> for ASP.NET 2.0 or higher')]
    [WebServiceBinding(ConformsTo := WsiProfiles.BasicProfile1_1)]
    Service = public class(System.Web.Services.WebService)
    public
      [WebMethod]
      method HelloWorld: string;
      [WebMethod]
      method Num2Word(value: Integer): String;
    end;
The implementation - translating numbers to Dutch words - is as follows:
  method Service.Num2Word(value: Integer): String;
  begin
    if value >= 1000000 then
      if (value mod 1000000) = 0 then
        Result := Num2Word(value / 1000000) + 'miljoen'
      else
        Result := Num2Word(value / 1000000) + 'miljoen ' +
                  Num2Word(value mod 1000000)
    else
      if value >= 1000 then
        if (value mod 1000) = 0 then
          Result := Num2Word(value / 1000) + 'duizend'
        else
          Result := Num2Word(value / 1000) + 'duizend ' +
                    Num2Word(value mod 1000)
      else
        if value >= 100 then
          if (value mod 100) = 0 then
            Result := Num2Word(value / 100) + 'honderd'
          else
            Result := Num2Word(value / 100) + 'honderd' +
                      Num2Word(value mod 100)
        else
          case (value / 10) of
   5,6,7,9: if (value mod 10) = 0 then
              Result := Num2Word(value / 10) + 'tig'
            else
              Result := Num2Word(value mod 10) + 'en' +
                        Num2Word(value / 10) + 'tig';
         8: if value = 80 then
              Result := 'tachtig'
            else
              Result := Num2Word(value mod 10) + 'entachtig';
         4: if value = 40 then
              Result := 'veertig'
            else
              Result := Num2Word(value mod 10) + 'enveertig';
         3: if value = 30 then
              Result := 'dertig'
            else
              Result := Num2Word(value mod 10) + 'endertig';
         2: if value = 20 then
              Result := 'twintig'
            else
              Result := Num2Word(value mod 10) + 'entwintig';
       0,1: case value of
              0: Result := 'nul';
              1: Result := 'een';
              2: Result := 'twee';
              3: Result := 'drie';
              4: Result := 'vier';
              5: Result := 'vijf';
              6: Result := 'zes';
              7: Result := 'zeven';
              8: Result := 'acht';
              9: Result := 'negen';
             10: Result := 'tien';
             11: Result := 'elf';
             12: Result := 'twaalf';
             13: Result := 'dertien';
             14: Result := 'veertien';
             15: Result := 'vijftien';
             16: Result := 'zestien';
             17: Result := 'zeventien';
             18: Result := 'achttien';
             19: Result := 'negentien'
            end
          end
  end;
Delphi developers who recently moved to Delphi Prism for .NET development may notice some differences between Delphi Prism and Delphi for .NET here.First of all, you all noticed the method keyword (instead of procedure or function), but it may also be interesting to note that I m using the / operator to perform integer division.Using Delphi for Win32 (or Delphi for .NET prior to Delphi Prism), the use of the / operator would result in a floating point value, but with Delphi Prism the result is an integer if both operands were integer to begin with.

ASP.NET Test
We can compile the MyWebService solution again, and view the starting page inside a browser which will now show two methods.If you click on the Num2Word method, a new page is shown where we can test the web method.For each parameter of the web method, we can enter a value (in this case only the "value" parameter), and once we click on the Invoke button a new browser window will pop-up showing the result of our SOAP call.

As an example, enter 1964 as value and click on Invoke. The result should be a SOAP envellope with the value "eenduizend negenhonderdvierenzestig", as follows:

  <?xml version="1.0" encoding="utf-8" ?>
  <string xmlns="http://eBob42.org/">eenduizend negenhonderdvierenzestig</string>
Note that this is not the only way in which to represent 1964 in words, we could also have said "negentienhonderdvierenzestig", but that's often used to mention years instead of amounts of money.

The fact that we can use the browser to test the web service (provided it's deployed on the local machine) is very helpful when it comes to testing and debugging web services - you do not need to write a separate client application in order to test the behaviour of the web method.

Deployment
Apart from running the Web Service project from the IDE using the localhost domain, at some time you will need to deploy it to a real web server machine.This can be done using the Publish Web Site option (when you right-click on the MyWebService project in the Solution Explorer.This will show you a dialog where you can specify the deployment options, as can be seen in the following screenshot:

Make sure to uncheck the option "Allow this precompiled site to be updatable". Although this option (when checked) offers the ability to update the site later, it also requires that the Oxygene command-line compiler is deployed on the web server (a topic left for another time).For now, unckeck the option which will result in a DeployedWebService virtual directory on my local host on the development machine, found at physical path C:\Inetpub\wwwroot\DeployedWebService containing the Service.asmx file, a PrecompiledApp.config and Web.config as well as a bin directory with the App_Code.dll code behind assembly as well as an App_Code.compiled and service.asmx.cdcab7d2.compiled file.These 6 files and one directory are all that we need to deploy the web service to another machine without Delphi Prism or the Oxygene compiler installed.
As an example, I ve created a virtual directory called DelphiPrismWebService on my www.bobswart.nl web server and just copied the contents of DeployedWebService to that new virtual directory.There is no Delphi Prism installed on my web server, but we with one more change, we can access the Delphi Prism Web Services at http://www.bobswart.nl/DelphiPrismWebService/Service.asmx

Note that although Delphi Prism (or the Oxygene compiler) is not required on the web server, we must ensure that the Microsoft .NET Framework 2.0 and higher, including 3.5 is installed on the web server.Otherwise you need to remove a number of assembly, HttpHandler and HttpModule references from the web.config file.Assemblies which are not used by our simple Web Service, but which may be required when you re building a more complex Web Service or Web application.The requirement for the .NET Framework version was an option that we could select when first creating the ASP.NET Web Service project; another reason to keep this in mind when creating new projects.

Summary
In this article, I've explained how ASP.NET Web Services work and how we can create them using Delphi Prism.Next time, we'll import and use (also called consume) this Web Service using Delphi Prism again.

For more information on SOAP and Web Services with Delphi, you can check out my Delphi 2006 XML, SOAP and Web Services on Lulu.com, or my RAD Studio 2007 XML, SOAP and Web Services courseware manual in PDF format (with free updates and e-mail support), or wait for the Delphi Prism Development Essentials manual to become available which will also include ASP.NET Web Service coverage.


This webpage © 2009-2010 by Bob Swart (aka Dr.Bob - www.drbob42.com).All Rights Reserved.