EiffelWeb: processing forms from the Web Contents 1. THE ROLE OF EIFFELWEB Why use EiffelWeb? Organization of this document Classes 2. WRITING A FORM-PROCESSING SYSTEM Writing a basic script A complete example 3. HOW DO FORMS GET PROCESSED? 4. ACCESSING VISITOR INFORMATION AND ENVIRONMENT VARIABLES Visitor information Accessing environment variables 5. USING THE DEBUGGING FACILITIES Using make_debug as a creation procedure Environment variables Error handling An example using make_debug 6. GENERATING HTML OUTPUT APPENDIX A: Class CGI_INTERFACE APPENDIX B: Class CGI_ENVIRONMENT APPENDIX C: Class HTML_GENERATOR APPENDIX D: Class HTML_CONSTANTS ---------------------------------------------------------------------------- Report identification EiffelWeb: Processing forms from the Web with Eiffel, ISE Technical Report TR-EI-60/WE. Publication history First published: August 1996. Version: 3.3.9, August 1996. Corresponds to version 3.3.9 of the ISE Eiffel 3 environment. Authors Fabrice Franceschi, Bertrand Meyer Reviewers: Xavier Le Vourch, Moriah Sandberg. Software credits EiffelWeb was designed and implemented by Fabrice Franceschi. Project leader: Xavier Le Vourch. Copyright notice and proprietary information Copyright © Interactive Software Engineering Inc. (ISE), 1996. All uses of the product documented in this book are subject to the terms and conditions of the ISE Eiffel user license. Any other use or duplication is a violation of the applicable laws on copyright, trade secrets and intellectual property. ---------------------------------------------------------------------------- 1. THE ROLE OF EIFFELWEB The World-Wide Web provides a remarkably versatile way to make information available to the world. But the flow should not just be one way: a Web site will want to obtain input from its visitors. This can provide feedback, new ideas, and sales leads. The simplest way for a Web site visitor to send such input is to fill in a form on one of the site's pages. This requires, on the other end, some software to process the form and extract its information. Such software is known as a CGI script, where the initials stand for Common Gateway Interface. Before EiffelWeb, CGI scripts were usually written in scripting languages such as Perl, or in C. For very simple processing of small forms this suffices; but scripts, like any other software,must grow and evolve. Object technology can offer a better solution - easier to produce, easier to maintain, easier to extend - than low-level approaches. The EiffelWeb library provides script developers with a simple set of tools to develop CGI scripts using the full benefits of Eiffel. Why use EiffelWeb? Although the concepts should be simple, form processing can be unpleasantly tedious. EiffelWeb provides a general framework, taking care of all the repetitive details, and frees you of many low-level tasks. In particular: * You do not need to worry about which of the two available methods (GET and POST) has been used to return data. One uses an environment variable, the other writes to the standard input, but EiffelWeb will get the data, wherever it is, to your application. * The basic format of the data returned from a form is very low-level: a set of fields separated by & characters, with special encoding for blanks and non-alphabetic characters. With ordinary CGI scripts, you must parse this information. EiffelWeb does the parsing for you and makes the resulting fields directly available. * After processing a form you will often display further information to the person who has submitted it. This information must be in HTML format (HTML, HyperText Markup Language, is the language used to format documents to be displayed by Web browsers). With ordinary scripting languages, you must take care of the HTML codes yourself. With EiffelWeb, you can use a set of high-level procedures taking care of the basic forms of output; the procedures will generate the proper HTML codes for you. You do not need to know HTML to use EiffelWeb. * Debugging a form-processing system can be extremely tricky. EiffelWeb provides you with a rich set of mechanisms to try out your application, set environment variables, specify what should happen in the case of an exception. * Best of all, the actual processing can benefit from the whole power of the Eiffel language, the ISE Eiffel environment, and the ISE Eiffel libraries. You have a full development environment at your disposal, rather than the limited expressive power of a simple scripting language. Organization of this document This manual shows how to use EiffelWeb. It is organized as follows: * Below is a short description of the EiffelWeb classes. * Chapter 2 shows how to write a small form-processing application. The example will be sufficient for many simple uses. * Chapter 3 gives some background about HTML form processing. * Chapter 4 explains how your application can access user information and environment variables. * Chapter 5 describes debugging facilities. * Chapter 6 presents the class HTML_GENERATOR for generating HTML code. * Appendix A presents the principal class, CGI_INTERFACE. * Appendix B presents class CGI_ENVIRONMENT. * Appendix C presents class HTML_GENERATOR. * Appendix D presents class HTML_CONSTANTS, used by HTML_GENERATOR. Classes Here is a short description of each class: * CGI_ENVIRONMENT: provides access to environment variables, including all the standard variables. * CGI_INTERFACE: a deferred class that encapsulates the behavior of all CGI scripts. It includes the data processing operations common to all scripts, namely the parsing of the string sent by the server and the debugging facilities. * HTML_CONSTANTS: defines a subset of the HTML language tags as Eiffel constants. Its obvious purpose is to be used for HTML generation (more will be added in future versions). * HTML_GENERATOR: implements a series of routines facilitating the generation of basic HTML documents. The output medium can be changed by simply redefining the routine put_basic. 2. WRITING A FORM-PROCESSING SYSTEM Processing Web forms with EiffelWeb is straightforward, as EiffelWeb takes care of parsing the form and makes the visitor's input available to you in the form of Eiffel objects, instances of EiffelWeb classes and their descendants. Writing a basic script Here are the basic steps to process the data entered by someone (called the "visitor" in the rest of this document) who has filled a form on your Web site: * Write an Eiffel class YOUR_FORM describing your form. YOUR_FORM should be a descendant from the EiffelWeb class CGI_INTERFACE, itself an heir to CGI_ENVIRONMENT which provides access to environment variables. * Write in YOUR_FORM an effective version of the deferred feature execute inherited from CGI_INTERFACE. This is the body of your form processing, where you can manipulate the data entered by the visitor and, if desired, produce HTML output for display to the visitor. * Either define the make creation procedure inherited from CGI_INTERFACE as a creation procedure of YOUR_FORM, or write your own creation procedure if you need more specific creation actions. The default make starts off everything as needed and calls execute; for most uses this is what you need. A complete example Let us build a small example. Our system will simply display the results of any HTML form as an indented list of name-value pairs. The name of the field will appear in bold face. You only need one class, the one called YOUR_FORM above - here CGI_DEMO; it can keep the inherited make as a creation procedure. Here is the class: class CGI_DEMO inherit CGI_INTERFACE creation make feature execute is -- Analyse form's fields and output them as name-value pairs. do ... See next ... end end -- class CGI_DEMO with execute effected as follows: execute is -- Analyse form's fields and output them as name-value pairs. local i: INTEGER; vl: LINKED_LIST [STRING] do generate_html_header; put_basic ("You have submitted the following name/value pairs: %N"); put_glossary_start from i := fields.lower until i > fields.upper loop put_glossary_term (fields.item (i)); put_glossary_definition; put_line_break; vl := value_list (fields.item (i)); from vl.start until vl.after loop put_basic (vl.item); vl.forth; if not vl.after then put_line_break end end; i := i + 1; end put_glossary_end end Procedure execute loops over the elements of the array fields, which holds the names of the fields. For each field it loops over the list of values for that field, which may have zero, one or more elements. Procedure generate_html_header, which like the other features used by this version of execute comes from CGI_INTERFACE, prints on the standard output the header of an HTML reply, which is necessary to avoid getting an error from the Web server. You will have noted that this text does not contain any HTML code. This is because using EiffelWeb does not require knowing HTML; you can produce output using abstract output procedures such as put_line_break, put_basic and generate_html_header which will take care of generating the appropriate HTML output. If you do know HTML and want to generate specific output, you can do so using the class HTML_GENERATOR (see section 6). 3. HOW DO FORMS GET PROCESSED? To help understand EiffelWeb it is useful to have a general picture of the tasks involved in processing a form received through the World-Wide Web. When a visitor clicks on the submit button of an HTML form, the visitor's Web browser sends the contents of text fields, selected buttons and items to the server. In turn, the server transmits these data to the CGI script associated with the form. The transfer can be done via two distinct methods: * GET: data is made available through the environment variable, QUERY_STRING * POST: data is available on the standard input of the CGI script With either method, all the fields are concatenated into a unique string which has the following structure: field1=value1&field2=value2&... Space characters are converted to `+' and any special character is replaced by its hexadecimal code. Execution of the CGI script also sets a number of environment variables, defining: the method, GET POST, with which the script was called (REQUEST_METHOD); the length of the data (CONTENT_LENGTH); the name of the server (SERVER_NAME); and others. A CGI script written in an ordinary scripting language will typically perform the following tasks in response to a visitor submitting a form: * Determine the method with which it was called, so as to know where to get the input. With EiffelWeb you will not need this step, as EiffelWeb takes care of finding the input for you. * Parsing the input, to decode the fields and obtain their values. With EiffelWeb you do not need to worry about this aspect, as EiffelWeb decodes everything and puts the result into Eiffel objects. * Process the input as needed by the application. EiffelWeb provides a specific place (the procedure execute of class CGI_INTERFACE) for such processing, various features and an array fields containing all the input fields, and of course the whole power of ISE Eiffel and its libraries to perform all the processing that you need. * Most of the time, produce output to be sent to the visitor in HTML form. Here too EiffelWeb provides all the necessary output facilities, taking care of generating the proper HTML syntax for you. 4. ACCESSING VISITOR INFORMATION AND ENVIRONMENT VARIABLES EiffelWeb provides a set of classes to access information provided by the visitor, as well as the values of various environment variables. Visitor information When a visitor enters information into a form, the Web server gets it in a raw, hardly usable form. This is where EiffelWeb steps in: it parses the information, and makes it available in a form that is convenient for direct processing by your classes according to your needs. A given field may have zero, one or move values, depending both on the field's type and on what the visitor entered in it. In usual CGI processing, there is no easy way to find out beforehand. EiffelWeb, however, enables you to obtain the information through a number of queries from class CGI_INTERFACE, which you can all apply to a field: * The query value_count yields the number of values available for a field; this is particularly useful when you need to process multiple values differently from single values * In the single-value case, the query value yields that value. (For multiple values it yields the first one.) * In the multiple-value case, the query value_list returns a LINKED_LISTcontaining the values. In the case of checkboxes and similar kinds of fields, your system will not see any field, not even an empty one, if the user did not check the box. To find out whether a field is available, use the query field_defined. If, as is often the case, you know the name of a field, you can just pass that name as a string argument to any of the preceding queries. You can also, as in the example of section 2, loop over all the fields of the form, using the array fields: ARRAY[STRING] and passing the value of fields.item (i), for various i, to the above queries. All the features listed in this section belong to class CGI_INTERFACE, whose complete specification appears in Appendix A. Accessing environment variables A Web server can set a number of environment variables, which will not change during the processing of a form and so can be represented as once functions, all returning strings. These strings are available from features of class CGI_ENVIRONMENT, whose complete specification appears in Appendix B. CGI_ENVIRONMENT also includes a procedure set_environment_variable to set environment variables to specific values, useful only for debugging. 5. USING THE DEBUGGING FACILITIES One of the most difficult steps in writing form processing software with traditional CGI scripts is debugging. When the script does not complete successfully, it exits with an error code; in such a case the Web server merely reports an internal error occurred, which is not much help. EiffelWeb makes the debugging process much more convenient through features of class CGI_INTERFACE. Using make_debug as a creation procedure Besides make, CGI_INTERFACE provides another procedure that can be used as creation procedure: make_debug, which extends make. Using make_debug you can perform extra operations, for example set environment variables to special values, and you can parse the input before starting any processing. A piece of methodological advice: once you are done with debugging, remember to set the creation procedure back to the normal make, just in case make_debug sets environment variables to values which are not appropriate for normal processing, or generates output that is intended for you and not for your visitors. Environment variables To recreate the context in which the application is called by the WWW server, you need to set the corresponding environment variables. This can lead to one of the most annoying aspects of traditional CGI script creation and debugging. You must have exit the working environment, set the variables, and restart the environment; or you could set the variables from the script, but this is just as inconvenient. EiffelWeb provides a better solution: set the variables on-the-fly by passing them as argument to the script. You will for example call the application as: your_script REQUEST_METHOD=GET QUERY_STRING="name=foo" setting the variables REQUEST_METHOD and QUERY_STRING to GET and to name=foo. To set the default values, make_debug calls the procedure set_environment; in its version inherited from CGI_INTERFACE, set_environment does nothing, but you can redefine it to set environment variables to specific values. To set an individual environment variable, use set_environment_variable. The defaults will be overriden by the scripts arguments if any. Here is an example class using these facilities: class CGI_SCRIPT inherit CGI_INTERFACE redefine set_environment end creation make_debug feature set_environment is -- Set two environment variables do set_environment_variable ("SCRIPT_NAME", "form_handler"); set_environment_variable ("SERVER_NAME", "test.oursite.com"); end end -- class CGI_SCRIPT Of course, you can still test your script manually and set the environment variables from the command line as follows (here expressed in C-shell syntax): setenv REQUEST_METHOD "GET" setenv QUERY_STRING "" setenv CONTENT_LENGTH 0 setenv SERVER_NAME "no_server" setenv SCRIPT_NAME "test_script" Error handling The creation procedure, whether make or make_debug, will catch any exception occurring during the execution of the script and will handle it according to the choosen option which set_environment can set by calling one of the three following procedures: * set_no_debug: an exception will terminate processing with no further action. * set_message: an exception will terminate processing and display a message explaining that an internal error occurred. * set_exception_trace: an exception will cause the the script to terminate and display the exception trace. This will only be in effect if the Lace option exception_trace is on; this is the case by default in all modes except final mode, for which you may turn it on through the line exception_trace (yes) in the default paragraph of your Ace. In all cases the application will not return an error code but keep control of the execution, therefore preventing the Web server from displaying its own message. If you detect an irrecoverable error during processing, you can stop execution using procedure raise_error. This will display an error message, which you can set through an earlier call to set_error. All the features listed in this section belong to class CGI_INTERFACE, whose complete specification appears in Appendix A. An example using make_debug Let's use again the basic example. Employing make_debug instead of make as creation procedure will allow us to change the debug level - e.g. to get the exception trace if an exception occurs. This will also allow us to set default values for some environment variables. This time instead of an indented list the results will be displayed in a bulleted list. class CGI_DEMO inherit CGI_INTERFACE redefine set_environment end creation make_debug feature set_environment is do set_exception_trace; set_environment_variable ("SERVER_NAME", "no_server"); set_environment_variable ("SCRIPT_NAME", "test_script"); end execute is local i: INTEGER; vl: LINKED_LIST[STRING] do generate_html_header; put_basic ("You have submitted the following name / value pairs:"); put_unordered_list_start; from i := fields.lower until i > fields.upper loop put_list_item_start; put_bold (fields.item (i)); put_line_break; vl := value_list (fields.item (i)); from vl.start until vl.after loop put_basic (vl.item); vl.forth; if not vl.after then io.putstring (", ") end end; put_list_item_end; i := i + 1 end; put_unordered_list_end end end -- class CGI_DEMO 6. GENERATING HTML OUTPUT Many form-processing systems will have to display information back to the visitor, if only to thank the visitor for filling in the form. This information must be in HTML format; it will often include some of the form's input, or elements generated from that input. To avoid forcing you to write low-level HTML code, the class HTML_GENERATOR provides a set of procedures to take care of the most common cases, such as: * put_bold: write a string in boldface. * put_header1: write a level 1 title. * put_preformatted: write a string exactly as it was. You can still, of course, include specific HTML codes if you wish. The procedures provided cover only a subset of HTML. By default all written information will go to the standard output. You can redirect it through the procedure put_basic. All the features listed are part of class HTML_GENERATOR whose complete specification appears in Appendix C. ---------------------------------------------------------------------------- Appendix A: class CGI_INTERFACE indexing description: "Access to information provided by a user through an HTML form. This class may be used as ancestor by classes needing its facilities." status: "" date: "$Date: $" revision: "$Revision: $" deferred class interface CGI_INTERFACE feature -- Initialization make -- Initiate input data parsing and process information. make_debug (args: ARRAY [STRING]) -- Set environment variables and proceed to regular execution. feature -- Access fields: ARRAY [STRING] -- Names of fields in the form. value (field_name: STRING): STRING -- First (unique?) value for a field. require field_not_void: field_name /= Void; field_exists: field_defined (field_name) ensure value_exists: Result /= Void value_count (field_name: STRING): INTEGER -- Number of values for a field. require field_not_void: field_name /= Void; field_exists: field_defined (field_name) ensure valid_count: Result >= 0 value_list (field_name: STRING): LINKED_LIST [STRING] -- List of values for a field. require field_not_void: field_name /= Void; field_exists: field_defined (field_name) ensure valid_count: Result.count = value_count (field_name) feature -- Status report field_defined (field_name: STRING): BOOLEAN -- Is field field_name defined? feature -- Status setting set_exception_trace -- Display exception trace on exception. set_message -- Display error message on exception. set_no_debug -- Do nothing on exception. feature -- HTTP facilities Generate_html_header -- Generate CGI header reply. feature -- Miscellanous execute -- Process user provided information. set_environment -- Set environment variable to user value. end -- class CGI_INTERFACE ---------------------------------------------------------------------------- Appendix B: class CGI_ENVIRONMENT indexing description: "Access to environment variables set by the HTTP server when the CGI application is executed. This class may be used as ancestor by classes needing its facilities." status: "" date: "$Date: $" revision: "$Revision: $" class interface CGI_ENVIRONMENT feature -- Environment variable setting set_environment_variable (variable, val: STRING) -- Set environment variable variable to val. require valid_variable: variable /= Void and then variable.count > 0; valid_value: val /= Void feature -- Not request-specific environment variables Gateway_interface: STRING -- Revision of the CGI specification to which this server complies. Server_name: STRING -- Server's hostname, DNS alias, or IP address. Server_software: STRING -- Name and version of information server answering the request. feature -- Headerline based environment variables Http_accept: STRING -- MIME types which the client will accept. Http_user_agent: STRING -- Browser the client is using to send the request. feature -- Request-specific environment variables Auth_type: STRING -- Protocol-specific authentication method used to validate user. Content_length: STRING -- Length of the said content as given by the client. Content_type: STRING -- Content type of data. Path_info: STRING -- Extra path information, as given by the client. Path_translated: STRING -- Translated version of PATH_INFO provided by server. Query_string: STRING -- Information which follows ? in URL referencing CGI program. Remote_addr: STRING -- IP address of the remote host making the request. Remote_host: STRING -- Hostname making the request. Remote_ident: STRING -- User name retrieved from server if RFC 931 supported. Remote_user: STRING -- Username, if applicable. Request_method: STRING -- Method with which the request was made. Script_name: STRING -- Virtual path to the script being executed. Server_port: STRING -- Port number to which request was sent. Server_protocol: STRING -- Name and revision of information protocol of this request. end -- class CGI_ENVIRONMENT ---------------------------------------------------------------------------- Appendix C: class HTML_GENERATOR indexing description: "HTML generation. This class may be used as ancestor by classes needing its facilities" status: "" date: "$Date: $" revision: "$Revision: $" class interface HTML_GENERATOR feature -- Transformation put_basic (s: STRING) -- Write s to medium. require string_not_void: s /= Void feature -- Miscellaneous put_bold (text: STRING) -- Put text in bold face. require text_not_void: text /= Void put_glossary_definition -- Put glossary definition tag. put_glossary_end -- End glossary list. put_glossary_start -- Start glossary list. put_glossary_term (text: STRING) -- Put text as glossary term. require text_not_void: text /= Void put_header1 (title: STRING) -- Put title as level 1 header. require title_not_void: title /= Void put_header2 (title: STRING) -- Put title as level 2 header. require title_not_void: title /= Void put_header3 (title: STRING) -- Put title as level 3 header. require title_not_void: title /= Void put_header4 (title: STRING) -- Put title as level 4 header. require title_not_void: title /= Void put_header5 (title: STRING) -- Put title as level 5 header. require title_not_void: title /= Void put_header6 (title: STRING) -- Put title as level 6 header. require title_not_void: title /= Void put_horizontal_rule -- Put a horizontal rule. put_italic (text: STRING) -- Put text in italic. require text_not_void: text /= Void put_line_break -- Put line break. put_link (url, anchor: STRING) -- Attach text anchor to url require anchor_not_void: anchor /= Void; url_not_void: url /= Void put_list_item_end -- Write list item end tag. put_list_item_start -- Write list item start tag. put_ordered_list_end -- End ordered list. put_ordered_list_start -- Start ordered list. put_paragraph_end -- End paragraph put_paragraph_start -- Start paragraph. put_preformatted (text: STRING) -- Put preformatted text text. require text_not_void: text /= void put_unordered_list_end -- End unordered list. put_unordered_list_start -- Start unordered list. end -- class HTML_GENERATOR ---------------------------------------------------------------------------- Appendix D: class HTML_CONSTANTS indexing description: "Tags of subset of the HTML language. This class may be used as ancestor by classes needing its facilities" status: "" date: "$Date: $" revision: "$Revision: $" class interface HTML_CONSTANTS feature -- Constants Bold_end: STRING is "" -- Bold face end tag. Bold_start: STRING is "" -- Bold face start tag. Glossary_definition: STRING is "
" -- Glossary list definition. Glossary_end: STRING is "" -- Glossary list end tag. Glossary_start: STRING is "
" -- Glossary list start tag. Glossary_term: STRING is "
" -- Glossary list term. H1_end: STRING is "" -- Header level 1 end tag. H1_start: STRING is "

