《Nodejs實戰》筆記三:Web程序是什麼?

1. 瞭解Web程序的結構

  • package.json 包
  • public/ 靜態資源文件夾,CSS和JS都放這裏
  • node_modules/ 依賴
  • 放程序代碼的html

    • app.js或者index.js 程序入口
    • models/ 數據庫模型
    • views/ 渲染頁面模板
    • controllers/或者routes/ HTTP請求處理器
    • middleware/ 中間件

2. 搭建一個RESTful web服務

這個概念我有點模糊
查了一下RESTful Web 服務 - 介紹
REST是一種軟件架構模式前端

HTPP方法 做用
GET 提供資源的只讀訪問
PUT 建立一個資源
DELETE 刪除一個資源
POST 更新或刪除一個資源
OPTIONS 獲取資源操做

在 REST 架構中,一個 REST 服務器只提供對資源的訪問,REST 客戶端訪問並呈現資源。
說白來就是服務器只負責增刪改查,提供資源給前端node

RESTful服務設計。 四個路由(都是HTTP方法)git

  • POST /articles 建立新文章
  • GET /articles/:id 獲取指定文章
  • GET /articles 獲取全部文章
  • DELETE /articles/:id 刪除指定文章

在考慮數據庫和界面以前, 先設計路由比較好
express文檔github

2.1 路由設計

const express = require('express')
const app = express()
const articles = [
    {
        title: 'example'
    }
]

app.set('port', 3000)

/**
 * 1. 獲取全部的文章
 */
app.get('/articles', (req, res, next) => {
    res.send(articles)
})

/**
 * 2. 建立一篇文章
 */
app.post('/articles', (req, res, next) => {
    res.send('OK')
})

/**
 * 3. 獲取指定的文章
 */
app.get('/articles/:id', (req, res, next) => {
    const id = req.params.id
    console.log('fetching:', id)
    res.send(articles[id])
})

/**
 * 4. 刪除指定的文章
 */
app.delete('/articles/:id', (req, res, next) => {
    const id = req.params.id
    console.log('fetching:', id)
    delete articles[id]
    res.send({ message: 'deleted' })
})

app.listen(app.get('port'), () => {
    console.log('App started on port', app.get('port'))
})
  1. express能自動的將數組轉爲json響應
  2. req.params是一個對象,能獲取路由的路徑
// 好比說 設置了路線GET /user/:name
// 那麼name屬性能夠做爲req.params.name

// GET /user/tj
req.params.name
// => "tj"

因爲chrome瀏覽器發送delete方法至關麻煩, 因而我用了一個插件PostMan,能夠模擬各類HTTP請求。很方便web

2.2 添加消息體(body)解析器

在上例的代碼中, post方法用不了,由於處理post請求須要消息體解析sql

由於post請求,是給服務器這邊發過來數據,發送數據可能有各類格式, 若是本身來寫的話會很麻煩,因此導入中間件幫咱們作這件事chrome

瞭解一下post提交數據的方式數據庫

  1. application/x-www-form-urlencoded(默認經常使用的)

默認表單提交就是這種方式express

  1. multipart/form-data

通常是上傳文件

  1. application/json

這種將數據以json格式提交,很贊=。=

  1. text/xml

沒用過,不作評價。

Express沒有內置,因而要下載中間件body-parser(受官方支持)

在上例的基礎上加代碼

const bodyParser = require('body-parser')

app.use(bodyParser.json()) // 1. 支持編碼爲JSON的請求消息體
app.use(bodyParser.urlencoded({ extended: true })) // 2. 支持編碼爲表單的請求消息體,也就是默認表單的形式

app.post('/articles', (req, res, next) => {
    const article = { 
        title: req.body.title,
        content: req.body.content
    }
    articles.push(article)
    res.send(articles)
})

這個中間件會幫咱們把post請求的數據處理好,掛載在req.body下。

2.3 添加數據庫

在Node中添加數據庫,通常會涉及如下幾個步驟

  1. 選擇想用的數據庫(好像說了廢話,2333)
  2. 在 npm 上看 哪些實現了 ORM 的熱門模塊
  3. 添加到項目中
  4. 建立模型,封裝數據庫訪問API
  5. 將模型添加到Express路由中

ORM是啥? 百度了一下。
ORM的意思
ORM:(Object/Relation Mapping): 對象/關係映射
ORM就是將編程語言裏的對象和數據庫中的創建關係

這裏選擇使用SQLite, 緣由是由於這個數據庫不須要安裝,是進程內數據庫,開箱即用。

2.3.1 製做本身的模型API

文章應該能被增刪改查,模型類Article應該提供如下方法

  • Article.all() 返回全部文章
  • Article.find(id) 返回指定ID的文章
  • Article.create(article) 建立文章
  • Article.delete(id) 刪除文章

Demo

const sqlite3 = require('sqlite3').verbose()
const dbname = 'later.sqlite'
const db = new sqlite3.Database(dbname)

db.serialize(() => {
    const sql = `
        CREATE TABLE IF NOT EXISTS articles (
            id integer primary key, 
            title, 
            content TEXT
        )
    `
    db.run(sql)
})

class Article {
    // cb是callback的縮寫
    static all(cb) {
        db.all('SELECT * FROM articles', cb)
    }

    static find(id, cb) {
        db.get('select * from articles where id = ?', id, cb)
    }

