放程序代碼的html
這個概念我有點模糊
查了一下RESTful Web 服務 - 介紹
REST是一種軟件架構模式前端
HTPP方法 | 做用 |
---|---|
GET | 提供資源的只讀訪問 |
PUT | 建立一個資源 |
DELETE | 刪除一個資源 |
POST | 更新或刪除一個資源 |
OPTIONS | 獲取資源操做 |
在 REST 架構中,一個 REST 服務器只提供對資源的訪問,REST 客戶端訪問並呈現資源。
說白來就是服務器只負責增刪改查,提供資源給前端node
RESTful服務設計。 四個路由(都是HTTP方法)git
在考慮數據庫和界面以前, 先設計路由比較好
express文檔github
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')) })
// 好比說 設置了路線GET /user/:name // 那麼name屬性能夠做爲req.params.name // GET /user/tj req.params.name // => "tj"
因爲chrome瀏覽器發送delete
方法至關麻煩, 因而我用了一個插件PostMan
,能夠模擬各類HTTP請求。很方便web
在上例的代碼中, post方法用不了,由於處理post請求須要消息體解析
sql
由於post請求,是給服務器這邊發過來數據,發送數據可能有各類格式, 若是本身來寫的話會很麻煩,因此導入中間件幫咱們作這件事chrome
瞭解一下post提交數據的方式數據庫
默認表單提交就是這種方式express
通常是上傳文件
這種將數據以json格式提交,很贊=。=
沒用過,不作評價。
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
下。
在Node中添加數據庫,通常會涉及如下幾個步驟
ORM是啥? 百度了一下。
ORM的意思
ORM:(Object/Relation Mapping): 對象/關係映射
ORM就是將編程語言裏的對象和數據庫中的表創建關係
這裏選擇使用SQLite, 緣由是由於這個數據庫不須要安裝,是進程內數據庫,開箱即用。
文章應該能被增刪改查,模型類Article應該提供如下方法
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)
執行SQL語句, 不會檢索結果。 若是執行失敗了,就會調用回調函數。
執行SQL查詢, 將第一個結果回調, 注意回調有兩個參數, 第一個是error, 第二個纔是結果
執行SQL查詢, 將全部的結果回調
下了一款sqlite的工具,往表裏插了幾條數據,測試了一下都是正確的~~
將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')) })
增刪改查的功能的實現好了
文章確定咱們不想一個一個慢慢建立,咱們能夠用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 }
拿到html後, 咱們能夠經過模板引擎來渲染用戶界面。
結合Express和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 %>將不會進行轉義
通常咱們在用瀏覽器輸入地址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) } }) }) })
使用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)數據庫存儲數據