Posted on March 25th, 2012 under Express.jsNode.js
Tags: Express.jsgitGitHubnode.js

Looking for a good tutorial on Express.js to help you get quickly productive in it? You have come to the right place.

This tutorial is for Express 3, the current version is Express 4. Unless you want to use Express 3, consider the tutorial outdated.

In this tutorial I will run you through the process setting up an Express.js app and making it do what a basic website might do. You will learn the basics of routes, views, Jade templates, Stylus CSS engine, handling POST and GET requests.

Let's create an online store to sell Ninja items. We will call it the Ninja Store.

Installing Express

First of all install Express.js as a global module:

$ npm install express -g

If that fails:

$ sudo npm install express -g

Now, create a directory and create an Express app in it:

$ mkdir ninja-store
$ cd ninja-store
$ express --sessions --css stylus

If you like shortcuts, you could accomplish the same this way too:

$ express ninja-store --sessions --css stylus && cd ninja-store

We want to have support for sessions, hence the --sessions option. Using --css, we specified that we would be using the Stylus CSS engine. If you are a Less fan, you can specify --css less. Not specifying the --css option will default to the plain vanilla CSS we all are familiar with.

Then install the dependencies for the app:

$ npm install

That will install a bunch of modules used by the app. With that the base of the app is ready. It is already a working app. Let's see what it outputs. Start the app:

$ node app
Express server listening on port 3000

Then load http://localhost:3000/ in your browser. You will see a simple webpage with the title "Express", which looks something like this:

So looks like the app is working. Time to find out how it works.

Request flow in Express

This is how a request to an Express server flows:

Route → Route Handler → Template → HTML

The route defines the URL schema. It captures the matching request and passed on control to the corresponding route handler. The route handler processes the request and passes the control to a template. The template constructs the HTML for the response and sends it to the browser.

The route handler need not always pass the control to a template, it can optionally send the response to a request directly .

To understand how Express works, let's get working on the Ninja Store app.

Setting up the Routes

We will modify some of the stuff Express generated for us to make it more suited for our app, and more logical so that you can understand the inner workings of Express better.

Rename the index.jade file in the views folder to home.jade. This is actually a part of setting up views in Express.js, I will get there in the next section.

Delete the index.js and user.js from the routes folder. We don't need them and their presence could be potentially confusing. Create a new file called store.js in the routes folder, and put the following code in it:

exports.home = function(req, res){
res.render('home', { title: 'Ninja Store' })
};

Then we are going to modify app.js:

Delete these

  , routes = require('./routes')
, user = require('./routes/user')

Add the following right before var app = express();.

var store = require('./routes/store');

Delete these

app.get('/', routes.index);
app.get('/users', user.list);

and add this

app.get('/', store.home);

With that we have set up our own route for the home page.

Routes are URL schema for a website. In Express you define them using app.get()app.post(),app.delete() etc. The get, post, delete methods are derived from their corresponding HTTP verbs.

So far we created a single route for our app. Very soon we will be creating more, but before that I'll tell you about the files in the routes directory.

The routes directory is a convention, not a compulsion. In the routes directory we create appropriately named files which will handle the routes we define in the app.js file. We will import these files into our app and assign the functions defined in them to handle various routes.

The imported file becomes sort of like an instance of the class of the route handler file. In our case./routes/store.js is the class, store is instance, and store.home is a method of the instance.

Again as a recommended convention, we create an appropriately named variable for the imported file from the routes directory. Then we pass one of its functions as the second parameter for the route. For eg:

app.get('/', store.home);

From that you might probably guess, we can pass any function as the second parameter for the route. You are correct, even this would work:

app.get('/', function(req, res) {
res.render('home', { title: 'Ninja Store' });
});

We don't need to render HTML pages either:

app.get('/', function(req, res) {
res.send('Look ma, no HTML!');
});

Now run the app again and see what you get. Title has changed to "Ninja Store" and you see:

We are making progress. Time to spice up the home page 

Rendering Views

You have seen res.render() and res.send() in action already and probably have a fair idea about what they do. res.send() will directly send a string of text to the browser and res.render() will render a Jade template.

While it is possible to create a website entirely using res.send(), it is certainly not recommended to do so, because you will only end up with a complex and dirty looking codebase. Jade is the default templating engine for Express. Let's find out the basics of res.render() and the Jade templates.

Open the file named home.jade in the views directory. Let's examine its content:

extends layout

block content
h1= title
p Welcome to #{title}

extend layout means, this view file should look for another view file named layout.jade in the same directory and fill in the block placeholders defined in layout.jade.

Now, let's take a look at the contents of layout.jade.

doctype 5
html
head
title= title
link(rel='stylesheet', href='/stylesheets/style.css')
body
block content