" -- Header level 1 start tag. H2_end: STRING is "

" -- Header level 2 end tag. H2_start: STRING is "

" -- Header level 2 start tag. H3_end: STRING is "

" -- Header level 3 end tag. H3_start: STRING is "

" -- Header level 3 start tag. H4_end: STRING is "

" -- Header level 4 end tag. H4_start: STRING is "

" -- Header level 4 start tag. H5_end: STRING is "

" -- Header level 5 end tag. H5_start: STRING is "
" -- Header level 5 start tag. H6_end: STRING is "
" -- Header level 6 end tag. H6_start: STRING is "
" -- Header level 6 start tag. Horizontal_rule: STRING is "
" -- Horizontal rule tag. Italic_end: STRING is "" -- Italic end tag. Italic_start: STRING is "" -- Italic start tag. Line_break: STRING is "
" -- Line break tag. List_item_end: STRING is "" -- List item end tag. List_item_start: STRING is "
  • " -- List item start tag. Ordered_list_end: STRING is "" -- Ordered list end tag. Ordered_list_start: STRING is "
      " -- Ordered list start tag. Paragraph_end: STRING is "

      " -- Paragraph end tag. Paragraph_start: STRING is "

      " -- Paragraph start tag. Preformatted_end: STRING is "" -- Preformatted text end tag. Preformatted_start: STRING is "

      "
                     -- Preformatted text start tag.
      
           Unordered_list_end: STRING is ""
                     -- Unordered list end tag.
      
           Unordered_list_start: STRING is "
        " -- Unordered list start tag. end -- class HTML_CONSTANTS