<p>Anda telah berjaya mencipta sistem pengurusan kandungan sistem fail rata (CMS) menggunakan Go. Langkah seterusnya ialah mengambil idea yang sama dan membuat pelayan web menggunakan Node.js. Saya akan menunjukkan kepada anda cara memuatkan perpustakaan, mencipta pelayan dan menjalankan pelayan. </p>
<p>CMS ini akan menggunakan struktur data tapak yang diperkenalkan dalam tutorial pertama "Membina CMS: Struktur dan Gaya". Jadi, muat turun struktur asas ini dan pasangkannya dalam direktori baharu. </p>
<h2>Dapatkan perpustakaan nod dan nod</h2>
<p>Cara paling mudah untuk memasang Node.js pada Mac ialah menggunakan Homebrew. Jika anda belum memasang Homebrew, tutorial Homebrew Revealed: The Ultimate Package Manager untuk OS X akan menunjukkan kepada anda cara memasangnya. </p>
<p>Untuk memasang Node.js menggunakan Homebrew, masukkan arahan berikut dalam terminal: </p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:shell;toolbal:false;">brew install node
</pre><div class="contentsignin">Salin selepas log masuk</div></div>
<p>Apabila selesai, arahan Node dan npm akan dipasang sepenuhnya pada Mac anda. Untuk semua platform lain, ikut arahan pada tapak web Node.js. </p>
<p>Sila ambil perhatian: Ramai pengurus pakej sedang memasang Node.js versi 0.10. Tutorial ini menganggap anda mempunyai versi 5.3 atau lebih tinggi. Anda boleh menyemak versi anda dengan menaip: </p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:shell;toolbal:false;">node --version
</pre><div class="contentsignin">Salin selepas log masuk</div></div>
Perintah <p><code class="inline">nod</code> menjalankan penterjemah JavaScript. Perintah <code class="inline">npm</code> ialah pengurus pakej untuk Node.js dan digunakan untuk memasang perpustakaan baharu, mencipta projek baharu dan menjalankan skrip projek. Envato Tuts+ mempunyai banyak tutorial dan kursus yang hebat tentang Node.js dan NPM. <code class="inline">node</code> 命令运行 JavaScript 解释器。 <code class="inline">npm</code> 命令是 Node.js 的包管理器,用于安装新库、创建新项目以及运行项目脚本。 Envato Tuts+ 上有许多关于 Node.js 和 NPM 的精彩教程和课程。</p>
<p>要安装 Web 服务器的库,您必须在 Terminal.app 或 iTerm.app 程序中运行以下命令:</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:shell;toolbal:false;">npm install express --save
npm install handlebars --save
npm install moment --save
npm install marked --save
npm install jade --save
npm install morgan --save
</pre><div class="contentsignin">Salin selepas log masuk</div></div>
<p>Express 是一个 Web 应用程序开发平台。它类似于Go中的goWeb库。 Handlebars 是用于创建页面的模板引擎。 Moment 是一个用于处理日期的库。 Marked 是一个很棒的 JavaScript 中 Markdown 到 HTML 转换器。 Jade 是一种 HTML 速记语言,可轻松创建 HTML。 Morgan 是 Express 的中间件库,可生成 Apache 标准日志文件。</p>
<p>安装库的另一种方法是下载本教程的源文件。下载并解压后,在主目录中输入:</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:shell;toolbal:false;">npm --install
</pre><div class="contentsignin">Salin selepas log masuk</div></div>
<p>这将安装创建该项目所需的一切。</p>
<h2>nodePress.js</h2>
<p>现在您可以开始创建服务器了。在项目的顶层目录中,创建一个名为 nodePress.js 的文件,在您选择的编辑器中打开它,然后开始添加以下代码。我将解释放入文件中的代码。</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">//
// Load the libraries used.
//
var fs = require('fs');
var path = require("path");
var child_process = require('child_process');
var process = require('process');
var express = require('express'); // http://expressjs.com/en/
var morgan = require('morgan'); // https://github.com/expressjs/morgan
var Handlebars = require("handlebars"); // http://handlebarsjs.com/
var moment = require("moment"); // http://momentjs.com/
var marked = require('marked'); // https://github.com/chjj/marked
var jade = require('jade'); // http://jade-lang.com/
</pre><div class="contentsignin">Salin selepas log masuk</div></div>
<p>服务器代码从初始化用于创建服务器的所有库开始。没有带有网址的注释的库是内部 Node.js 库。</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">//
// Setup Global Variables.
//
var parts = JSON.parse(fs.readFileSync('./server.json', 'utf8'));
var styleDir = process.cwd() + '/themes/styling/' + parts['CurrentStyling'];
var layoutDir = process.cwd() + '/themes/layouts/' + parts['CurrentLayout'];
var siteCSS = null;
var siteScripts = null;
var mainPage = null;
</pre><div class="contentsignin">Salin selepas log masuk</div></div>
<p>接下来,我设置所有全局变量和库配置。使用全局变量并不是最好的软件设计实践,但它确实有效并且有助于快速开发。</p>
<p><code class="inline">parts</code> 变量是一个包含网页所有部分的哈希数组。每个页面都引用该变量的内容。它从服务器目录顶部的 server.json 文件的内容开始。</p>
<p>然后,我使用 server.json 文件中的信息创建用于此站点的 <code class="inline">styles</code> 和 <code class="inline">layouts</code> 目录的完整路径。</p>
<p>然后将三个变量设置为空值:<code class="inline">siteCSS</code>、<code class="inline">siteScripts</code> 和 <code class="inline">mainPage</code>。这些全局变量将包含所有 CSS、JavaScript 和主索引页内容。这三个项目是任何 Web 服务器上请求最多的项目。因此,将它们保留在内存中可以节省时间。如果 server.json 文件中的 <code class="inline">Cache</code> 变量为 false,则每个请求都会重新读取这些项目。</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">marked.setOptions({
renderer: new marked.Renderer(),
gfm: true,
tables: true,
breaks: false,
pedantic: false,
sanitize: false,
smartLists: true,
smartypants: false
});
</pre><div class="contentsignin">Salin selepas log masuk</div></div>
<p>此代码块用于配置 Marked 库以从 Markdown 生成 HTML。大多数情况下,我会打开表格和 smartLists 支持。</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">parts["layout"] = fs.readFileSync(layoutDir + '/template.html', 'utf8');
parts["404"] = fs.readFileSync(styleDir + '/404.html', 'utf8');
parts["footer"] = fs.readFileSync(styleDir + '/footer.html', 'utf8');
parts["header"] = fs.readFileSync(styleDir + '/header.html', 'utf8');
parts["sidebar"] = fs.readFileSync(styleDir + '/sidebar.html', 'utf8');
//
// Read in the page parts.
//
var partFiles = fs.readdirSync(parts['Sitebase'] + "parts/");
partFiles.forEach(function(ele, index, array) {
parts[path.basename(ele, path.extname(ele))] = figurePage(parts['Sitebase'] + "parts/" + path.basename(ele, path.extname(ele)));
});
</pre><div class="contentsignin">Salin selepas log masuk</div></div>
<p><code class="inline">parts</code> 变量进一步加载 <code class="inline">styles</code> 和 <code class="inline">layout</code> 目录中的部分。 <code class="inline">site</code> 目录内的 <code class="inline">parts</code> 目录中的每个文件也被加载到 <code class="inline">parts</code> 全局变量中。不带扩展名的文件名是用于存储文件内容的名称。这些名称在 Handlebars 宏中得到扩展。</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">//
// Setup Handlebar's Helpers.
//
//
// HandleBars Helper: save
//
// Description: This helper expects a
// "<name>" "<value>" where the name
// is saved with the value for future
// expansions. It also returns the
// value directly.
//
Handlebars.registerHelper("save", function(name, text) {
//
// Local Variables.
//
var newName = "", newText = "";
//
// See if the name and text is in the first argument
// with a |. If so, extract them properly. Otherwise,
// use the name and text arguments as given.
//
if(name.indexOf("|") > 0) {
var parts = name.split("|");
newName = parts[0];
newText = parts[1];
} else {
newName = name;
newText = text;
}
//
// Register the new helper.
//
Handlebars.registerHelper(newName, function() {
return newText;
});
//
// Return the text.
//
return newText;
});
//
// HandleBars Helper: date
//
// Description: This helper returns the date
// based on the format given.
//
Handlebars.registerHelper("date", function(dFormat) {
return moment().format(dFormat);
});
//
// HandleBars Helper: cdate
//
// Description: This helper returns the date given
// in to a format based on the format
// given.
//
Handlebars.registerHelper("cdate", function(cTime, dFormat) {
return moment(cTime).format(dFormat);
});
</pre><div class="contentsignin">Salin selepas log masuk</div></div>
<p>下一段代码定义了我定义的在 Web 服务器中使用的 Handlebars 帮助程序:<code class="inline">save</code>、<code class="inline">date</code> 和 <code class="inline">cdate</code>
</p>Untuk memasang perpustakaan pelayan web, anda mesti menjalankan arahan berikut dalam program Terminal.app atau iTerm.app: <p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:plaintext;toolbal:false;">{{save "name|Richard Guay"}}
{{save "newName" "Richard Guay"}}
Name is: {{name}}
newName is: {{newName}}
</pre><div class="contentsignin">Salin selepas log masuk</div></div>
</p>Express ialah platform pembangunan aplikasi web. Ia serupa dengan perpustakaan goWeb dalam Go. Bar hendal ialah enjin templat untuk membuat halaman. Moment ialah perpustakaan untuk bekerja dengan tarikh. Marked ialah penukar Markdown kepada HTML yang hebat dalam JavaScript. Jade ialah bahasa singkatan HTML yang memudahkan untuk membuat HTML. Morgan ialah perpustakaan middleware untuk Express yang menjana fail log standard Apache. 🎜
🎜Cara lain untuk memasang perpustakaan ialah memuat turun fail sumber untuk tutorial ini. Selepas memuat turun dan membuka zip, masukkan dalam direktori utama: 🎜
<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">//
// Create and configure the server.
//
var nodePress = express();
//
// Configure middleware.
//
nodePress.use(morgan('combined'))
</pre><div class="contentsignin">Salin selepas log masuk</div></div><div class="contentsignin">Salin selepas log masuk</div></div>
🎜Ini akan memasang semua yang diperlukan untuk mencipta projek ini. 🎜
🎜nodePress.js🎜
🎜Kini anda boleh mula membuat pelayan. Dalam direktori peringkat atas projek anda, buat fail yang dipanggil nodePress.js, bukanya dalam editor pilihan anda dan mula menambah kod berikut. Saya akan menerangkan kod yang saya masukkan ke dalam fail. 🎜
<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">//
// Define the routes.
//
nodePress.get('/', function(request, response) {
setBasicHeader(response);
if((parts["Cache"] == true) && (mainPage != null)) {
response.send(mainPage);
} else {
mainPage = page("main");
response.send(mainPage);
}
});
nodePress.get('/favicon.ico', function(request, response) {
var options = {
root: parts['Sitebase'] + 'images/',
dotfiles: 'deny',
headers: {
'x-timestamp': Date.now(),
'x-sent': true
}
};
response.set("Content-Type", "image/ico");
setBasicHeader(response);
response.sendFile('favicon.ico', options, function(err) {
if (err) {
console.log(err);
response.status(err.status).end();
} else {
console.log('Favicon was sent:', 'favicon.ico');
}
});
});
nodePress.get('/stylesheets.css', function(request, response) {
response.set("Content-Type", "text/css");
setBasicHeader(response);
response.type("css");
if((parts["Cache"] == true) && (siteCSS != null)) {
response.send(siteCSS);
} else {
siteCSS = fs.readFileSync(parts['Sitebase'] + 'css/final/final.css');
response.send(siteCSS);
}
});
nodePress.get('/scripts.js', function(request, response) {
response.set("Content-Type", "text/javascript");
setBasicHeader(response);
if((parts["Cache"] == true) && (siteScripts != null)) {
response.send(siteScripts);
} else {
siteScripts = fs.readFileSync(parts['Sitebase'] + 'js/final/final.js', 'utf8');
response.send(siteScripts);
}
});
nodePress.get('/images/:image', function(request, response) {
var options = {
root: parts['Sitebase'] + 'images/',
dotfiles: 'deny',
headers: {
'x-timestamp': Date.now(),
'x-sent': true
}
};
response.set("Content-Type", "image/" + path.extname(request.params.image).substr(1));
setBasicHeader(response);
response.sendFile(request.params.image, options, function(err) {
if (err) {
console.log(err);
response.status(err.status).end();
} else {
console.log('Image was sent:', request.params.image);
}
});
});
nodePress.get('/posts/blogs/:blog', function(request, response) {
setBasicHeader(response);
response.send(post("blogs", request.params.blog, "index"));
});
nodePress.get('/posts/blogs/:blog/:post', function(request, response) {
setBasicHeader(response);
response.send(post("blogs", request.params.blog, request.params.post));
});
nodePress.get('/posts/news/:news', function(request, response) {
setBasicHeader(response);
response.send(post("news", request.params.news, "index"));
});
nodePress.get('/posts/news/:news/:post', function(request, response) {
setBasicHeader(response);
response.send(post("news", request.params.news, request.params.post));
});
nodePress.get('/:page', function(request, response) {
setBasicHeader(response);
response.send(page(request.params.page));
});
</pre><div class="contentsignin">Salin selepas log masuk</div></div><div class="contentsignin">Salin selepas log masuk</div></div>
🎜Kod pelayan bermula dengan memulakan semua perpustakaan yang digunakan untuk mencipta pelayan. Perpustakaan tanpa anotasi dengan URL ialah perpustakaan Node.js dalaman. 🎜
<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">//
// Start the server.
//
var addressItems = parts['ServerAddress'].split(':');
var server = nodePress.listen(addressItems[2], function() {
var host = server.address().address;
var port = server.address().port;
console.log('nodePress is listening at http://%s:%s', host, port);
});
</pre><div class="contentsignin">Salin selepas log masuk</div></div><div class="contentsignin">Salin selepas log masuk</div></div>
🎜Seterusnya, saya menyediakan semua pembolehubah global dan konfigurasi perpustakaan. Menggunakan pembolehubah global bukanlah amalan reka bentuk perisian terbaik, tetapi ia berfungsi dan membantu dalam pembangunan pesat. 🎜
Pembolehubah 🎜<code class="inline">parts</code> ialah tatasusunan cincang yang mengandungi semua bahagian halaman web. Setiap halaman merujuk kandungan pembolehubah ini. Ia bermula dengan kandungan fail server.json di bahagian atas direktori pelayan. 🎜
🎜Saya kemudian menggunakan maklumat dalam fail server.json untuk mencipta laluan penuh ke direktori <code class="inline">styles</code> dan <code class="inline">layouts</code> untuk tapak ini . 🎜
🎜Kemudian tetapkan tiga pembolehubah kepada nilai kosong: <code class="inline">siteCSS</code>, <code class="inline">siteScripts</code> dan <code class="inline">mainPage</code> kod>. Pembolehubah global ini akan mengandungi semua kandungan CSS, JavaScript dan halaman indeks utama. Ketiga-tiga projek ini adalah yang paling banyak diminta pada mana-mana pelayan web. Oleh itu, menyimpannya dalam ingatan menjimatkan masa. Jika pembolehubah <code class="inline">Cache</code> dalam fail server.json adalah palsu, item ini akan dibaca semula pada setiap permintaan. 🎜
<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">//
// Function: setBasicHeader
//
// Description: This function will set the basic header information
// needed.
//
// Inputs:
// response The response object
//
function setBasicHeader(response) {
response.append("Cache-Control", "max-age=2592000, cache");
response.append("Server", "nodePress - a CMS written in node from Custom Computer Tools: http://customct.com.");
}
</pre><div class="contentsignin">Salin selepas log masuk</div></div><div class="contentsignin">Salin selepas log masuk</div></div>
🎜Blok kod ini digunakan untuk mengkonfigurasi perpustakaan Bertanda untuk menjana HTML daripada Markdown. Selalunya saya menghidupkan jadual dan sokongan senarai pintar. 🎜
<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">//
// Function: page
//
// Description: This function processes a page request
//
// Inputs:
// page The requested page
//
function page(page) {
//
// Process the given page using the standard layout.
//
return (processPage(parts["layout"], parts['Sitebase'] + "pages/" + page));
}
</pre><div class="contentsignin">Salin selepas log masuk</div></div><div class="contentsignin">Salin selepas log masuk</div></div>
🎜Pembolehubah <code class="inline">parts</code> memuatkan lagi bahagian daripada direktori <code class="inline">styles</code> dan <code class="inline">layout</code>. Setiap fail dalam direktori <code class="inline">parts</code> dalam direktori <code class="inline">site</code> juga dimuatkan ke dalam <code class="inline">parts</code > kod> dalam pembolehubah global. Nama fail tanpa sambungan ialah nama yang digunakan untuk menyimpan kandungan fail. Nama-nama ini dikembangkan dalam makro Bar Hendal. 🎜
<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">//
// Function: post
//
// Description: This function processes a post request
//
// Inputs:
// type The type of post.
// cat The category of the post.
// post The requested post
//
function post(type, cat, post) {
//
// Process the post given the type and the post name.
//
return (processPage(parts["layout"], parts['Sitebase'] + "posts/" + type + "/" + cat + "/" + post));
}
</pre><div class="contentsignin">Salin selepas log masuk</div></div><div class="contentsignin">Salin selepas log masuk</div></div>
🎜Sekeping kod seterusnya mentakrifkan pembantu Bar Hendal yang saya takrifkan untuk digunakan dalam pelayan web: <code class="inline">save</code>, <code class="inline">tarikh</code> dan <code class ="inline">cdate</code>. Pembantu simpan membenarkan membuat pembolehubah dalam halaman. Versi ini menyokong versi goPress di mana nama dan nilai parameter dipisahkan bersama dengan "|". Anda juga boleh menentukan penjimatan menggunakan dua parameter. Contohnya: 🎜
<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">//
// Function: processPage
//
// Description: This function processes a page for the CMS.
//
// Inputs:
// layout The layout to use for the page.
// page Path to the page to render.
//
function processPage(layout, page) {
//
// Get the pages contents and add to the layout.
//
var context = {};
context = MergeRecursive(context, parts);
context['content'] = figurePage(page);
context['PageName'] = path.basename(page, path.extname(page));
//
// Load page data.
//
if(fileExists(page + ".json")) {
//
// Load the page's data file and add it to the data structure.
//
context = MergeRecursive(context, JSON.parse(fs.readFileSync(page + '.json', 'utf8')));
}
//
// Process Handlebars codes.
//
var template = Handlebars.compile(layout);
var html = template(context);
//
// Process all shortcodes.
//
html = processShortCodes(html);
//
// Run through Handlebars again.
//
template = Handlebars.compile(html);
html = template(context);
//
// Return results.
//
return (html);
}
</pre><div class="contentsignin">Salin selepas log masuk</div></div><div class="contentsignin">Salin selepas log masuk</div></div>
🎜Ini akan menghasilkan hasil yang sama. Saya lebih suka pendekatan kedua, tetapi perpustakaan Handlebars dalam Go tidak membenarkan berbilang hujah. 🎜
<p><code class="inline">date</code> 和 <code class="inline">cdate</code> 帮助程序格式化当前日期 (<code class="inline">date</code>) 或给定日期 (<code class="inline">cdate</code>)根据 <strong>moment.js</strong> 库格式化规则。 <code class="inline">cdate</code> 帮助程序期望渲染的日期是第一个参数并且具有 ISO 8601 格式。</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">//
// Create and configure the server.
//
var nodePress = express();
//
// Configure middleware.
//
nodePress.use(morgan('combined'))
</pre><div class="contentsignin">Salin selepas log masuk</div></div><div class="contentsignin">Salin selepas log masuk</div></div>
<p>现在,代码创建一个 Express 实例来配置实际的服务器引擎。 <code>nodePress.use()</code> 函数设置中间件软件。中间件是在每次调用服务器时提供服务的任何代码。在这里,我设置了 Morgan.js 库来创建正确的服务器日志输出。</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">//
// Define the routes.
//
nodePress.get('/', function(request, response) {
setBasicHeader(response);
if((parts["Cache"] == true) && (mainPage != null)) {
response.send(mainPage);
} else {
mainPage = page("main");
response.send(mainPage);
}
});
nodePress.get('/favicon.ico', function(request, response) {
var options = {
root: parts['Sitebase'] + 'images/',
dotfiles: 'deny',
headers: {
'x-timestamp': Date.now(),
'x-sent': true
}
};
response.set("Content-Type", "image/ico");
setBasicHeader(response);
response.sendFile('favicon.ico', options, function(err) {
if (err) {
console.log(err);
response.status(err.status).end();
} else {
console.log('Favicon was sent:', 'favicon.ico');
}
});
});
nodePress.get('/stylesheets.css', function(request, response) {
response.set("Content-Type", "text/css");
setBasicHeader(response);
response.type("css");
if((parts["Cache"] == true) && (siteCSS != null)) {
response.send(siteCSS);
} else {
siteCSS = fs.readFileSync(parts['Sitebase'] + 'css/final/final.css');
response.send(siteCSS);
}
});
nodePress.get('/scripts.js', function(request, response) {
response.set("Content-Type", "text/javascript");
setBasicHeader(response);
if((parts["Cache"] == true) && (siteScripts != null)) {
response.send(siteScripts);
} else {
siteScripts = fs.readFileSync(parts['Sitebase'] + 'js/final/final.js', 'utf8');
response.send(siteScripts);
}
});
nodePress.get('/images/:image', function(request, response) {
var options = {
root: parts['Sitebase'] + 'images/',
dotfiles: 'deny',
headers: {
'x-timestamp': Date.now(),
'x-sent': true
}
};
response.set("Content-Type", "image/" + path.extname(request.params.image).substr(1));
setBasicHeader(response);
response.sendFile(request.params.image, options, function(err) {
if (err) {
console.log(err);
response.status(err.status).end();
} else {
console.log('Image was sent:', request.params.image);
}
});
});
nodePress.get('/posts/blogs/:blog', function(request, response) {
setBasicHeader(response);
response.send(post("blogs", request.params.blog, "index"));
});
nodePress.get('/posts/blogs/:blog/:post', function(request, response) {
setBasicHeader(response);
response.send(post("blogs", request.params.blog, request.params.post));
});
nodePress.get('/posts/news/:news', function(request, response) {
setBasicHeader(response);
response.send(post("news", request.params.news, "index"));
});
nodePress.get('/posts/news/:news/:post', function(request, response) {
setBasicHeader(response);
response.send(post("news", request.params.news, request.params.post));
});
nodePress.get('/:page', function(request, response) {
setBasicHeader(response);
response.send(page(request.params.page));
});
</pre><div class="contentsignin">Salin selepas log masuk</div></div><div class="contentsignin">Salin selepas log masuk</div></div>
<p>这部分代码定义了实现 Web 服务器所需的所有路由。所有路由都运行 <code>setBasicHeader()</code> 函数来设置正确的标头值。所有针对页面类型的请求都会调用 <code>page()</code> 函数,而所有针对 post 类型页面的请求都会调用 <code>posts()</code> 函数。</p>
<p><code class="inline">Content-Type</code> 的默认值为 HTML。因此,对于 CSS、JavaScript 和图像,<code class="inline">Content-Type</code> 显式设置为其适当的值。</p>
<p>您还可以使用 <code>put</code>、<code>delete</code> 和 <code>post</code> REST 动词定义路由。这个简单的服务器仅使用 <code>get</code> 动词。</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">//
// Start the server.
//
var addressItems = parts['ServerAddress'].split(':');
var server = nodePress.listen(addressItems[2], function() {
var host = server.address().address;
var port = server.address().port;
console.log('nodePress is listening at http://%s:%s', host, port);
});
</pre><div class="contentsignin">Salin selepas log masuk</div></div><div class="contentsignin">Salin selepas log masuk</div></div>
<p>在定义所使用的不同函数之前要做的最后一件事是启动服务器。 server.json 文件包含 DNS 名称(此处为 <code>localhost</code>)和服务器的端口。解析后,服务器的 <code>listen()</code> 函数使用端口号来启动服务器。服务器端口打开后,脚本会记录服务器的地址和端口。</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">//
// Function: setBasicHeader
//
// Description: This function will set the basic header information
// needed.
//
// Inputs:
// response The response object
//
function setBasicHeader(response) {
response.append("Cache-Control", "max-age=2592000, cache");
response.append("Server", "nodePress - a CMS written in node from Custom Computer Tools: http://customct.com.");
}
</pre><div class="contentsignin">Salin selepas log masuk</div></div><div class="contentsignin">Salin selepas log masuk</div></div>
<p>定义的第一个函数是 <code>setBasicHeader()</code> 函数。该函数设置响应头,告诉浏览器将页面缓存一个月。它还告诉浏览器该服务器是nodePress服务器。如果您需要任何其他标准标头值,您可以使用 <code>response.append()</code> 函数在此处添加它们。</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">//
// Function: page
//
// Description: This function processes a page request
//
// Inputs:
// page The requested page
//
function page(page) {
//
// Process the given page using the standard layout.
//
return (processPage(parts["layout"], parts['Sitebase'] + "pages/" + page));
}
</pre><div class="contentsignin">Salin selepas log masuk</div></div><div class="contentsignin">Salin selepas log masuk</div></div>
<p><code>page()</code> 函数将页面的布局模板以及页面在服务器上的位置发送到 <code>processPage()</code> 函数。</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">//
// Function: post
//
// Description: This function processes a post request
//
// Inputs:
// type The type of post.
// cat The category of the post.
// post The requested post
//
function post(type, cat, post) {
//
// Process the post given the type and the post name.
//
return (processPage(parts["layout"], parts['Sitebase'] + "posts/" + type + "/" + cat + "/" + post));
}
</pre><div class="contentsignin">Salin selepas log masuk</div></div><div class="contentsignin">Salin selepas log masuk</div></div>
<p><code>post()</code> 函数就像 <code>page()</code> 函数,不同之处在于帖子有更多项目来定义每个帖子。在这个系列的服务器中,一个post包含一个<code>type</code>、category,以及实际的<code>post</code>。类型为 <code>blogs</code> 或 <code>news</code>。类别是 <code>flatcms</code>。由于这些代表目录名称,因此您可以将它们设为您想要的任何名称。只需将命名与文件系统中的名称相匹配即可。</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">//
// Function: processPage
//
// Description: This function processes a page for the CMS.
//
// Inputs:
// layout The layout to use for the page.
// page Path to the page to render.
//
function processPage(layout, page) {
//
// Get the pages contents and add to the layout.
//
var context = {};
context = MergeRecursive(context, parts);
context['content'] = figurePage(page);
context['PageName'] = path.basename(page, path.extname(page));
//
// Load page data.
//
if(fileExists(page + ".json")) {
//
// Load the page's data file and add it to the data structure.
//
context = MergeRecursive(context, JSON.parse(fs.readFileSync(page + '.json', 'utf8')));
}
//
// Process Handlebars codes.
//
var template = Handlebars.compile(layout);
var html = template(context);
//
// Process all shortcodes.
//
html = processShortCodes(html);
//
// Run through Handlebars again.
//
template = Handlebars.compile(html);
html = template(context);
//
// Return results.
//
return (html);
}
</pre><div class="contentsignin">Salin selepas log masuk</div></div><div class="contentsignin">Salin selepas log masuk</div></div>
<p><code>processPage()</code> 函数获取要呈现的页面内容的布局和路径。该函数首先创建 <code>parts</code> 全局变量的本地副本,并添加“contents”主题标签以及调用 <code>figurePage()</code> 函数的结果。然后,它将 <code>PageName</code> 哈希值设置为页面名称。</p>
<p>然后,该函数使用 Handlebars 将页面内容编译到布局模板。之后, <code>processShortCodes()</code> 函数将展开页面上定义的所有短代码。然后,Handlebars 模板引擎再次检查代码。然后浏览器接收结果。</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">//
// Function: processShortCodes
//
// Description: This function takes a string and
// processes all of the shortcodes in
// the string.
//
// Inputs:
// content String to process
//
function processShortCodes(content) {
//
// Create the results variable.
//
var results = "";
//
// Find the first match.
//
var scregFind = /\-\[([^\]]*)\]\-/i;
var match = scregFind.exec(content);
if (match != null) {
results += content.substr(0,match.index);
var scregNameArg = /(\w+)(.*)*/i;
var parts = scregNameArg.exec(match[1]);
if (parts != null) {
//
// Find the closing tag.
//
var scregClose = new RegExp("\\-\\[\\/" + parts[1] + "\\]\\-");
var left = content.substr(match.index + 4 + parts[1].length);
var match2 = scregClose.exec(left);
if (match2 != null) {
//
// Process the enclosed shortcode text.
//
var enclosed = processShortCodes(content.substr(match.index + 4 + parts[1].length, match2.index));
//
// Figure out if there were any arguments.
//
var args = "";
if (parts.length == 2) {
args = parts[2];
}
//
// Execute the shortcode.
//
results += shortcodes[parts[1]](args, enclosed);
//
// Process the rest of the code for shortcodes.
//
results += processShortCodes(left.substr(match2.index + 5 + parts[1].length));
} else {
//
// Invalid shortcode. Return full string.
//
results = content;
}
} else {
//
// Invalid shortcode. Return full string.
//
results = content;
}
} else {
//
// No shortcodes found. Return the string.
//
results = content;
}
return (results);
}
</pre><div class="contentsignin">Salin selepas log masuk</div></div>
<p><code>processShortCodes()</code> 函数将网页内容作为字符串并搜索所有短代码。短代码是类似于 HTML 标签的代码块。一个例子是:</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:html;toolbal:false;">-[box]-
<p>This is inside a box</p>
-[/box]-
</pre><div class="contentsignin">Salin selepas log masuk</div></div>
<p>此代码在 HTML 段落周围有一个 <code>box</code> 的简码。其中 HTML 使用 <code><</code> 和 </code>>></code>,短代码使用 <code>-[</code> 和 </code>>]-</code>。在名称后面,可以包含或不可以包含包含短代码参数的字符串。</p>
<p><code>processShortCodes()</code> 函数查找短代码,获取其名称和参数,找到末尾以获取内容,处理短代码的内容,使用参数和内容执行短代码,将结果添加到完成中页面,并在页面的其余部分搜索下一个短代码。循环是通过递归调用函数来执行的。</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">//
// Define the shortcodes function array.
//
var shortcodes = {
'box': function(args, inside) {
return ("<div class='box'>" + inside + "</div>");
},
'Column1': function(args, inside) {
return ("<div class='col1'>" + inside + "</div>");
},
'Column2': function(args, inside) {
return ("<div class='col2'>" + inside + "</div>");
},
'Column1of3': function(args, inside) {
return ("<div class='col1of3'>" + inside + "</div>");
},
'Column2of3': function(args, inside) {
return ("<div class='col2of3'>" + inside + "</div>");
},
'Column3of3': function(args, inside) {
return ("<div class='col3of3'>" + inside + "</div>");
},
'php': function(args, inside) {
return ("<div class='showcode'><pre type='syntaxhighlighter' class='brush: php'>" + inside + "</pre><div class="contentsignin">Salin selepas log masuk</div></div></div>");
},
'js': function(args, inside) {
return ("<div class='showcode'><div class="code" style="position:relative; padding:0px; margin:0px;"><pre type='syntaxhighlighter' class='brush: javascript'>" + inside + "</pre><div class="contentsignin">Salin selepas log masuk</div></div></div>");
},
'html': function(args, inside) {
return ("<div class='showcode'><div class="code" style="position:relative; padding:0px; margin:0px;"><pre type='syntaxhighlighter' class='brush: html'>" + inside + "</pre><div class="contentsignin">Salin selepas log masuk</div></div></div>");
},
'css': function(args, inside) {
return ("<div class='showcode'><div class="code" style="position:relative; padding:0px; margin:0px;"><pre type='syntaxhighlighter' class='brush: css'>" + inside + "</pre><div class="contentsignin">Salin selepas log masuk</div></div></div>");
}
};
</pre>
<p>下一节定义 <code>shortcodes</code> json 结构,该结构定义与其函数关联的短代码的名称。所有短代码函数都接受两个参数:<code>args</code> 和 <code>inside</code>。 <code>args</code> 是名称和空格之后、标签结束之前的所有内容。 <code>inside</code> 是开始和结束短代码标记包含的所有内容。这些功能是基本功能,但您可以创建一个短代码来执行您能在 JavaScript 中想到的任何功能。</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">//
// Function: figurePage
//
// Description: This function figures the page type
// and loads the contents appropriately
// returning the HTML contents for the page.
//
// Inputs:
// page The page to load contents.
//
function figurePage(page) {
var result = "";
if (fileExists(page + ".html")) {
//
// It's an HTML file. Read it in and send it on.
//
result = fs.readFileSync(page + ".html");
} else if (fileExists(page + ".amber")) {
//
// It's a jade file. Convert to HTML and send it on. I
// am still using the amber extension for compatibility
// to goPress.
//
var jadeFun = jade.compileFile(page + ".amber", {});
// Render the function
var result = jadeFun({});
} else if (fileExists(page + ".md")) {
//
// It's a markdown file. Convert to HTML and send
// it on.
//
result = marked(fs.readFileSync(page + ".md").toString());
//
// This undo marked's URI encoding of quote marks.
//
result = result.replace(/\"\;/g,"\"");
}
return (result);
}
</pre><div class="contentsignin">Salin selepas log masuk</div></div>
<p><code>figurePage()</code> 函数接收服务器上页面的完整路径。然后,此函数根据扩展名测试它是否为 HTML、Markdown 或 Jade 页面。我仍然在 Jade 中使用 .amber,因为那是我在 goPress 服务器上使用的库。所有 Markdown 和 Jade 内容都会先转换为 HTML,然后再传递给调用例程。由于 Markdown 处理器将所有引号翻译为 <code>"</code>,因此我在传回之前将它们翻译回来。</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">//
// Function: fileExists
//
// Description: This function returns a boolean true if
// the file exists. Otherwise, false.
//
// Inputs:
// filePath Path to a file in a string.
//
function fileExists(filePath) {
try {
return fs.statSync(filePath).isFile();
} catch (err) {
return false;
}
}
</pre><div class="contentsignin">Salin selepas log masuk</div></div>
<p><code>fileExists()</code> 函数是 <code>fs.exists()</code> 函数的替代品,该函数曾经是 Node.js 的 <code>fs</code> 库的一部分。它使用 <code>fs.statSync()</code> 函数来尝试获取文件的状态。如果发生错误,则会返回 <code>false</code>。否则,返回 <code>true</code>。</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:javascript;toolbal:false;">//
// Function: MergeRecursive
//
// Description: Recursively merge properties of two objects
//
// Inputs:
// obj1 The first object to merge
// obj2 The second object to merge
//
function MergeRecursive(obj1, obj2) {
for (var p in obj2) {
try {
// Property in destination object set; update its value.
if (obj2[p].constructor == Object) {
obj1[p] = MergeRecursive(obj1[p], obj2[p]);
} else {
obj1[p] = obj2[p];
}
} catch (e) {
// Property in destination object not set; create it and set its value.
obj1[p] = obj2[p];
}
}
return obj1;
}
</pre><div class="contentsignin">Salin selepas log masuk</div></div>
<p>最后一个函数是 <code>MergeRecursive()</code> 函数。它将第二个传递对象复制到第一个传递对象中。在添加特定于页面的部分之前,我利用它将主 <code>parts</code> 全局变量复制到本地副本中。</p>
<h3>本地运行</h3>
<p>保存文件后,您可以使用以下命令运行服务器:</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:shell;toolbal:false;">node nodePress.js
</pre><div class="contentsignin">Salin selepas log masuk</div></div>
<p>或者,您可以使用 package.json 文件中的 <code class="inline">npm</code> 脚本。您可以像这样运行 npm 脚本:</p>
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:shell;toolbal:false;">npm start
</pre><div class="contentsignin">Salin selepas log masuk</div></div>
<p>这将运行 package.json 文件内的 <code>start</code> 脚本。</p>
<p><img src="https://img.php.cn/upload/article/000/000/164/169372039225694.jpg" alt="Cipta sistem pengurusan kandungan: nodePress"></p>
<p>将您的网络浏览器指向 <code>http://localhost:8080</code>,您将看到上面的页面。您可能已经注意到我在主页上添加了更多测试代码。对页面的所有更改都包含在本教程的下载中。它们大多只是一些小的调整,以更全面地测试功能并适应使用不同库的任何差异。最显着的区别是 Jade 库不使用 <code class="inline">$</code> 来命名变量,而 Amber 则使用。</p>
<h2>结论</h2>
<p>现在,您在 Go 和 Node.js 中拥有完全相同的平面文件系统 CMS。这只是您可以使用此平台构建的内容的表面。尝试并尝试新事物。这是创建您自己的网络服务器的最佳部分。</p>
Atas ialah kandungan terperinci Cipta sistem pengurusan kandungan: nodePress. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!