It does indeed have a block named content, the content for which is defined in home.jade. Note, you can use anything for block names; "content" is just a convention.

What you are seeing is Jade code. The very basics of Jade is this:

i. HTML tag names to create tags, but without the angular brackets
ii. Spaces or tabs for indentation, but don't mix them
iii. # to assign an id to a tag
iv. . to assign a class to a tag, or create a div with a class if not already created
v. (property='value') to create attributes and assign values to a tag

Jade is out of scope of this tutorial, so you might want to read more about it here. However, as we proceed with the tutorial, I will explain you the relevant Jade code we come across.

The doctype 5 you see in layout.jade is doing a doctype declaration of HTML5. Also notice the relatively 'cryptic' title= title, the code is explained below.

title= title: The view expects a variable called title from the route handler. The variable will be set as the content of the title tag AFTER escaping special characters. To not-escape, use title!= title.

You can send data to views via the variables object in the res.render() method. Eg:

res.render('home', { title: 'Ninja Store' });

The title variable can be accessed using =title!=title or #{title} in the views.

  p Welcome to #{title}
p= title
p!= title

By default res.send() and res.render() send the HTTP status code of 200. You can specify your own HTTP status code.

Custom status code example for res.send():

res.send('File not Found', 404);

Custom status code example for res.render() (make sure you have created a file named 404.jadein the views directory):

res.status(404).render('404', { title: 'File not Found'});

Ok, now that we know the basic of views, let's revamp our homepage!

Put the following code in the home.jade file:

extends layout

block content
#wrapper
#logo
img(src='/images/logo.png')
#display
#login
form(method='post')
| Enter your name if you want to be a ninja
div
input(type='text', name='username')
input(type='submit', value='Log In') #footer
div Copyright © Ninja Store #{+new Date().getFullYear()}
a(href='/page?name=about') About
| |
a(href='/page?name=contact') Contact Us

Make sure you create a nice logo for the store, name it logo.png, and keep it in the/public/images/ directory.

Put the following code in the style.styl file in the /public/stylesheets/ directory:

body
padding: 0
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif a
color: #0069FF #wrapper
width: 450px
margin: 0 auto
padding: 40px 20px
background: #fff #logo
text-align: center #display
margin: 20px 0 50px #userbar
margin-bottom: 10px

Aren't we supposed to edit the style.css file? Well, style.styl is the Stylus file which generates the style.css file. Even though we code our CSS in the .styl file, we always include the .css file in the Jade template:

link(rel='stylesheet', href='/stylesheets/style.css')

So what is the point of using Stylus? Stylus offers a dynamic and efficient syntax for generating CSS. The details is out of scope of this tutorial, you can learn more about Stylus here.

Note: just like Jade templates, make sure you either uses spaces or tabs consistently for indenting your Stylus code.

Changes made to the views and everything in the public directory will be updated immediately when you refresh the browser. Refresh the browser and see what you get. In rare cases, the view may not be update - feel free to restart the server.

Wow! There you have, a log in form.

Try logging in.

Meh! Hit by an error:

Cannot POST /

Let me explain what's going on.

Remember the route

app.get('/', store.home);

we created?

That was for GET requests to the root of the website. Since our form uses the POST method, the route is not capturing it. We need to create a POST router for the home page to process the form.

Handling Forms - POST, GET Requests

Add this to app.js:

app.post('/', store.home_post_handler);

Now we need to define the home_post_handler() function in store.js module. Add the following tostore.js:

// handler for form submitted from homepage
exports.home_post_handler = function(req, res) {
    // if the username is not submitted, give it a default of "Anonymous"
    username = req.body.username || 'Anonymous';
    // store the username as a session variable
    req.session.username = username;
    // redirect the user to homepage
    res.redirect('/');
};

So handling POST data is pretty easy - just look for them in the req.body object.

Refresh the browser.

You still get Cannot POST /. That's because for changes you make in .js files to take effect, you need to restart the server. So restart the server and try logging in again.

This time the form submission works fine, but you still see the log in form. Did we log in or not? We did log in, but our route handler made it confusing. We'll change it now. Edit the exports.home()function:

// handler for homepage
exports.home = function(req, res) {
    // if user is not logged in, ask them to login
    if (typeof req.session.username == 'undefined') res.render('home', { title: 'Ninja Store'});
    // if user is logged in already, take them straight to the items list
    else res.redirect('/items');
};

Restart the server and log in again.

Cannot GET /items

An error again. But it looks more promising this time, the app is trying to load an undefined route. If we define the route, we would have it working.

Before we get to defining the missing route, let's optimize the views a little bit. Some components of the view will be common to all the views, so it is a good idea to modularize them. The Jade template engine has a command called include using which you can include files into a view.