    static create(data, cb) {
        const sql = 'insert into articles(title, content) values(?, ?)'
        db.run(sql, data.title, data.content, cb)
    }

    static delete(id, cb) {
        if (!id) return cb(new Error('pleast provider an id'))
        db.run('delete from articles where id = ?', id, cb)
    }
}

module.exports = db
module.exports.Article = Article

關於sql語句, 有些忘了, 查資料SQL語法

還有sqlite的用法 sqliteAPI

db.run(sql, [param,...], cb)

對於db.run(sql, [param,...], cb)
執行SQL語句, 不會檢索結果。 若是執行失敗了,就會調用回調函數。

db.get(sql, [param,...], cb)

執行SQL查詢, 將第一個結果回調, 注意回調有兩個參數, 第一個是error, 第二個纔是結果

db.all(sql, [param,...], cb)

執行SQL查詢, 將全部的結果回調
下了一款sqlite的工具,往表裏插了幾條數據,測試了一下都是正確的~~

2.3.2 將數據庫導入路由中

將db.js導入以前寫的路由中

const express = require('express')
const app = express()
const Article = require('./db').Article
const bodyParser = require('body-parser')

app.set('port', 3000)

app.use(bodyParser.json()) // 1. 支持編碼爲JSON的請求消息體
app.use(bodyParser.urlencoded({ extended: true })) // 2. 支持編碼爲表單的請求消息體


/**
 * 獲取全部的文章
 */
app.get('/articles', (req, res, next) => {
    Article.all((err, articles) => {
        if (err) return next(err)
        res.send(articles)
    })
})

/**
 * 建立一篇文章
 */
app.post('/articles', (req, res, next) => {
    const article = { 
        title: req.body.title,
        content: req.body.content
    }
    Article.create(article, (err) => {
        return next(err)
    })
    res.send('create OK')
})

/**
 * 獲取指定的文章
 */
app.get('/articles/:id', (req, res, next) => {
    const id = req.params.id
    Article.find(id, (err, article) => {
        if(err) return next(err)
        res.send(article)
    })
})

/**
 * 刪除指定的文章
 */
app.delete('/articles/:id', (req, res, next) => {
    const id = req.params.id
    Article.delete(id, (err) => {
        return next(err)
    })
    res.send({ message: 'deleted' })
})

app.listen(app.get('port'), () => {
    console.log('App started on port', app.get('port'))
})

增刪改查的功能的實現好了

2.3.3 用爬蟲爬取文章存入數據庫中

文章確定咱們不想一個一個慢慢建立,咱們能夠用readability之類的模塊,自動幫咱們從網頁中提取文章

這裏書上用了readability

yarn add node-readability
const read = require('node-readability')

app.post('/download/articles', (req, res, next) => {
    const url = req.body.url
    read(url, (err, result) => {
        if (err || !result) {
            res.status(500).send('Errror downloading article')
        }
        const article = { 
            title: result.title,
            content: result.content
        }
        Article.create(article, (err) => {
            return next(err)
        })
        res.send('create OK')
    })
})

使用這個庫裏的read()方法, 傳入須要爬取的文章地址。

在回調函數中能獲取能該網頁

const article = { 
    title: result.title,
    content: result.content // 這個獲得的是內容的html
}

2.3.5 渲染模板

拿到html後, 咱們能夠經過模板引擎來渲染用戶界面。

結合Express和EJS

首先建立模板文件,

  1. 建立views/articles.ejs
<html>
    <head>
        <title>Later</title>
    </head>
    <body>
        <div class="container">
            <ul>
                <% articles.forEach((article) => { %>
                <li>
                    <a href="/articles/<%= article.id %>">
                        <%= article.title %>
                    </a>
                    <div>
                        <%- article.content %>
                    </div>
                </li>
                <% }) %>
            </ul>
        </div>
    </body>
</html>

注意點
<%= code %>會對code進行html轉義
<%- code %>將不會進行轉義

  1. 使用Express提供的 res.format 方法。

通常咱們在用瀏覽器輸入地址http://localhost:3000/articles
瀏覽器發出get請求, 通常請求類型都是text/html

Express提供的 res.format 方法,它能夠根據請求發送響應格式的響應。

因而寫代碼

app.get('/articles', (req, res, next) => {
    Article.all((err, articles) => {
        if (err) return next(err)
        res.format({
            html: () => {
                res.render('articles.ejs', { articles }) // 會自動去views/articles.ejs查找
            },
            json: () => {
                res.send(articles)
            }
        })
    })
})

3. 總結

使用express能夠很快的搭出應用,可是在其中咱們須要用挺多中間件的

像這個項目就用到了三個

"body-parser": "^1.18.3",
"ejs": "^2.6.1",
"node-readability": "^3.0.0",

body-parser 在客戶端post請求給服務器發送數據的時候, 這個中間件能夠替咱們解析各類請求方式的數據。簡化操做
ejs 是模板引擎,用於渲染
node-readability 是用來下載文章的,隨便給它一個網頁,它能解析出文章內容,有點相似爬蟲

這算是一個MVC的小項目了使用express做爲控制器(Controller)這一層, 負責轉發請求,處理請求使用ejs做爲視圖層(Views)渲染頁面使用sqlite做爲模型層(Model)數據庫存儲數據

相關文章
相關標籤/搜索