使用 Node.js 平台的express开发框架及mongodb搭建一个简单的博客

首先讲下简单博客功能:

  • 1.编辑内容发布前:


    发布前.png
  • 2.发布后:
    里面的img标签会成功解析成图片


    发布后
  • 3.更新界面
    可点击“更新”链接 对之前发布的内容进行更新,这里我就把图片src给换了

更新界面
  • 4.更新后的页面
    更新后,我们发现图片也变了。


    更新后的页面
  • 5.删除指定的博客内容

通过这次搭建简单的博客,我们可以对express框架、模板引擎、路由、数据库操作及ajax等有进一步的了解。好了,转入正题:

首先安装node 这个就不必说了。。。以下指令如无特别说明均在Node.js command prompt(Node.js 命令提示符)完成。

一、express的准备工作

1、安装express

npm install express-generator -g

2、快速生成项目

express blog

3、进入项目并安装相关依赖

cd blog && npm install

4、启动express

npm start

5、在浏览器中,打开 http://localhost:3000/ 即可

express安装成功后

二、mongodb的配置工作

mongodb属于非关系型数据库NoSQL(Not Only SQL)。Redis,CouchDB也均属于非关系型数据库,而SQL Server,Oracle,MySQL(开源),PostgreSQL(开源) 属于关系型数据库SQL (Structured Query Language) 。关于SQL 和 NoSQL 的区别可见 SQL 和 NoSQL 的区别

关系型与非关系型数据库结构对比

由上表,我们可以看到mongodb是由集合组成,而集合又由文档组成,文档又由一组组键值对组成,也就是说mongodb最小单位为文档。而关系型数据库最小单位为记录;

非关系型数据库举例
关系型数据库举例

其他详见上面介绍的文章或者自行查阅资料。

1、下载与安装mongodb软件
由于我的电脑是win7 32位 官网默认给出的最新版不支持,因此我安装了3.2.4版(32位)
其下载地址为:http://downloads.mongodb.org/win32/mongodb-win32-i386-3.2.4-signed.msi
安装完后,我的默认安装地址:C:\Program Files\MongoDB\Server\3.2\bin

2、配置环境变量
配置环境变量,以便在任何目录下均可使用mongodb,而无需进入到mongodb中bin的目录。步骤如下:


Paste_Image.png
Paste_Image.png
Paste_Image.png

编辑图中圈出的path(用户变量及系统变量均要修改),在

Paste_Image.png

变量值后面加上;C:\Program Files\MongoDB\Server\3.2\bin 也就是你自己的安装目录;注意别忘记前面的分号;啦。

3、设置数据存放目录

在之前新建的blog目录下再新建data目录,然后执行:

mongod.exe --dbpath C:\Users\ssd170329\express\blog\data --storageEngine=mmapv1  
//ssd170329记得替换成自己的pc名字

之所以在指令后面加--storageEngine=mmapv1 是因为

在2015/3/17以前,MongoDB只有一个存储引擎,叫做MMAP,MongoDB3.0的推出使得MongoDB有了两个引擎:MMAPv1和WiredTiger。
MMAPv1:适应于所有MongoDB版本,MongoDB3.0的默认引擎
WiredTiger:仅支持64位MongoDB

,因此不加会报错!

设置数据存放目录这步完成后,会有如下提示:


Paste_Image.png

并且data文件夹里会新出现一些文件及文件夹

data文件夹里新出现的文件及文件夹

4、启动mongodb
假如我们刚完成数据存放目录的设置,则该步骤可以省略,否则需要以相同指令启动mongodb

mongod.exe --dbpath C:\Users\ssd170329\express\blog\data --storageEngine=mmapv1  
//ssd170329记得替换成自己的pc名字

不启动的话在后面会报错。

三、数据库的操作准备知识

上面启动mongodb后,我们先来熟悉下mongodb数据库的操作,在数据库操作之前,我们需要先连接mongodb 连接的指令:mongo 。提示:该指令需要另外开一个Node.js command prompt窗口再输入!!!前面的指令mongod.exe --dbpath C:\Users\ssd170329\express\blog\data --storageEngine=mmapv1只是启动mongodb用的,后面我们重新开一个Node.js command prompt窗口是为了可以方便的操作数据库。