Create a file called footer.jade in the views directory with the following content:

#footer
div Copyright © Ninja Store #{+new Date().getFullYear()}
a(href='/page?name=about') About
| |
a(href='/page?name=contact') Contact Us

Create a file called userbar.jade in the views directory with the following content:

#userbar
| Welcome
b #{username}
| |
a(href='/items') Items
| |
a(href='/logout') Log Out

Now edit the home.jade file:

extends layout

block content
#wrapper
#logo
a(href='/')
img(src='/images/logo.png')
#display
#login
form(method='post')
| Enter your name if you want to be a ninja
div
input(type='text', name='username')
input(type='submit', value='Log In') include footer

We will be defining the missing items/ and a related route in a while. In the process, we will also find out how to handle GET requests in Express.js.

There are two types of parametric GET requests in Express.js - req.params and req.query.

The req.params object contains request parameters right in the URL. It uses the clean URL scheme. Eg: http://example.com/product/1274.

The req.query object contains request parameters in the GET query. It uses the conventional GET request scheme. Eg: http://example.com?product=1274.

For displaying the items we will use the req.params method.

Add these routes to app.js:

// display the list of item
app.get('/items', store.items);
// show individual item
app.get('/item/:id', store.item);

/items for listing the items of the store.

/item/:name for displaying the individual items.

We will use a simple hard-coded array as the data source. Add the following to the store.js file.

// our 'database'
var items = {
    SKN:{name:'Shuriken', price:100},
    ASK:{name:'Ashiko', price:690},
    CGI:{name:'Chigiriki', price:250},
    NGT:{name:'Naginata', price:900},
    KTN:{name:'Katana', price:1000}
};

Now create the corresponding route handlers for the routes we added. Add the following tostore.js:

// handler for displaying the items
exports.items = function(req, res) {
    // don't let nameless people view the items, redirect them back to the homepage
    if (typeof req.session.username == 'undefined') res.redirect('/');
    else res.render('items', { title: 'Ninja Store - Items', username: req.session.username, items:items });
}; // handler for displaying individual items
exports.item = function(req, res) {
    // don't let nameless people view the items, redirect them back to the homepage
    if (typeof req.session.username == 'undefined') res.redirect('/');
    else {
        var name = items[req.params.id].name;
        var price = items[req.params.id].price;
        res.render('item', { title: 'Ninja Store - ' + name, username: req.session.username, name:name, price:price });
    }
};

Time to create the views for the routes.

View for listing all the items on the store. Name it items.jade.

extends layout

block content
#wrapper
#logo
img(src='/images/logo.png')
#display
include userbar -for (var id in items)
- var item = items[id]
div
a(href='/item/#{id}') #{item.name} - $#{item.price} include footer

View for listing individual items of the store. Name it item.jade.

extends layout

block content
#wrapper
#logo
img(src='/images/logo.png')
#display
include userbar p The #{name.toLowerCase()} is one of the must-have items for any aspiring ninja. It costs just $#{price} on our store.
p Buy it today! include footer

Our footer links uses the conventional GET style links, let's find out how to handle those kind of requests.

First create the routes for the footer links. Add the following to app.js.

// show general pages
app.get('/page', store.page);

Now, the route handler in store.js:

// handler for showing simple pages
exports.page = function(req, res) {
    var name = req.query.name;
    var contents = {
        about: 'Ninja Store sells the coolest ninja stuff in the world. Anyone shopping here is cool.',
        contact: 'You can contact us at <address><strong>Ninja Store</strong>,<br>1, World Ninja Headquarters,<br>Ninja Avenue,<br>NIN80B7-JP,<br>Nihongo.</address>'
    };
    res.render('page', { title: 'Ninja Store - ' + name, username: req.session.username, content:contents[name] });
};

And then the view for the pages. Create a new view file named page.jade in the views directory with the following content.

extends layout

block content
#wrapper
#logo
a(href='/')
img(src='/images/logo.png')
#display
p!= content include footer

Notice how we use p!= content instead of p #{content}. That is because we don't want the Jade engine to escape the HTML content of the variable.

we need to create a route and a handler for logging out. Add this route to app.js:

app.get('/logout', function(req, res) {
    // delete the session variable
    delete req.session.username;
    // redirect user to homepage
    res.redirect('/');
});

Notice how we specified the route handler right in the app.js file with the route definition. All the route definition cares about is that the second parameter should be a function, wherever it may be defined; just make sure to pass the request object (req) and the response object (res) to it.

With that our Ninja Store website ready. Start the app and get ready to witness the brilliance of the Ninja Store in action.

Conclusion

