如何用nodejs创建一个webservice
Looking for a good tutorial on Express.js to help you get quickly productive in it? You have come to the right place.
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.jade
in 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的更多相关文章
- nodejs创建一个简单的web服务
这是一个突如其来的想法,毕竟做web服务的框架那么多,为什么要选择nodejs,因为玩前端时,偶尔想调用接口获取数据,而不想关注业务逻辑,只是想获取数据,使用java或者.net每次修改更新后还要打包 ...
- 如何用Unity创建一个的简单的HoloLens 3D程序
注:本文提到的代码示例下载地址>How to create a Hello World 3D holographic app with Unity 之前我们有讲过一次如何在HoloLens中创建 ...
- nodejs创建一个HTTP服务器 简单入门级
const http = require('http');//请求http.createServer(function(request, response){ /*createServer该函数 ...
- 如何用Maven创建一个普通Java项目
一下内容包括:用Maven创建一个普通Java项目,并把该项目转成IDEA项目,导入到IDEA,最后把这个项目打包成一个jar文件. 有时候运行mvn命令失败,重复运行几次就OK了,无解(可能因为网络 ...
- 用NodeJS创建一个聊天服务器
Node 是专注于创建网络应用的,网络应用就需要许多I/O(输入/输出)操作.让我们用Node实现有多么简单,并且还能轻松扩展. 创建一个TCP服务器 var net = require('net') ...
- 如何用java创建一个jdbc程序
第一个jdbc程序 JDBC简介 Java数据库连接(Java Database Connectivity,JDBC),是一种用于执行SQL语句的Java API,它由一组用Java编程语言编写的类和 ...
- 如何用Eclipse创建一个JavaSwing的项目
创建之前必须先给开发工具安装WindowBuilder插件(安装方法可自行百度) 方式一: 创建项目 new--other--WindowBuilder--SWT Designer----SWT/JF ...
- Maven(一)如何用Eclipse创建一个Maven项目
1.什么是Maven Apache Maven 是一个项目管理和整合工具.基于工程对象模型(POM)的概念,通过一个中央信息管理模块,Maven 能够管理项目的构建.报告和文档. Maven工程结构和 ...
- 如何用Swift创建一个复杂的加载动画
现在在苹果应用商店上有超过140万的App,想让你的app事件非常具有挑战的事情.你有这样一个机会,在你的应用的数据完全加载出来之前,你可以通过一个很小的窗口来捕获用户的关注. 没有比这个更好的地方让 ...
随机推荐
- 一个数学不好的菜鸡的快速沃尔什变换(FWT)学习笔记
一个数学不好的菜鸡的快速沃尔什变换(FWT)学习笔记 曾经某个下午我以为我会了FWT,结果现在一丁点也想不起来了--看来"学"完新东西不经常做题不写博客,就白学了 = = 我没啥智 ...
- django restframework Serializers
序列化器允许将诸如查询集和模型实例之类的复杂数据转换为原生 Python 数据类型,然后可以将它们轻松地呈现为 JSON,XML 或其他内容类型.序列化器还提供反序列化,在首次验证传入数据之后,可以将 ...
- 【CF1076D】Edge Deletion 最短路+贪心
题目大意:给定 N 个点 M 条边的无向简单联通图,留下最多 K 条边,求剩下的点里面从 1 号顶点到其余各点最短路大小等于原先最短路大小的点最多怎么构造. 题解:我们可以在第一次跑 dij 时直接采 ...
- typescript接口(学习笔记非干货)
typescript的核心原则之一就是对所具有的shape类型检查结构性子类型化 One of the core principles of typescript is to check struct ...
- 弹指之间 -- Polychord
CHAPTER 19 复合和弦 Polychord 示例歌曲:爱很简单,恰是你的温柔
- 51Nod1376 (dp + BIT // cdq分治)
https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1376 求LIS的数量. 乍一看觉得还是dp,仔细一看确实可以用dp做. ...
- ev的offsetX,pageX,clientX和screenX
event.offsetX.event.offsetY(相对事件发生的具体元素左上角的定位) 鼠标相对于事件源元素(srcElement)的X,Y坐标,只有IE事件有这2个属性,标准事件没有对应的属性 ...
- 远程客户端连接MysqL数据库太慢解决方案
远程客户端连接MysqL数据库太慢解决方案局域网客户端访问mysql 连接慢问题解决. cd /etc/mysql vi my.conf [mysqld] skip-name-resolve 此选项禁 ...
- nginx: [warn] duplicate MIME type "text/html"错误
检查配置文件时提示:nginx: [warn] duplicate MIME type "text/html" in /home/web/nginx/inc/gzip.conf:9 ...
- 多目标遗传算法 ------ NSGA-II (部分源码解析) 实数、二进制编码的变异操作 mutation.c
遗传算法的变异操作 /* Mutation routines */ # include <stdio.h> # include <stdlib.h> # include < ...