1、数据库操作
  • a、查看本地有哪些数据库可以使用show dbs (默认会有个名为test的数据库)
  • b、查看当前操作的数据库名db
  • c、新建数据库use blog (此时我们输入db时还不会显示blog,需要进行比如插入文档等,才会被记录到本地)
  • d、往blog插入文档 db.post.insert({"content": "123"})
    此时命令行中会出现 WriteResult({"nInserted" : 1}) ,则表明我们插入数据成功,同时在数据存放目录里(blog\data)看到新增了 blog.ns 和 blog.0 两个文件。
  • e、删除数据库
    删除前需要切换到该数据库,否则会默认删除test数据库。删除的指令db.dropDatabase()
2、集合操作

不同的集合便构成了数据库,下面针对某个集合来说说集合的相关操作

  • a、查看数据库里的集合 show collections
    若无,则什么也不显示,若有则显示相应的集合名词及system.indexes
  • b、创建一个名为items的集合db.createCollection('items')
  • c、当然你可以无需向上面那样特意创建集合,你可以db.items.insert({}), 这样表示向items集合里插入空文档,当无items集合时,会自动创建一个items集合
  • d、删除items集合 db.items.drop()
3、文档操作

上面讲了集合的操作,下面讲下其里面的文档如何操作(增、删、改、查):

  • a、增-----在post集合中插入一个或多个文档
    插入单个文档: db.post.insert({"content":"123"})
    插入多个文档:db.post.insert([{"content":"456"},{"content":"789"}])
  • b、删----删除post集合中的文档
    db.post.remove({"content","123"})
  • c、改-----修改post集合中的文档
    db.post.update({"content","456"},{$set{"content","abc"}})
  • d、查----查询post集合中的文档
    查询所有文档:db.post.find()
    查询content为abc的文档:db.post.find({},{"content","abc"})

当然还有很多其他方法,大家可以自行查阅资料。
另外值得一提的是,大家别把数据库和集合的概念搞错了,比如本例中show dbs 结果是 blog 、local(或者test),而show collections结果是info 、post、system.indexes , 因此db.blog.find()在此处的用法是错误的!而db.post.find()才正确!

Paste_Image.png

四、修改express里的相关文件

express文件解释.png
1、修改模板引擎

由于express默认的jade模板引擎我们前端使用的不怎么习惯,我们可以改成ejs模板。

  • a、安装ejs模板引擎 (安装前记得cd到blog目录下)
    npm install ejs --save
  • b、修改app.js中的关于模板引擎部分
    在根目录app.js中修改模板引擎
    修改前,app.js中有这样一段是关于模板引擎的
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

第一句话的意思是 设置模板引擎的路径在根目录中的views里,第二句话的意思是,将模板引擎设置为jade的后缀的文件,为了修改成ejs的模板引擎我们需要将上面两句改为

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'html');
app.engine('html', require("ejs").__express);
  • c、 修改views文件夹下的文件
    将views文件夹下的jade文件全部删除,并新增index.html、update.html、error.html 三个文件,
    改三个文件里的内容分别如下:
    index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<title><%= title %></title>
<link rel="stylesheet" type="text/css" href="/stylesheets/style.css">
</head>

<body>

    <h3 class=""><%= name %></h3>

</body>

</html>

里面的<%= title %><%= name %> 就是ejs所输出的标签(另外补充下:<%= %>输出标签,其会原文输出HTML标签,而<%- %>输出标签,其会被浏览器解析后再输出, 这个就跟innerText与innerHTML的性质差不错)

update.html

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<title><%= title %></title>
<link rel="stylesheet" type="text/css" href="/stylesheets/style.css">
</head>

<body>

    <h3 class=""><%= name %></h3>

</body>

</html>

error.html

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<title>错误页</title>
<link rel="stylesheet" type="text/css" href="/stylesheets/style.css">
</head>

<body>

    <div class="content">错误!</div>

</body>

</html>
2、设置路由器

以上只是修改了模板引擎相关部分,接下来需要修改路由器部分:
首先删除route里面的所有js文件,然后新建index.js及update.js (由于error 在app.js里有写,因此这里就没新建error.js)

index.js里的代码:

var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
    res.render('index', { title: '博客首页', name: '博客'});
});

module.exports = router;

update.js里的代码

var express = require('express');
var router = express.Router();

/* GET update page. */
router.get('/update', function(req, res, next) {
    res.render('update', { title: '博客更新页', name: '博客更新'});
});

module.exports = router;

