Cgi website development learning instance

Source: Internet
Author: User
Tags bind error handling file upload mutex

Page Template

It may be because velocity is used to writing code in the company, and google's ctemplate is used to implement similar functions. Ctemplate is relatively simple than velocity. It only includes variable replacement, simple reference, and loop. Here are detailed documents of ctemplate, which means that there cannot be too many logic on the template.
First, deploy the dispatcher in the previous article, set the environment variables in apache, and set the template and path:
   

The code is as follows: Copy code
Ctemplate: Template: SetTemplateRootDirectory (input. getenv ("TEMPLATE_PATH "));



Then, after calling the real uri processing function through the bound handler, merge the output dictionary object with the template:

The code is as follows: Copy code
Std: string templatePath;
If (context. dict) // has dict
{
// Get template
TemplatePath = context. dict-> name () + ". tpl ";
// Expend
Std: string out;
Bool expandResult = ctemplate: ExpandTemplate (templatePath, ctemplate: STRIP_WHITESPACE, context. dict. get (), & out );

If (expandResult) // good, we expend template success
    {
Context. ostream <cgicc: HTTPHTMLHeader ();
Context. ostream <out;
    }
Else // oops, we response 500
    {
Context. ostream <cgicc: HTTPStatusHeader (500, "Internal Server Error ");
Context. ostream <"fail to expand template:" <templatePath;
    }
}
Else
{
// If no dictionary exists, it is output by the corresponding function.
}



Here is the dictionary output logic. Obtain the name and suffix of the root dictionary and search for the corresponding template in the root path. Call the ctemplate function to expand the template. Finally, the expanded content is output through the stream.
If no dictionary output is available, it is output by itself in the processing function by default (mainly to allow the processing function to output json content by itself)
If another branch is not bound, the template content is directly output:

The code is as follows: Copy code
// Find if there is a template
Std: string templatePath = path. substr (1) + ". tpl ";
If (ctemplate: mutable_default_template_cache ()-> LoadTemplate (templatePath, ctemplate: STRIP_WHITESPACE ))
{
// Expend
Std: string out;
Bool expandResult = ctemplate: ExpandTemplate (templatePath, ctemplate: STRIP_WHITESPACE,
_ EmptyDict. get (), & out );
If (expandResult) // good, we expend template success
    {
Context. ostream <cgicc: HTTPHTMLHeader ();
Context. ostream <out;
    }
Else // oops, we response 500
    {
Context. ostream <cgicc: HTTPStatusHeader (500, "Internal Server Error ");
Context. ostream <"fail to expand template:" <templatePath;
    }
}
Else // not bind and not find a template file
{
Context. ostream <cgicc: HTTPStatusHeader (404, "not find ");
Context. ostream <"not find ";
}



If not bound, use an empty dictionary to expand the template, that is, directly output the template. If none of them are found, 404 is returned.

An example of binding a function:

The code is as follows: Copy code
Void handle (Context & context)
{
Std: vector <Volume> volumes;
// FIXME: mock data
Volume v1 ("1", "a", "cover_1.png", 5 );
Volume v2 ("2", "B", "cover_2.png", 1 );

Volumes. push_back (v1 );
Volumes. push_back (v2 );

Boost: shared_ptr <ctemplate: TemplateDictionary> listPageDict (new ctemplate: TemplateDictionary ("list "));
For (int I = 0; I <volumes. size (); ++ I)
    {
Ctemplate: TemplateDictionary * listSection = listPageDict-> AddSectionDictionary ("LIST_SECTIONS ");
ListSection-> SetIntValue ("id", volumes [I]. id ());
ListSection-> SetValue ("name", volumes [I]. name ());
ListSection-> SetValue ("cover_img_path", volumes [I]. cover_path ());
    }

Context. dict = listPageDict;
}



Corresponding template file:

The code is as follows: Copy code
<! DOCTYPE html>
<Html>
<Head>
<Meta charset = "UTF-8"/>
<Title> title </title>
</Head>
<Body>
<Ol>
{{# LIST_SECTIONS }}
<Li> name :{{ name }}</li>
{{/ LIST_SECTIONS }}
</Ol>
</Body>
</Html>



The output is the name field of the Volume object.



Uri binding

The previous two blogs talked about the process parameters from uri to template output.
First, the parameter binding is simply put in a map. Here we use the boost unordered_map (that is, hashmap). In fact, we can already use the unordered_map provided by c ++ 11.

Boost: unordered: unordered_map <std: string, RequestHandleFunc> _ mapping;

The value of this map is a functor, which is defined:

Typedef boost: function <void (Context & context)> RequestHandleFunc;

That is, there is no return value. The parameter is the Context function.
The Context structure is very simple. It encapsulates input and output, that is, cgi, ostream, dict, and other objects without too much abstraction.

The code is as follows: Copy code
Struct Context
{
Cgicc: Cgicc & cgi;
Cgicc: CgiInput & input;
Std: ostream & ostream;
Boost: shared_ptr <ctemplate: TemplateDictionary> dict;
};



CgiInput is added here, mainly because cgicc encapsulates common cgi environment variables and cannot get custom environment variables (introduced in the previous blog ).
The binding function is very simple, that is, direct insertion. It should be noted that unordered_map is not thread-safe, and there is no thread similar to java's concurrent hashmap (google's intel tbb Library has a similar data structure ), so the mutex variable in the boost thread library is used:

The code is as follows: Copy code
Void bind (const std: string & path, RequestHandleFunc func)
{
Boost: unique_lock <boost: shared_mutex> lock (_ mutex );
_ Mapping. insert (std: pair <std: string, RequestHandleFunc> (path, func ));
}



Note: the boost locks are divided into unique_lock and shared_lock. Here, they are "writers", so the exclusive locks are required.
The preceding code has been added to the solution. You must also note that you need to lock the map when searching. Here is "reader", so use shared_lock:

The code is as follows: Copy code
Boost: shared_lock <boost: shared_mutex> lock (_ mutex );



To facilitate binding, a macro is written and bound directly.

The code is as follows: Copy code
# Define REGISTER_URL (URL, CLASS, METHOD)
CLASS c # CLASS; Handler: instance (). bind (URL, boost: bind (& CLASS: METHOD, & c # CLASS, _ 1 ))
No Comments


File upload and cgicc

To upload a file in html, you only need to put an input type = file in the form. If you want to use ajax asynchronous Upload (based on jquery below), pay attention to the following points (the following operations, some APIs that need to be defined based on html5 ):

1. Create an input element on the page. The type is file. If you need to select multiple files for one input file, add the attribute multiple = "multiple" to the tag ". (Note: The browser must support html5. For more information, see)

2. Obtain the uploaded file bound to this element:

Var files = document. getElementById ('XXX'). files;

First, obtain the element. By reading the file attribute of the element, you can obtain all selected files.

3. Assemble formdata because the file to be uploaded may be large. Therefore, even if multiple file selections are supported, different requests are sent here.

The code is as follows: Copy code
For (var I = 0; I <array. length; I ++ ){
Var file = files [index];
Var formData = new FormData ();
FormData. append ("file", file );
FormData. append ("id", I + 1 );
}



Here, each file is separately assembled into a formdata, which contains the file content and an id.

4. Initiate a post request using jquery:

The code is as follows: Copy code
$. Ajax ({
Type: 'post ',
Url: "file/new ",
Data: formData,
Async: true,
Cache: false,
ProcessData: false,
ContentType: false,
Xhr: function (){
MyXhr = $. ajaxSettings. xhr ();
If (myXhr. upload ){
MyXhr. upload. addEventListener ('progress', progressHandlerFunction, false );
          }
Return myXhr;
},
Success: function (data ){
If (! Data. result ){
ProgressTag. remove ();
$ ("#" + Index). after ($ ('<span style = "color: red">' + data. msg + '</span> '));
          }
      }
});



The xhr section will be mentioned later. There are two more important points here. The processData attribute must be set to false, and the contentType must be set to false. The other backend is unclear. cgicc must read the Content-Disposition attribute from the http header when processing file uploads (when multipart post is used, only when the two attributes mentioned above are set in jquery can they be correctly encapsulated according to the standard protocol, so that cgicc can read the correct content to separate strings.

5. Icing on the cake and adding progress bars:

The code is as follows: Copy code
Var progressTag = $ ("<progress/> ",{
Value: 0,
Min: 0,
Max: file. size
});
$ ("#" + Index). after (progressTag );

Function progressHandlerFunction (evt ){
If (evt. lengthComputable ){
ProgressTag. attr ("max", evt. total );
ProgressTag. attr ("value", evt. loaded );
      }
  }



Here, a progress Tag is dynamically added through jquery (added in html5), and a handler is defined. As we can see in jquery's ajax function, we can add handler function callback in the upload event by getting the native xhr object, and feedback the specific progress to the user while uploading. Of course, you can bind the download event here if necessary.

6. cgicc processes files:

Cgicc processes files similar to common form elements. The only difference is that it needs to be obtained through the cgi. getFile function, rather than cgi. getElement.

The code is as follows: Copy code
Cgicc: form_iterator idIter = cgi. getElement ("id ");
Cgicc: file_iterator fileIter = cgi. getFile ("file ");
If (idIter = cgi. getElements (). end ())
{
// Handle error
}
Int id = (int) idIter-> getIntegerValue ();

If (fileIter = cgi. getFiles (). end ())
{
// Handle error
}
Std: ofstream of (boost: lexical_cast <std: string> (id ));
FileIter-> writeToStream ();



Error handling is ignored here. By getting the form element as the file name, the file is directly written to the disk through the standard library file output stream ofstream.

This completes the simple process of selecting a file from the page and saving it to the background.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.