In this section, we will learn how to create simple programmed webServices.
In a programmed service, web pages are generated by a program,
as opposed to static pages or pages with embedded code.
(services with embedded Smalltalk code are described elsewhere.
Have a look at examples,
overview and
browse the STT classes.
For a demonstration, start a webServer in the launchers settings dialog,
and enable the STT service there.)
Here, we describe how pure programmed services are created and coded. In these, smalltalk code is fully responsible both for the generation of html pages and to handle submitted forms.
and press the "Create new HTTP Server" button.
This will instantiate a new instance of the HTTPServer class
(but not yet start it, so HTTP requests are not yet served by this).
If the default Smalltalk server port (8080) was used, a number of default services
as appropriate for demonstration purposes are also initially added
(see below on how these can be removed or others be added).
To be added: more info on how to configure FCGI.
At this point, the server is fully instantiated, but not yet answering request on its port.
To actually start the server,
find its item in the settings tree, select it,
and press the "start" button (near the bottom of the dialog).
You may want to turn on the debug and trace check-boxes,
to get some feedback on the servers actions on the transcript.
pick some service, and add it by pressing the "Add Selected Service" button.
Notice, that every service is registered under a so-called "linkName".
This is the first component of the URL-path.
For example, if a service has been registered under a linkName of "foo",
the URL to get to this service in the webBrowser will be: "http://<yourHostName>:8080/foo".
You may want to play with the webServer for a few minutes,
to see what is already provided and how the settings affect the service behavior.
As a recommendation, make sure that at least the
following services are up and running:
| WebHomePageForSTX | will respond under the "/" (i.e. root) linkname with an introduction and links to other running services. |
| HelloWorldService | will respond under the "Hello" linkname with an obvious response. |
| WebDemoApp1 | a demo service showing some simple examples. This service is also able to present its own source. |
| CommancheSTTService | a service which demonstrates embedded smalltalk code (very similar to PHP) |
Also, play with the security and authentication settings.
Finally, change the replyRepresenter settings. A replyPresenter is a wrapper, which is able to wrap another services output and add a common decoration to it. This allows you to easily change the look of a services pages (i.e. background, colors, font and icons etc.), without even touching the service itself. For now, only an exept-presenter and a null-presenter are provided. To write your own, take any of them as a starting base.
Now, we let us create our own web application...
In ST/X, web services are created as subclasses of HTTPService.
To make your life easier, the browser provides an extra menu item,
for the creation and initial dfinition of web service classes.
In the browser, select the 'New Web Application' item from the
class-list-menu,
and accept the resulting class definition template:
Answer 'yes' when asked if the application code shall be generated.
After that, go back to the webServer settings dialog, reselect the services page,
and ensure that the new service is added to the services list.
HTTPService subclass:#NewService
instanceVariableNames: ''
classVariableNames: ''
poolDictionaries: ''
category: 'WebApplications'
The required methods for a webService (which have been already generated for you as templates by the browser) are:
linkName |
(class protocol) | specifies the default linkName. That name is used as default for the registration towards the webServer. The browser-generated code will return the classes name. |
process:aRequest |
(instance protocol) | the method invoked to process HTTP-requests. The browser-generated code will generate simple HTML for a "Hello World" message. |
"process:" method later,
to fit our needs.
When you send it a request from firefox, internet explorer or any other web browser,
you should get the following response:
The argument to the "process:" message is a request object,
which contains all relevant information of the http-request.
For example, the requesting host,
the URL, any authentication information etc. are found there.
To get familiar with it, place a self halt
into your "process:" method,
and invoke it by sending a request from your firefox or IE browser.
There, inspect the request object and/or browser its class.
One special item of the request object is the response-object.
This is an object, which collects the generated HTML for the response.
It understands the write-stream protocol, so you can generate HTML simply
as if you would write it to a file.
At the entry of the "process:"-method, the response is empty.
"process:"-method,
to return some more information about the request:
process:aRequest
"This is the web applications main processing method.
It will be invoked for every incoming webBrowser-request.
The argument, aRequest contains the parameters (url, fields, parameters etc.)."
|response|
response := aRequest response.
response nextPutLine:'<HTML>'.
response nextPutLine:' <HEAD>'.
response nextPutLine:' <TITLE>Hello</TITLE>'.
response nextPutLine:' </HEAD>'.
response nextPutLine:' <BODY>'.
response nextPutLine:' <H1>Hello World !</H1>'.
response nextPutLine:' The URL was: "', aRequest url ,'"'.
response nextPutLine:' <BR>'.
response nextPutLine:' The requestor was: "', aRequest peerName ,'"'.
response nextPutLine:' <BR>'.
response nextPutLine:' The current time is: "', Time now printString ,'"'.
response nextPutLine:' <BR>'.
response nextPutLine:' The relative URL path is: "', aRequest pathRelativeToService ,'"'.
response nextPutLine:' </BODY>'.
response nextPutLine:'</HTML>'.
Of course, you can generate whatever response you like here -
It is even possible to send an HTTP-request to another webServer and postprocess its
response (that is what a meta-search engine does).
foo:aRequest
|response|
response := aRequest response.
response nextPutLine:'<HTML>'.
response nextPutLine:' <HEAD>'.
response nextPutLine:' <TITLE>Foo</TITLE>'.
response nextPutLine:' </HEAD>'.
response nextPutLine:' <BODY>'.
response nextPutLine:' <H1>Hello Foo !</H1>'.
response nextPutLine:' </BODY>'.
response nextPutLine:'</HTML>'.
and
bar:aRequest
|response|
response := aRequest response.
response nextPutLine:'<HTML>'.
response nextPutLine:' <HEAD>'.
response nextPutLine:' <TITLE>Bar</TITLE>'.
response nextPutLine:' </HEAD>'.
response nextPutLine:' <BODY>'.
response nextPutLine:' <H1>Hello Bar !</H1>'.
response nextPutLine:' </BODY>'.
response nextPutLine:'</HTML>'.
and then change the main-process method into a dispatcher:
process:aRequest
"This is the web applications main processing method.
It will be invoked for every incoming webBrowser-request.
The argument, aRequest contains the parameters (url, fields, parameters etc.)."
|response nameOfSubPage|
response := aRequest response.
nameOfSubPage := aRequest pathRelativeToService.
( #('foo' 'bar' ) includes:nameOfSubPage) ifTrue:[
self
perform:(nameOfSubPage , ':') asSymbol
with:aRequest.
^ self.
].
response nextPutLine:'Bad Request.'.
In the above code, please have a look at two specialities:
perform:-message, which takes the pages name as a message-selector
to dispatch into the "foo:" and "bar:" methods.
perform:-message
can possibly open security holes.
http://localhost:8080/NewService/foo
and
http://localhost:8080/NewService/bar
Notice the default behavior for unknown pages.
"reportBadRequest:-message to the request,
as in:
process:aRequest
"This is the web applications main processing method.
It will be invoked for every incoming webBrowser-request.
The argument, aRequest contains the parameters (url, fields, parameters etc.)."
|response nameOfSubPage|
response := aRequest response.
nameOfSubPage := aRequest pathRelativeToService.
( #('foo' 'bar' ) includes:nameOfSubPage) ifTrue:[
self
perform:(nameOfSubPage , ':') asSymbol
with:aRequest.
^ self.
].
aRequest reportNotFound:'This is not a valid foo-bar request !'.
Have a look into the HTTPRequest classes' "error-reporting"
category for more possible answers.
The classes are found under the "Net-Documents-ModelTree" category;
the classes to look at are "HTML::AbstractElement" for the
entities and "HTML::TreeBuilder" for building.
The treebuilder understands HTML-tag-like messages, and constructs a dom-like tree of HTMLElement objects on the fly. Eventually, the tree is asled for its ascii representation, which is returned as the requests response.
The advantage of using a tree is that there is no longer a need to construct it sequentially from top to bottom, as would be the case with sequential, linewise html-generation. Thus, you can define and use a much more modular aproach, in which components and parts of the document can be easily generated from building blocks.
Using this library, the code for the foo-handler would be:
foo:aRequest
|response builder|
response := aRequest response.
builder := HTML::TreeBuilder new.
builder beginWith:(HTML::Document new).
builder
head;
title:'Foo';
headEnd;
body;
h1:'Hello Foo!';
bodyEnd.
response nextPutAll:(builder rootElement htmlString).
Text to be continued ...
Copyright © Claus Gittinger Development & Consulting
Copyright © eXept Software AG
<cg@exept.de>