这里各自的路由设置好后,我们还需要对项目入口文件app.js中的路由部分进行修改才能够正常访问:
原有app.js中的部分

var routes = require('./routes/index');
var users = require('./routes/users');

...

app.use('/', routes);
app.use('/users', users);

上面修改成

var index = require('./routes/index');
var update = require('./routes/update');

...

app.use('/', index);
app.use('/', update);

接下来我们可以测试下到目前为止的效果:
控制面板cd到 在项目的根目录里,然后执行npm start
然后浏览器打开 http://localhost:3000/http://localhost:3000/update 我们可以看到相应的正确页面,而如果我们访问 http://localhost:3000/123http://localhost:3000/qwe 等我们未在路由器里定义的路径则会返回error的页面。

3、修改模板

首先将页面样式做好:
index.html里新增的代码

<div class="blog-add">
    <div class="blog-textarea">
        <textarea name="" class="textarea-add" cols="30" rows="10"></textarea>
    </div>
    <p class="blog-action">
        <a class="btn-publish" href="javascript:;">发布</a>
    </p>
</div>

<div class="blog">
    <ul class="blog-list">
        <li class="blog-item">
            <p class="blog-content">你好</p>
            <p class="blog-extra">
                <span class="date">2015-10-10</span>
                <a href="javascript:;" class="delete">删除</a>
                <a href="javascript:;" class="update">更新</a>
            </p>
        </li>
    </ul>
</div>

update里新增的代码

<div class="blog-add">
    <div class="blog-textarea">
        <textarea name="" class="textarea-add" cols="30" rows="10"></textarea>
    </div>
    <p class="blog-action">
        <a class="btn-update" href="javascript:;">更新</a>
    </p>
</div>

修改后的css

