zhxy/http服务器/appweb-4.3.4-0/doc/guide/esp/users/mvcTour.html

454 lines
24 KiB
HTML

<!-- BeginDsi "dsi/head.html" -->
<!DOCTYPE html>
<html lang="en">
<head>
<title>Embedthis Appweb 4.3.4 Documentation</title>
<meta name="keywords" content="embedded web server, web server software, embedded HTTP, application web server,
embedded server, small web server, HTTP server, library web server, library HTTP, HTTP library" />
<meta name="description" content="Embedthis Sofware provides commercial and open source embedded web servers for
devices and applications." />
<meta name="robots" content="index,follow" />
<link href="../../../doc.css" rel="stylesheet" type="text/css" />
<link href="../../../print.css" rel="stylesheet" type="text/css" media="print"/>
<!--[if IE]>
<link href="../../../iehacks.css" rel="stylesheet" type="text/css" />
<![endif]-->
<link href="http://www.google.com/cse/style/look/default.css" type="text/css" rel="stylesheet" />
</head>
<body>
<div class="top">
<a class="logo" href="http://appwebserver.org/">&nbsp;</a>
<div class="topRight">
<div class="search">
<div id="cse-search-form"></div>
<div class="version">Embedthis Appweb 4.3.4</div>
</div>
</div>
<div class="crumbs">
<a href="../../../index.html">Home</a>
<!-- EndDsi -->
&gt; <a href="index.html">ESP Guide</a> &gt; <b>ESP MVC Tour</b>
</div>
</div>
<div class="content">
<div class="contentRight">
<h1>Quick Nav</h1>
<ul class="nav">
<li><a href="#blog">Blog App</a></li>
<li><a href="#create">Creating a New App</a></li>
<li><a href="#run">Run the App</a></li>
<li><a href="#hello">Hello World</a></li>
<li><a href="#layout">Layout Pages</a></li>
<li><a href="#dynamic">Dynamic Content</a></li>
<li><a href="#scaffolds">Scaffolds</a></li>
<li><a href="#newPosts">Create New Posts</a></li>
<li><a href="#homePage">Edit the Home Page</a></li>
<li><a href="#layout">Changing the Layout</a></li>
<li><a href="#validations">Validations</a></li>
<li><a href="#learn">Learn More</a></li>
</ul>
<!-- BeginDsi "dsi/espSeeAlso.html" -->
<h1>See Also</h1>
<ul class="nav">
<li><a href="../../../guide/esp/users/using.html">ESP Overview</a></li>
<li><a href="../../../guide/esp/users/tour.html">ESP Tour</a></li>
<li><a href="../../../guide/esp/users/template.html">Templates and Layouts</a></li>
<li><a href="../../../guide/esp/users/controls.html">HTML Controls</a></li>
<li><a href="../../../guide/esp/users/config.html">ESP Configuration Directives</a></li>
<li><a href="../../../guide/esp/users/mvc.html">Model-View-Controller</a></li>
<li><a href="../../../guide/esp/users/generator.html">Application Generator</a></li>
<li><a href="../../../guide/esp/users/controllers.html">Controllers and Actions</a></li>
<li><a href="../../../guide/esp/users/database.html">Database Interface</a></li>
<li><a href="../../../guide/appweb/users/caching.html">Caching Responses</a></li>
</ul>
<!-- EndDsi -->
</div>
<div class="contentLeft">
<h1>ESP MVC Tour</h1>
<p>This quick tour of the ESP MVC Framework provides an overview of the ESP Model-View-Controller
framework and how to use it for your web applications.</p>
<p>First make sure you have read the <a href="../../appweb/users/quickStart.html">Quick Start</a>,
and <a href="tour.html">ESP Tour</a> and that you have ESP installed on your system so you
can type along as you go. This tour uses the <em><a href="generator.html">esp</a></em> application
generator.</p>
<a id="blog"></a>
<a id="create"></a>
<h2 class="section">Creating a New Application</h2>
<p>To create a new ESP application, you will use <em>esp</em>, the ESP application generator
program. Type the following esp command in a command terminal window:</p>
<pre>
<b>home&gt;</b> esp generate app blog<b>
[CREATE] Directory: blog
[CREATE] Directory: blog/cache
[CREATE] Directory: blog/controllers
[CREATE] Directory: blog/db
[CREATE] Directory: blog/layouts
[CREATE] Directory: blog/static
[CREATE] Directory: blog/static/images
[CREATE] Directory: blog/static/js
[CREATE] Directory: blog/static/themes
[CREATE] Directory: blog/views
[CREATE] File: blog/layouts/default.esp
[CREATE] File: blog/static/images/banner.jpg
[CREATE] File: blog/static/images/favicon.ico
[CREATE] File: blog/static/images/splash.jpg
[CREATE] File: blog/static/index.esp
[CREATE] File: blog/static/js/jquery.esp.js
[CREATE] File: blog/static/js/jquery.js
[CREATE] File: blog/static/js/jquery.tablesorter.js
[CREATE] File: blog/static/layout.css
[CREATE] File: blog/static/themes/default.css
[CREATE] File: blog/appweb.conf
[CREATE] Database: blog/db/blog.mdb
[TASK] Complete</b>
</pre>
<p>This simple command accomplished quite a bit. It first created a new directory called <b>blog</b> for
the application, and then created subdirectories for various parts of the application. Initially, some of
these directories are empty, but they will be used as your application grows.</p>
<p>The command also created an appweb.conf configuration file to allow you to run appweb for your application.
The appweb.conf configuration file is the place where you can define your database name, and other
configuration settings. The ESP web framework follows the <i>"convention over
configuration"</i> philosophy popularized by <a href="http://www.rubyonrails.org">Ruby on Rails</a>. This
means that ESP adopts certain conventions about where files and directories should be placed and about
how names are used. If you work with these conventions, then you need to do little or no configuration.
Things will just work.</p>
<p>Here are the most important directories:</p>
<table title="directories">
<thead>
<tr>
<th>Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>cache</td>
<td>Cached controller and view modules</td>
</tr>
<tr>
<td>controllers</td>
<td>Application controller code</td>
</tr>
<tr>
<td>db</td>
<td>Database file and database initialization scripts</td>
</tr>
<tr>
<td>layouts</td>
<td>Page layout templates</td>
</tr>
<tr>
<td>static</td>
<td>Static web pages</td>
</tr>
<tr>
<td>views</td>
<td>View web pages</td>
</tr>
</tbody>
</table>
<p>See the "<a href="generator.html">esp</a>" command documentation for an explanation of the other generated
directories.</p>
<!--
<p>The purpose of the other directories:</p>
<table title="directories">
<thead>
<tr>
<th>Name</th><th>Description</th>
</tr>
</thead>
<tbody>
<tr><td>.tmp</td><td>Temporary files</td></tr>
<tr><td>bin</td><td>Binary programs and tools</td></tr>
<tr><td>doc</td><td>Generated documentation for the application</td></tr>
<tr><td>logs</td><td>Application logs</td></tr>
<tr><td>messages</td><td>Message catalogues for internationalized applications</td></tr>
<tr><td>test</td><td>Unit tests</td></tr>
<tr><td>utils</td><td>Source for utility scripts and programs</td></tr>
</tbody>
</table>
-->
<a id="run"></a>
<h2 class="section">Running your Application</h2>
<p>You can immediately run your application after generation. The <em>esp</em> command will invoke appweb
to run your application: </p>
<pre>
<b>home&gt;</b> cd blog
<b>home/blog&gt;</b> esp run
<b>[RUN] appweb -v
appweb: 2: Configuration for Embedthis Appweb
appweb: 2: ---------------------------------------------
appweb: 2: Host: magnetar.local
appweb: 2: CPU: x86_64
appweb: 2: OS: MACOSX
appweb: 2: Distribution: Apple 10.7.1
appweb: 2: Version: 4.0.0-B0
appweb: 2: BuildType: DEBUG
appweb: 2: ---------------------------------------------
appweb: 2: Config File /private/tmp/blog/appweb.conf
appweb: 2: Upload directory: /tmp
appweb: 2: Set connector "netConnector"
appweb: 2: Add handler "fileHandler" on host "default" for extensions:
html git jpeg jpg png pdf ico css js ""
appweb: 2: Activating module (Loadable) espHandler
appweb: 2: Add handler "espHandler" on host "default" for extensions: *.esp
appweb: 2: Add handler "espHandler" on host "default" for route: ""
appweb: 2: Configured host ":4000" at "."
appweb: 1: Started HTTP service on "*:4000"
appweb: 1: HTTP services Started at Wed Oct 05 18:08:45 2013 PDT
appweb: 1: Starting host named: "127.0.0.1:4000"</b>
</pre>
<p>Then enter <em>localhost:4000</em> in your browser. You should see your first application home
page.</p>
<img src="../../../images/esp/tour/home.png" class="screen" alt="home" />
<a id="hello"></a>
<h2 class="section">Hello World</h2>
<p>The next step is to create a simple "Hello World" web page. ESP web pages have an <em>.esp</em>
extension. Create a file called <em>hello.esp</em> under the <em>static</em> directory with the following
content:</p>
<pre>
&lt;%@ layout ""%&gt;
&lt;html&gt;
&lt;body&gt;
&lt;h1&gt;Hello World&lt;/h1&gt;
&lt;/body&gt;
</pre>
<p>Don't worry about the layout directive for now, that tells ESP not to use a layout page.</p>
<p>To view the "Hello" web page, type in the following url:
<em>http://localhost:4000/static/hello.esp</em> in your browser.</p>
<img src="../../../images/esp/tour/hello.png" alt="hello" class="bare-screen" />
<a id="layouts"></a>
<h2>Layouts</h2>
<p>The ESP templating engine can apply a layout page to allow each page so that it inherits its layout and
look and feel from a master layout page. The ESP layout directive allows you to control what layout
page (if any) is used. In the previous example, we set the layout page to "" which means &mdash;
"don't use a layout page". If we remove that directive, the page will use the default layout. In this case
we remove the html and body elements as well as the layout page has been defined with those. The new page
now has a single line.</p>
<pre>
&lt;h1&gt;Hello Bright New World&lt;/h1&gt;
</pre>
<p>And the web page now looks like this in your browser:</p>
<img src="../../../images/esp/tour/hello-layout.png" alt="hello" class= "bare-screen" />
<a id="dynamic"></a>
<h2 class="section">Dynamic Content</h2>
<p>While that was fun, the output is static and boring, so let's add some dynamic content. You can embed
"C" language code and ESP function calls by including them inside a special ESP web page directive that
will be executed and converted to HTML before being sent to the client. There are a variety of server-side
ESP web page directives, the one you'll use first is: <em>&lt;%= expression %&gt;</em>.</p>
<p>To add the current date and time, modify the hello.esp web page and add the highlighted line:</p>
<pre>
&lt;h2&gt;<b>Generated on &lt;%= mprGetDate(0); %&gt;</b>&lt;/h2&gt;
</pre>
<p>Now when you re-run the page, it will display the current date and time:</p>
<img src="../../../images/esp/tour/hello-dynamic.png" alt="hello" class="bare-screen" />
<h3>No Restart Require</h3>
<p>Notice that you did not have to restart the web server, nor did you have to manually recompile the
application. Rather, ESP transparently recompiled the web page in the background. ESP noticed that the hello.esp
web page has been modified and it re-parsed and compiled it into a loadable module, ready for
execution.</p>
<p>You can also embed more complex ESP into our page, like:</p>
<pre>
&lt;h3&gt;&lt;% render("Request method is %s", getMethod()); %&gt;&lt;/h3&gt;
&lt;%
int i;
for (int i = 0; i &lt; 10; i++) {
render(" Line: %d&lt;/br&gt;\r\n", i);
}
%&gt;
</pre>
<p>By using the ESP statement directive <em>&lt;% code %&gt;</em>, you can embed arbitrary "C" language
statements in your web page. The <em>render</em> function allows you to write arbitrary data which is patched
back where the directive was defined in the page. See the <a href="template.html">Views and Layouts</a>
document for full details about all the ESP web page directives.</p>
<a id="views"></a>
<a id="scaffolds"></a>
<h2 class="scaffolds">Scaffolds</h2>
<p>Scaffolding is a quick way to generate pieces of your application. The <em>esp</em> command can generate
database tables, views and controllers for you. The command below will create a <em>post</em> database table
with a blog post title and post comment body. The <em>title</em> is a string data type and the <em>body</em> is a
multi-line text field.</p>
<pre>
<b>home/blog&gt;</b> esp generate scaffold post title:string body:text
<b> [CREATE] /private/tmp/blog/controllers/post.c
[CREATE] /private/tmp/blog/views/post-list.c
[CREATE] /private/tmp/blog/views/post-edit.c
[UPDATE] Database schema
[TASK] Complete</b>
</pre>
<p>This command created a database table called <em>post</em> and a post controller with
<em>post-list</em> and <em>post-edit</em> views.</p>
<p>Now if you set your browser to the URI for the post controller, you will see your post listing screen.
<img src="../../../images/esp/tour/post-list.png" alt="postList" class="bare-screen" />
<a id="newPosts"></a></p>
<h2 class="section">Create New Posts</h2>
<p>The new post button directs your browser to the <em>/post/init</em> URI. Behind the scenes, Appweb
parses this URI and and selects the appropriate request route and handler for the request. It
then identifies <em>post</em> as the name of the controller and <em>init</em> as the name of an action to invoke.
The controller file <em>controllers/post.c</em> defines the controller action to respond to this
request.</p>
<p>The controller file defines functions called actions, that are bound to URIs of the same name. Actions
are defined using the <em>espDefineAction</em> API in the initialization function of the controller. When
a request comes for a given action, the corresponding action method is invoked.</p>
<p>A minimal controller file looks like this:</p>
<pre>
#include "esp-app.h"
static void hello() {
render("Hello World\n");
}
ESP_EXPORT int esp_controller_<b>NAME</b>(EspRoute *eroute, MprModule *module)
{
espDefineAction(eroute, "hello", hello);
return 0;
}
</pre>
<p>Where <em>NAME</em> is the name of the controller.</p>
<h3>Actions</h3>
<p>The job of the action is to respond to the request and generate the response via views for the
client. Here is the <em>create</em> action in the generated <em>post</em> controller.</p>
<pre>
static void create() {
if (writeRec(createRec("post", params()))) {
inform("New post created");
redirect("@");
} else {
renderView("post-edit");
}
}
</pre>
<p>The <em>create</em> action above creates a new post record based on the request parameters and then
uses the <em>views/post-edit.esp</em> view to render a response to the client. ESP <em>edit</em> scaffolds
handle the work of both <em>edit</em> and <em>create</em> views because they are so similar.</p>
<p>If an action does not call any of the <em>render</em> methods (as the <em>list</em> action does not),
ESP will automatically invoke a view of the same name. In the case of the <em>list</em> action, the
<em>views/post-list.esp</em> will be used to generate the response.</p>
<img src= "../../../images/esp/tour/create.png" alt="createPost" class="bare-screen" />
<p>Click OK to add the new blog post.</p>
<a id="homePage"></a>
<h2>Edit the Home Page</h2>
<p>You can edit the application's home page to add a link to your post listing page. Change
<em>static/index.esp</em> to contain just the following:</p>
<pre>
&lt;h2&gt;&lt;% label("Go to My Blog", "@post/"); %&gt;&lt;/h2&gt;
</pre>
<p>The <em>@post/</em> link means redirect to the <em>post</em> controller if the label is clicked.
Reload the page and you will see:</p>
<img src="../../../images/esp/tour/home-link.png" alt="homeLink" class="bare-screen" />
<h2>Code Errors</h2>
<p>What happens if you make a mistake entering the embedded "C" code in an ESP page. Say you forgot the
semicolon in the last example. You will see an error like this in your browser:</p>
<img src="../../../images/esp/tour/error.png" alt="homeLink" class="screen" />
<p>You can suppress these errors by setting <em>EspShowErrors</em> to <em>off</em> in the appweb.conf
configuration file.</p>
<a id="layout"></a>
<h2>Changing the Layout</h2>
<p>You may wish to add some tabs to navigate your blog app. ESP provides a layout
template engine that is used by all views to provide a common look and feel to your application. View pages
leverage templates so they don't need to repeat page elements that are common across the application. Edit
the layout template file <em>layouts/default.esp</em> and change it to use the <em>tabs</em>
view control.</p>
<pre>
&lt;div class="top"&gt;
&lt;h1&gt;Blog Application&lt;/h1&gt;
<b> &lt;% tabs(makeRec("{ \
'Blog Posts': '~/post', \
'New Post': '~/post/init', \
}"), 0); %&gt;
</b>&lt;/div&gt;
</pre>
<p>This will create two tab buttons to navigate your application.</p>
<p>After modifying the layout, you will need to re-compile the cached web pages. Normally, ESP
transparently compiles the web pages whenever you modify a web page. However, if you change a layout,
you may need to recompile the app. This is easy to do via the <em>esp</em> command.</p>
<pre>
esp compile
</pre>
<img src="../../../images/esp/tour/tabs.png" alt="tabs" class="bare-screen" />
<a id="validations">
</a>
<h2 class="section">Validations</h2>
<p>ESP provides flexible validation methods to help ensure the data you save is correct.</p>
<p>You can add calls to validate record data before it is saved to the database. To do this, edit the
<em>controllers/post.c</em> file and add calls to <em>ediAddValidation</em>.</p>
<pre>
ESP_EXPORT int esp_controller_post(EspRoute *eroute, MprModule *module)
{
<b>Edi *edi;</b>
/* Existing code */
<b>edi = getDatabase();
ediAddValidation(edi, "present", "post", "title", 0);
ediAddValidation(edi, "present", "post", "body", 0);
ediAddValidation(edi, "unique", "post", "title", 0);</b>
return 0;
}
</pre>
<p>This will cause the database to automatically validate that the <em>title</em> and <em>body</em> fields are
not blank and that the <em>title</em> is unique in the database.</p>
<p>If you click OK in the Post <em>edit</em> web page without entering any data
you will see the following:</p>
<img src="../../../images/esp/tour/validate.png" alt="validate" class="screen" />
<p>This automatically identified the input fields in error and generated a summary of the errors above the
form. Of course, this default error highlighting behavior can be overridden if desired by modifying the
application style sheets.<p>
<p>Other validation types include: checkNumber, checkBoolean, checkDate and checkFormat.
You can also define new validation types by calling
<a href="../../../api/esp.html#group___edi_service_1ga889df64bdd239f71c66b4fa920be8f46">ediDefineValidation</a>.
<a id="learn"></a>
<h2 class="section">Learn More ...</h2>
<p>That concludes the a quick tour through some of the capabilities of the ESP web framework.</p>
<p>To learn more, please read:
<ul>
<li><a href="index.html">ESP Documentation</a></li>
<li><a href="tour.html">ESP Web Framework Tour</a></li>
<li><a href="../../../ref/esp/espArchitecture.html">ESP Web Framework Architecture</a></li>
</ul>
<p>You may also like to ask questions at the <a href="http://www.ejscript.org/forum/">ESP Support Forum</a>.</p>
</div>
</div>
<!-- BeginDsi "dsi/bottom.html" -->
<div class="bottom">
<p class="footnote">
<a href="../../../product/copyright.html" >&copy; Embedthis Software LLC, 2003-2013.
All rights reserved. Embedthis, Appweb, ESP, Ejscript and Embedthis GoAhead are trademarks of Embedthis Software LLC.</a>
</p>
</div>
<script src="http://www.google.com/jsapi" type="text/javascript"></script>
<script type="text/javascript">
google.load('search', '1', {language : 'en'});
google.setOnLoadCallback(function() {
var customSearchControl = new google.search.CustomSearchControl(
'000262706376373952077:1hs0lhenihk');
customSearchControl.setResultSetSize(google.search.Search.FILTERED_CSE_RESULTSET);
var options = new google.search.DrawOptions();
options.enableSearchboxOnly("http://appwebserver.org/search.html");
customSearchControl.draw('cse-search-form', options);
}, true);
</script>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-179169-2']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
</body>
</html>