You know what? The whole project is on GitHub  I intentionally didn't tell you earlier, so that you don't get lazy and skip learning. Now that you done the hard work, feel free to clone the Ninja Store repository and go berserk on it.

Express.js is a lot more than what I covered in this tutorial. I have already, and will be covering the more advanced topics in dedicated articles on my website.

原地址:http://www.hacksparrow.com/express-js-tutorial.html

如何用nodejs创建一个webservice的更多相关文章

  1. nodejs创建一个简单的web服务

    这是一个突如其来的想法,毕竟做web服务的框架那么多,为什么要选择nodejs,因为玩前端时,偶尔想调用接口获取数据,而不想关注业务逻辑,只是想获取数据,使用java或者.net每次修改更新后还要打包 ...

  2. 如何用Unity创建一个的简单的HoloLens 3D程序

    注:本文提到的代码示例下载地址>How to create a Hello World 3D holographic app with Unity 之前我们有讲过一次如何在HoloLens中创建 ...

  3. nodejs创建一个HTTP服务器 简单入门级

    const http = require('http');//请求http.createServer(function(request, response){    /*createServer该函数 ...

  4. 如何用Maven创建一个普通Java项目

    一下内容包括:用Maven创建一个普通Java项目,并把该项目转成IDEA项目,导入到IDEA,最后把这个项目打包成一个jar文件. 有时候运行mvn命令失败,重复运行几次就OK了,无解(可能因为网络 ...

  5. 用NodeJS创建一个聊天服务器

    Node 是专注于创建网络应用的,网络应用就需要许多I/O(输入/输出)操作.让我们用Node实现有多么简单,并且还能轻松扩展. 创建一个TCP服务器 var net = require('net') ...

  6. 如何用java创建一个jdbc程序

    第一个jdbc程序 JDBC简介 Java数据库连接(Java Database Connectivity,JDBC),是一种用于执行SQL语句的Java API,它由一组用Java编程语言编写的类和 ...

  7. 如何用Eclipse创建一个JavaSwing的项目

    创建之前必须先给开发工具安装WindowBuilder插件(安装方法可自行百度) 方式一: 创建项目 new--other--WindowBuilder--SWT Designer----SWT/JF ...

  8. Maven(一)如何用Eclipse创建一个Maven项目

    1.什么是Maven Apache Maven 是一个项目管理和整合工具.基于工程对象模型(POM)的概念,通过一个中央信息管理模块,Maven 能够管理项目的构建.报告和文档. Maven工程结构和 ...

  9. 如何用Swift创建一个复杂的加载动画

    现在在苹果应用商店上有超过140万的App,想让你的app事件非常具有挑战的事情.你有这样一个机会,在你的应用的数据完全加载出来之前,你可以通过一个很小的窗口来捕获用户的关注. 没有比这个更好的地方让 ...

随机推荐

  1. 【java】详解native方法的使用

    目录结构: contents structure [+] 关于native关键字 使用native关键字 使用步骤 案例 编写.java文件 编译.java文件 获得.h文件 编写hello.cpp文 ...

  2. 基本数据类型对象包装(Integer等)

    基本数据类型 包装类 byte Byte short             Short int   Integer long Long boolean Boolean float          ...

  3. Vue初学者可能不知道的坑

    1.setTimeout/ setInterval 场景一 :this指向改变无法用this访问vue实例 mounted(){ setTimeout( function () { //setInte ...

  4. Boost filessystem...

    CMakeList.txt: cmake_minimum_required(VERSION 3.8) project(Demo) ) set(SOURCE_FILES main.cpp) //需要添加 ...

  5. poj 1064(二分答案)

    传送门:Problem 1064 https://www.cnblogs.com/violet-acmer/p/9793209.html 题意: 有N条绳子,长度分别为 length[1,2,3,.. ...

  6. (erase) Mispelling4 hdu1984

    Mispelling4 Time Limit: 1000/500 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total ...

  7. 个股与指数的回归分析(自带python ols 参数解读)

    sklearn实战-乳腺癌细胞数据挖掘(博客主亲自录制视频教程) https://study.163.com/course/introduction.htm?courseId=1005269003&a ...

  8. mysql -- 慢日志使用

    修改配置文件 show_query_log = OFF 是否开启慢日志查询 long_query_time = 2 时间限制,超过次时间,则记录 slow_query_log_file = /usr/ ...

  9. jq实现对checkbox的常用操作

    场景1:点击checkbox 其他checkbox被选中的都取消  (意思是只能选中一个checkbox) function fn(obj,event){ //obj就是这个this这个对象 ,eve ...

  10. hbase记录-修改压缩格式

    在业务空闲的时候修改压缩格式 先测试 ---测试表create 'test', { NAME => 'c',VERSIONS => 1}desc 'test'disable 'test'a ...