* {margin: 0;padding: 0;}
body {padding: 50px;font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;}
a {color: #00B7FF;}
li {list-style: none;}
a {text-decoration: none;}

.blog-add .blog-textarea {margin: 20px 0 10px;padding: 10px;border: 1px solid #ccc;}
.blog-add .textarea-add {width: 100%;height: 80px;border: none;resize: none;outline: none;}
.blog-add .blog-action {line-height: 30px;text-align: right;}
.blog-add .blog-action .btn-publish,
.blog-add .blog-action .btn-update {border: 1px solid #00B7FF;padding: 5px 20px;border-radius: 3px;}
.blog-list .blog-item {padding-top: 10px;line-height: 22px;}
.blog-list .blog-item p {padding: 5px 0;word-wrap: break-word;}
.blog-list .blog-item {border-bottom: 1px dotted #ddd;}
.blog-list .blog-extra {overflow: hidden;color: #999;text-align: right;}
.blog-list .blog-extra .date {float: left;}
.blog-list .blog-extra a {border-right: 1px dotted #00B7FF;padding: 0 10px;margin-right: -6px;font-size: 12px;}

记得在public里javascripts目录下新增jquery.min.js文件。

五、express与mongodb交互

首先梳理下交互的流程:前台输入完内容后点击发布,将内容发送至后台数据库保存后,再由后台从数据库中读取数据然后将数据显示到页面上。

1、发送数据至后台

index.html中新增的js代码:

<script type="text/javascript" src="/javascripts/jquery.min.js"></script>
<script>
$(function() {

    // 发布
    $('.btn-publish').on('click', function() {
        var blogContent = $('.textarea-add').val().trim().replace(/\n/g, '<br/>');

        if (!blogContent) {
            alert('内容不能为空!');
            return;
        }

        var date = new Date(),
            yy = date.getFullYear(),
            MM = date.getMonth() + 1,
            dd = date.getDate(),
            hh = date.getHours(),
            mm = date.getMinutes(),
            ss = date.getSeconds();

        var postData = {
            'content': blogContent,
            'date': yy + '-' + MM + '-' + dd + ' ' + hh + ':' + mm + ':' + ss
        };

        $.ajax({ 
            url: '/',
            type: 'post',
            data: postData,
            success: function(data){
                alert('发布成功!');
                location.href = '/';
            },
            error: function(data){ 
                alert('发布失败!');
                location.href = 'error';
            }
        }); 

    });

});
</script>
2、后端接收数据,并将数据保存至数据库

接下来需要用到mongoose,关于它的介绍可看下面:

mongoose 是一个文档对象模型库(ODM),它的语法和 mongodb 里的 shell 命令是一样的。如果你使用过 node.js 直接操作 mongodb,你会发现代码中会出现很多嵌套、回调以及各种潜在问题。但有了 mongoose,你可以直接在 node.js 里使用 mongoose 自身的语法,不仅代码简洁,操作数据方便,而且避免了很多额外的问题。

需要使用mongoose,我们就得安装它:
npm install mongoose --save

安装完后,

  • a、在blog/data 目录下新建mongoose.js,mongoose.js的代码如下:
var mongoose = require('mongoose');

mongoose.connect('mongodb://localhost/blog');
// 注意!!mongodb://localhost/blog 这里的blog要和你创建的
// 数据库名称(不是集合名称)一样,而不是指文件的路径,它跟
// 文件路径没有半毛钱的关系。今天被坑到了。。。。

var blogSchema = new mongoose.Schema({
    content: {type: String, unique:true}, // unique 保证数据的唯一,但有时候不管用
    date: String
}, {collection: 'post'});

var post = mongoose.model('post', blogSchema);

module.exports = post;
  • b、在根目录app.js里的 var app = express();下一行新增如下一行代码:global.post = require('./data/mongoose');
  • c、由于之前我们使用ajax发送数据是在index.html中post的,因此需要在index.js中处理post请求,下面在index.js中新增代码
/* POST home page. */
router.post('/', function(req, res) { 
    var content = req.body.content;
    var date = req.body.date;

    if (content && date) {
        var newPost = new post({
            content: content,
            date: date
        });

        newPost.save(function (err) {
            if (err) {
                console.error(err);
                return;
            }
            // newPost is saved!
            console.log('保存成功!');
            res.send(200);
        });
    }
});

接下来,我们npm start重启服务器,然后在页面的编辑区域随便输什么点击发布链接后 在命令行输入 db.post.find()后,我们可以在命令行中看到你之前输入的内容。但是此时页面并没有解析出你刚刚输入的内容,只是数据库里存储了你输入的内容,接下来我们需要从数据库中读取该内容并发送至页面上。

3、读取数据库中的数据并将其展示到页面上
  • a、首先我们将index.js中的以下内容
/* GET home page. */
router.get('/', function(req, res, next) {
    res.render('index', { title: '博客首页', name: '博客'});
});

修改为

/* GET home page. */
router.get('/', function(req, res, next) {

    post.find({}, function(err, docs) {
        if (err) {
            console.error(err);
            return;
        }
        // docs 是包含了符合条件的多个文档的一个数组
        // console.log(docs);
        res.render('index', { title: '博客首页', name: '博客', content: docs.reverse()}); //reverse的目的是让最新的数据在前面
    });

});
  • b、其次我们将index.html中的以下部分
<div class="blog">
    <ul class="blog-list">
        <li class="blog-item">
            <p class="blog-content">你好</p>
            <p class="blog-extra">
                <span class="date">2015-10-10</span>
                <a href="javascript:;" class="delete">删除</a>
                <a href="javascript:;" class="update">更新</a>
            </p>
        </li>
    </ul>
</div>

修改为:

<div class="blog">
    <ul class="blog-list">
        <% for(var i=0; i<content.length; i++) { %>
            <li class="blog-item">
                <p class="blog-content"><%- content[i].content %></p>
                <p class="blog-extra">
                    <span class="date"><%= content[i].date %></span>
                    <a href="javascript:;" class="delete" data-content="<%= content[i].content %>">删除</a>
                    <a href="javascript:;" class="update" data-content="<%= content[i].content %>">更新</a>
                </p>
            </li>
        <% } %>
    </ul>
</div>

上面用到了ejs模板引擎当中的流程控制标签<% %>,前面介绍了另外两个输出标签<% - %><% = %> ,其他常用的还有<%# %> 注释标签 , % 对标记进行转义,-%>去掉没有的空格。

4、数据的删除与更新

前面你应该注意到了下面的语句

<a href="javascript:;" class="delete" data-content="<%= content[i].content %>">删除</a>
<a href="javascript:;" class="update" data-content="<%= content[i].content %>">更新</a>

这两个语句就是为本节做铺垫的。我们前面实现了页面内容实时发布,现在我们需要增加页面的删除与更新功能,首先我们来实现页面的更新功能。

  • (1)、页面数据的删除
    首先在index.html中新增js代码:
    // 删除
    $('.delete').on('click', function () {
        var deleteContent = $('.delete').attr('data-content');
        var postData ={
            'deleteContent':deleteContent
        };
        if (confirm("你确定要删除该条微型博客吗")){
            $.ajax({
                type: 'post',
                url: '/',
                data: postData,
                success:function (data) {
                    location.href='/'

                },
                error:function (data) {
                    alert("删除失败,请重试!");
                    location.href='/'
                }
            })
        }
    });

前台我们利用ajax向后台发了一个ajax的post请求,因此后台我们需要对该请求进行处理(即在数据库中删除指定的内容):
后台index.js 中的router.post部分新增js代码:

    var deleteContent = req.body.deleteContent;

    if (deleteContent) {
        post.remove({content: deleteContent}, function(err) {
            if (err) {
                console.error(err);
                return;
            }
            console.log('删除成功!');
            res.send(200);
        });
    }
  • (2)、页面数据的更新
    接下来我们来实现下更新的功能;

更新的流程是:
在主页面点击更新链接--->调转至更新页面--->在更新页面编辑文字-->再点击发布变返回至主页面;

代码的实现思路是:
更新链接绑定点击事件(当点击更新链接时跳转至update.html并在url后面以搜索字符串的形式添加之前已经填好的数据),当跳转至update后再将url后面添加的数据赋值给update的textarea,再绑定update里的更新链接的点击事件(当点击时将textarea里的数据保存至数据库中,然后跳转至主页面),当跳转至主页面后又从数据库中读取相应数据并渲染至主页面上;

  • a、 index.html新增的js代码
    // 更新跳转
    $('.update').on('click', function() {
        var updateContent = $(this).attr('data-content');
        location.href = '/update?updateBlog=' + updateContent;
    });
  • b、update.html新增的js代码
<script type="text/javascript" src="/javascripts/jquery.min.js"></script>
<script>
$(function() {

    // 从url读取要更新的内容,简单粗暴处理
    var oldContent = decodeURI(window.location.search.substr(12));

    // 设置它到文本域,并保留换行
    $('.textarea-add').val(oldContent.replace(/\<br\/>/g, '\n'));

    // 更新
    $('.btn-update').on('click', function() {
        var updateContent = $('.textarea-add').val().trim().replace(/\n/g, '<br/>');

        if (updateContent === oldContent) {
            alert('内容没有更新!');
            return;
        }

        var postData = {
            'oldContent': oldContent,
            'updateContent': updateContent
        };

        $.ajax({ 
            url: '/',
            type: 'post',
            data: postData,
            success: function(data){
                alert('更新成功!');
                location.href = '/';
            },
            error: function(data){ 
                alert('更新失败!');
                location.href = 'error';
            }
        }); 
    });

});
</script>

由于前端点击更新后通过ajax发出了一个post请求,因此后端index.js需要处理该post请求,因此index.js post里面router.post的代码更新如下:

/* POST home page. */
router.post('/', function(req, res) { 
    var content = req.body.content;
    var date = req.body.date;

    if (content && date) {
        var newPost = new post({
            content: content,
            date: date
        });

        newPost.save(function (err) {
            if (err) {
                console.error(err);
                return;
            }
            // newPost is saved!
            console.log('保存成功!');
            res.send(200);
        });
    }

    var deleteContent = req.body.deleteContent;

    if (deleteContent) {
        post.remove({content: deleteContent}, function(err) {
            if (err) {
                console.error(err);
                return;
            }
            console.log('删除成功!');
            res.send(200);
        });
    }

    var oldContent = req.body.oldContent,
        updateContent = req.body.updateContent;

    if (oldContent && updateContent) {
        post.update({content: oldContent}, {$set: {'content': updateContent}}, function(err) {
            if (err) {
                console.error(err);
                return;
            }
            console.log('更新成功!');
            res.send(200);
        });
    }
});

至此,我们可以重启下服务器 npm start ,终于完成了开头描述的效果啦~ 看此简单的功能,其实里面涉及到蛮多知识的~ 最后贴下自己的所有代码到github上---https://github.com/have-not-BUG/simpleBlog 欢迎star或下载。
另: 以上如有错误之处,恳请指出。谢谢!

补充资料:
1、MongoDB 更新文档
2、mongodb_查询操作使用_条件查询、where子句等

参考资料:
1、SQL 和 NoSQL 的区别
2 、使用express和mongodb搭建一个极简博客
2、ejs模板的书写

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容