express4.x API中文文档

原文在这里http://www.expressjs.com.cn/4x/api.html发现这不支持表格,表格内容没有翻译,因为水平有限,部分语句可能存在错误,之后我会陆续更正,同时使语句更加通顺。

一、express()

创建一个Express应用,express()是一个由express模块导出的入口(top-level)函数.

var express=require("express");
var app=express();
内置方法

express.static(root,[options])
express.static是一个Express内置的一个唯一的一个中间件,是基于server-static开发的,负责托管Express应用内的静态资源。
root参数指的是静态资源文件所在的根目录。
options对象是可选的,支持以下属性:

1、dotfiles:对点文件(以点开头的文件)提供服务,可能的值"allow"、"deny"、"ignore",默认值是ignore

2、etag

3、extensions:设置文件的拓展回退,默认值为false

4、index:设置目录的首页文件,设置为false时禁止使用目录首页默认值为"index.html"

5、lastModefied:设置Last-Modified头到操作系统上的文件的最后修改日期,默认为true

6、maxAge:设置Cache-Control头文件的max-age属性,单位是毫秒,也可以是ms format格式的字符串,默认值为0

7、redirect:当请求路径是一个目录时,重定向到尾部添加"/",默认值是true

8、setHeader:设置响应文件的http头文件

关于此中间件的细节,请参考通过Express托管静态资源文件

二、Application

app对象一般是Express应用程序,通过使用入口函数express()导出

var express=require("express");
var app=express();

app.get("/",function(req,res){
    res.send("hello world");
});
app.listen(3000);

app对象有处理以下事务的一些方法:

  • 处理http请求的路由:请看app.METHOD和app.param

  • 配置中间件:请看app.route

  • 渲染HTML视图:请看app.render

  • 设置模板引擎:请看app.engine

app同样可以设置一些影响app工作的属性,获取更多信息,请看Application settings

属性

app.locals

app.locals对象是一个JavaScript对象,并且它的属性是应用程序的本地变量

app.locals.title
// =>"My App"

app.locals.email
// =>"me@myapp.com"

一旦设置了app.locals的属性,这个属性就会存在于应用程序的整个生命周期,二而不同于res.locals设置的属性只存在请求的生命周期中

你可以在应用程序的模板中使用本地变量,这对于模板和应用级数据来说提供了很有用的辅助功能,但是你在中间件中并不能访问本地变量

app.locals.title="My App";
app.locals.strftime=require("strftime");
app.locals.email="me@myapp.com";

app.mountpath

app.mountpath属性是子app加载的路径

一个子app是express的实例,它可以作为路由句柄处理请求
var express=require("express");

var app=express();//the main app
var admin=express();//the sub app

admin.get("/",function(req,res){
    console.log(admin.moutpath);// /admin
    res.send("Admin Homepage");
})

app.use("/admin",admin);//mout the sub app

它有点像req对象的baseUrl属性,除了req.baseUrl返回匹配的url路径,而不是模式。

如果一个子应用加载了多种路径模式,app.moutpath返回一个它加载的模式的列表,如下所示:

var admin=express();

admin.get("/",function(req,res){
    console.log(admin.mountpath);//["/adm*n","/manager"]
    res.send("Admin Homepage");
})

var secret=express();
secret.get("/",function(req,res){
    console.log(secret.mountpath);// /secr*t
    res.send("Admin Secret");
})

admin.use("/secr*t",secret);// load the "secret" router on "/secr*t",on the "admin" sub app
admin.use(["/adm*n","/manager"],admin);//load the "amdin" router on "/adm*n" and "/manager",on the parent app
事件

app.on("mount",claaback(parent))

这个事件当一个sub-app被挂载到一个父级应用程序时被触发,父级应用程序会被当做是一个参数传递给回调函数

var admin=express();

admin.on("mount",function(parent){
    console.log("Admin Mounted");
    console.log(parent);  //refers to the parent app
})

admin.get("/",function(req,res){
    res.send("Admin Homepage");
});

app.use("/admin",admin);
方法

app.all(path,callback[,callback...])

这个方法就像标准的app.METHOD()方法,除了它还可以匹配所有http方法。

对于特定的路径前缀或者是任意匹配映射全局逻辑。例如,你把下面的内容放在所有路由定义的前面,它要求所有从这个点开始的路由都需要加载认证和自动加载一个用户,请记住这些回调函数并不一点是终点,loadUser可以像一个任务一样,然后使用next()方法继续匹配接下来的路由

app.all("*",requireAuthentication,loadUser);

相等于:

app.all("*",requireAuthentication);
app.all("*",loadUser);

另一个例子就是使用全局函数,这个例子很像前面的,但是这个把路径限制了以"/api"开头的路径

app.all("/api/*",requireAuthentication);

app.delete(path,callback[,callback...])

HTTP DELETE路由请求到特定的路径和特定的回调函数,获取更多信息,参看routing guide

你可以提供很多的回调函数,就像中间件一样,除了这些回调可以调用next("route")来绕过接下来的路由回调,你可以用这种机制给一个路由设置一些前提条件,如果当前路由并不能满足条件,你可以将控制权传递给接下来的路由。

app.delete("/",function(req,res){
    res.send("DELETE request to homepage")
})

app.disable(name)

name是一个app settings table的一个属性,给name设置布尔值false,使用app.set("foo",false)相当于使用app.disable("foo");

例如:

app.disable("trust proxy);
app.get("trust proxy");
// =>false

app.disabled(name)

name是一个app settings table的一个属性,如果给name设置disable(false),则返回true

app.disabled("trust proxy");
// =>true

app.enable("trust proxy");
app.disabled("trust proxy");
// =>false

app.enable(name)

name是一个app settings table的一个属性,给name设置布尔值true,使用app.set("foo",true)相当于使用app.enable("foo");

app.enable("trust proxy");
app.get("trust proxy");
// =>true

app.enabled(name)

name是一个app settings table的一个属性,如果给name设置enable(true),则返回true

app.enabled("trust proxy");
// =>false

app.enable("trust proxy");
app.enabled("trust proxy");
// =>true

app.engine(ext,callback)

设置模板引擎来渲染

默认情况下,Express需要使用require()来加载基于文件后缀名的引擎,例如,如果你想要渲染一个名为"foo.jade"的文件,Express在内部调用下面的内容,并且缓存require()的结果以供后续调用,以此来提高性能

app.enging("jade",require("jade").__express);

为那些没有使用.__express的引擎使用这个方法,或者你想对模板引擎做一个不一样的拓展。

例如,使EJS模板引擎匹配".html"文件

app.engine("html",require("ejs).renderFile);

在这个情况下,EJS提供了一个和Express希望的拥有(path,options,callback)的.renderFile()方法,尽管说明了这个方法在ejs._express的内部,所以你使用".ejs"为后缀名时,你不需要做什么。

一些模板引擎并不遵循这个惯例,consolidata.js这个库可以使得这写模板引擎遵循这个惯例,所以他们也可以和Express无缝紧密地工作

var engines=require("consolidate");
app.engine("haml",engines.haml);
app.engine("html",engines.hogan);

app.get(name)

返回应用程序设置的name的值,这个name是一个app setting table的一个字符串之一,例如:

app.get("title");
// =>undefined

app.set("title","My Site");
app.get("title");
// =>" My Site"

app.get(path,callback[,callback...])

特定路径和回调函数的HTTP GET请求的路由,获取更多信息,请看routing guide

你可以提供很多的回调函数,就像中间件一样,除了这些回调可以调用next("route")来绕过接下来的路由回调,你可以用这种机制给一个路由设置一些前提条件,如果当前路由并不能满足条件,你可以将控制权传递给接下来的路由。

app.get("/",function(req,res){
    res.send("GET request to homepage");
})

app.listent(port,[hostname],[backlog],[callback])

绑定和监听主机和端口,这个方法和node原生的http.Server.listen()相同

var express=require("express");
var app=express();
app.listen(3000);

由express()创建的app实例其实是一个JavaScript方法,app是作为node的http服务器的一个回调函数去处理请求。这使得很容易使用相同的代码库为你的应用程序同时提供http和https版本,以为app并不从这里继承(它仅仅是一个方法)

var express=require("express");
var https=require("https");
var http=require("http");
var app=express();

http.createServer(app).listen(80);
https.createServer(options,app).listen(443);

app.listen()方法是下面所示的一个简便方法(仅仅对于HTTP而言):

app.listen=function(){
    var server=http.createServer(this);
    return server.listen.apply(server,arguments);

app.METHOD(path,callback[,callback])

当METHOD 是一个HTTP请求方法,比如小写的GET、PUT、POST等,返回一个HTTP请求。因此,实际的方法是appp.get()、app.post()、app.put()等,请看以下完整列表。

获取更多信息,请看routing guide

Expres支持以下路由方法,相等于响应的HTTP方法

checkout、connect、copy、delete、get、head、lock、merge、mkactivity、mkcol、move、m-search、notify、options、path、post、propfind、proppatch、purge、put、report、search、subscribe、trace、unlock、unsubscribe

通过以下方法将已有的JavaScript变量的名字转化为路由方法,例如:app"m-search"...

你可以提供很多的回调函数,就像中间件一样,除了这些回调可以调用next("route")来绕过接下来的路由回调,你可以用这种机制给一个路由设置一些前提条件,如果当前路由并不能满足条件,你可以将控制权传递给接下来的路由。

API文档对最常用的HTTP方法app.get()、app.post()、app.put()和app.delete()有明确的条目列出,但是,在上面列出的其他方法其实也有相同的方式。

这里有个特别的路由方法app.all(),它不是像其他HTTP方法一样工作,它在一个路径上对所有HTTP请求方法加载同一个中间件

在下面的例子中,无论是使用GET,POST,PUT,DELETE或是其他任何HTTP请求方法请求"/secret",处理方法都会执行。

app.all("/secret",function(req,res,next){
    console.log("Accessing the secret section..."));
    next();//pass control to the next handle

app.param([name],callback)

为路由参数添加一个触发回调函数,name是一个参数或者是一个name组成的数组,回调函数是一个方法。回调函数的参数是requset对象,response对象,next中间件和按照顺序的name的值。

如果name是一个数组,回调函数触发器按照这个数组中每个参数的声明顺序进行挂载。此外,除数组中最后一个参数外,在回调函数中调用next会调用数组中下一个参数的回调函数,对于数组中的最后一个参数, 调用next会调用这个路由正在处理的下一个中间件,就像它是一个string一样

例如,当:user在一个路由路径中,你可能需要匹配用户加载逻辑去自动提供req.user的路径或者展示参数输入验证

app.param("user",function(req,res,next,id){
    //try to get the user details from the User model and attach it to the request object
    User.find(id,function(err,user){
        if(err){
            next(err);
        }else if(user){
            req.user=user;
            next();
         }else{
            next(new Error("failed to load user"));
         }
      });
});

回调函数是他们本地定义的路由器,他们不是继承自挂接的app或者是路由器,因此,定义在app中的param回调函数只会被定义在app路由器中定义的参数触发

所有的param回调函数都会在其他出现param的路由处理之前调用,并且再一次请求和响应循环中,他们只会被调用一次,即使这个参数匹配在很多路由中,就像下面的例子一样:

app.param("id",function(req,res,next,id){
    console.log("CALLED ONLY ONCE");
    next();
});

app.get("/user/:id",function(req,res,next){
    console.log("although this matches");
    next();
});

app.get("/user/:id",function(req,res){
    console.log("and this matches to");
    res.end();
})

On GET /user/42,打印出以下内容:

CALLED ONLY ONCE
although this matches
and this matched too
app.param(["id","page"],function(req,res,next,value){
    console.log("CALL ONLY ONCE with",value);
    next();
})

app.get("/user/:id/:page",function(req,res,next){
    console.log("although this matches");
    next();
});

app.get("/user/:id/:page",function(req,res){
    console.log("and this matches too");
    res.end();
});

On GET /user/42/3,打印出以下内容:

CALLED ONLY ONCE with 42
CALLED ONLY ONCE with 3
although this matched
and this matches too

app.path[name],callback)

返回应用程序的路径字符串。

var app=express(),
      blog=express(),
      blogAdmin=express();

app.use("/blog",blog);
blog.use("/admin",blogAdmin);

console.log(app.path());//""
console.log(blog.path());//"/blog"
console.log(blogAdmin.path());//"/blog/admin"

这个方法在复杂挂载的app中会变得非常复杂,它总是比用req.baseUrl去获取app的路径更好

app.post(path,callback[,callback...])

特定路径和回调函数的HTTP POST请求的路由,获取更多信息,请看routing guide

你可以提供很多的回调函数,就像中间件一样,除了这些回调可以调用next("route")来绕过接下来的路由回调,你可以用这种机制给一个路由设置一些前提条件,如果当前路由并不能满足条件,你可以将控制权传递给接下来的路由。

app.post("/",function(req,res){
    res.send("POST request to homepage");
});

app.put(path,callback[,callback...])

特定路径和回调函数的HTTP PUT请求的路由,获取更多信息,请看routing guide

你可以提供很多的回调函数,就像中间件一样,除了这些回调可以调用next("route")来绕过接下来的路由回调,你可以用这种机制给一个路由设置一些前提条件,如果当前路由并不能满足条件,你可以将控制权传递给接下来的路由。

app.put("/",function(req,res){
    res.send("PUT request to homepage")
})

app.render(view,[locals],callback)

通过回调函数返回一个HTML视图,它接受一个可选参数,这个变量是一个用于视图的包含本地变量的对象,它很像res.render(),除了它不能发送给渲染的视图给客户端。

app.render()是非常有用的生成渲染视图的函数,在res.render()内部是用app.render()来渲染视图。

本地变量cache是为视图缓存保留的,如果你想在开发中使用视图缓存,可以将它设置为true,视图缓存在生产环境中默认使用。

app.render("email",function(err,html){
    //...
});

app.render("email",{name:"Tobi"},function(err,html){
//...
})

app.route(path)

返回一个单独路由器的实例,然后你可以用它和可选的中间件来处理HTTP操作。用app.route()避免重复的路由名字(和错字错误)

var app=express();

app.route("/event").all(function(req,res,next){
    //runs for all HTTP verb first
    //think of it as route specific middleware
})
.get(function(req,res,next){
    res.json(...);
})
.post(function(req,res,next){
    //maybe add a new event...
})

app.set(name,value)

给name分配一个值,name是app setting table中的一个属性。

给一个布尔属性调用app.set("foo",true)效果和调用app.enable("foo")是一样的,同样,给一个布尔属性调用app.set("foo",false)效果和调用app.disable("foo")是一样的。

通过app.get()方法取出设置的值

app.set("title","My title");
app.get("title");//" My title"

Application Setting

如果name是application settings中的一个,它会影响应用程序,完整的application settings列表如下:

app.use([path,]function[,function...])

在路径中挂载中间件,如果没有指定路径,默认为"/"。

路由会匹配任何紧跟在"/"后面的路径,例如,app.use("/apple",...)会匹配"/apple"、"/apple/images","/apple/images/news"等。

在一个中间件中,req.originalUrl是req.baseUrl和req.path的组合,就像下面的例子一样

app.use("/admin",function(req,res,next){
    //GET "http://www.example.com/admin/new"
    console.log(req.originalUrl);//"/admin/new"
    console.log(req.baseUrl);//"/admin"
    console.log(req.path);//"/new"

在一个路径中挂载一个中间件会使只要请求路径的基础地址匹配路径就会触发中间件的函数执行。因为路径默认为“/”,中间件挂载没有路径会在app每一个请求时执行。

//this middleware will be executed for every request to the app
app.use(function(req,res,next){
    console.log("Time :%d",Date.now());
    next();
})

中间件函数是按顺序执行的,因此中间件的顺序是很重要的

//this middleware will not allow the request to go beyond it
ap.use(function(req,res,next)){
    res.send("Hello World");
})

//request will never reach this route
app.get("/",function(req,res){
    res.send("Welcome");
})

path可以是一个字符串,一个路径模式,一个正则表达式去匹配路径,或者是他们的组成的数组

function可以是一个中间件函数、一系列中间件函数、中间件函数组成的数组或者是他们的组合。因为router和app是中间件的接口,当你想要任何其他中间件函数的时候你可以使用他们。

下面是在一个Express app中使用express static中间件的一些例子。

服务应用程序文件夹中的“public”目录中的静态资源

//GET /style.css etc
app.use(express.static(__dirname+"/public"));

在路径"/static"中挂载中间件仅仅当请求的路径以"/static"开头提供其中的静态资源

//GET /static/style.css etc
app.use("/static",express.static(__dirname+"/public"));

通过在static中间件之后加载logger中间件来禁止对静态资源请求进行日志记录

app.use(express.static(__dirname+"/public"));
app.use(logger());

从多个目录中提供静态资源,但是"/public"目录更有优先权

app.use(express.static(__dirname+"/public"));
app.use(express.static(__dirname+"/files"));
app.use(express.static(__dirname+"/uploads"));

三、Request

req对象代表HTTP请求并且对于请求的查询字符串有属性,parameter、body、HTTP headers等,按照惯例,在这份文档中用req代替这个对象(HTTP response是res),但是它真正的名字是由工作的回调函数中的参数决定的。

例如:

app.get("/user/:id",function(req,res){
    res.send("user "+req.params.id);
});

但是你也可以像这样:

app.get("/user/:id",function(request,response){
    response.send("user "+request.params.id);
})
属性

在Express4中,req.files默认不在req对象中。在req.files对象中获取上传的文件,通过大量处理的中间件如busboymulterformidablemultipartyconnect-multiparty或者pez

req.app

这个属性持有一个正在使用中间件的Express应用程序的实例。

如果你创建一个模块仅仅是为了导出作为中间件在主要文件中使用,中间件就可以通过req.app访问express。
例如:

//index.js
app.get("/viewdirectory",require("./mymiddleware.js"));
//mymiddleware.js
module.exports=function(req,res){
    res.send("The views directory is" + req.app.get("views"));
});

req.baseUrl

一个路由器挂载的路径,例如:

var greet=express.Route();

greet.get("/jp",function(req,res){
    console.logd(req.baseUrl);
    res.send("Konichiwa!");
});

app.use("/greet",greet);//load the router on "/greet"

即使你使用路径模式或者是一组路径模式来加载路由器,baseUrl属性返回的是匹配的字符串,不是模式,在下面的例子中,greet路由器挂载在了两个路径模式中

app.use(["/gre+t","/hel{2}o"],greet],greet);//load the router on "/gre+t" and "/hel{2}o"

当请求是/greet/jp,req.baseUrl就是"/greet",当请求是/hello/jp,req.baseUrl就是"/hello"。
req.baseUrl很像app对象的mountpath属性,除了app.mountpath返回的是匹配的路径属性。

req.body

包含在请求体中提交的键值对数据,默认值是undefined,当你使用body-parsing中间件,像bosy-parsermulter,就会取出这些键值对。

下面这个例子展示了如何使用bosy-parser中间件解析req.body。

var app=require("express");
var bodyParser=require("body-parser");
var multer=require("multer");

app.use(bodyParser.json());//for parsing application/json
app.use(badyParser.urlencoded({extened:true}));//for parsing application/x-www-form-urlencoded
app.use(multer());//for parsing multipart/form-data

app.post("/",function(req,res){
    console.log(req.body);
    res.json(req.bosy);
})

req.cookies

当使用cookie-parser中间件,这个属性就是包含请求发送的cookies的对象,如果请求中没有cookies,它默认就是{}

//Cookie:name=tj
req.cookies.name
//=> "tj"

获取更多信息、问题、关注,请看cookie-parser

req.fresh
表明请求是否是"fresh",它是req.stale的相反面。

如果一个cache-control没有一个no-cache指令,req.fresh是true,并且下面的情况也是true

  • if-modidied-since请求头指定了并且last-modified请求头相等于或者早于modified请求头

  • if-none-match请求头是*

  • if-none-match请求头在解析成它的指令后,没有匹配etag响应头

req.fresh
//=>true

获取更多信息、问题、关注,请看fresh

req.hostname

包含从"Host"HTTP头获取的主机名

//Host:"example.com:3000"
req.hostname
//=> "example.com"

req.ip

请求的IP地址。

如果trust proxy设置的是enabled,返回的就是上游地址;获取更多信息,请看Express behind proxies

req.ip
//=>"127.0.0.1"

req.ips

当trust proxy设置为true是,这个属性包含在“X-Forwarded-For”请求头中的ip地址数组,否则它包含的就是一个空数组。

例如,如果“X-Forwarded-For”是“client,proxy1,proxy2”,req.ips就是["client","proxy1","proxy2"],proxy2就是最下游的ip

获取更多关于trust proxy的设置,请看app.set

req.originalUrl

req.url不是一个原生的Express属性,他是从Node的http模块中继承的。

这个属性很像req.url,但是它保留了原始的请求URL,允许你为了路由目的自由重写req.url。例如,app.use的“mounting”特性会重写req.url去脱离挂载点

//GET /search?q=something
req.originalUrl
//=>"/search?q=something"

req.params

一个包含匹配路由名为“parameters”属性的对象,例如,如果你有路由为“/user/:name”,name属性存在于req.params.name中,这个对象默认是{}

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

当你使用一个正则表达式定义路由时,用req.params[n]会提供capture groups,当n是第n个capture group时,这个规则适用于没有命名的字符串路径比如/file/*

//GET /file/javascript/jquery.js
req.params[0]
//=>"javascript/jquery.js"

req.path

包含请求URL的路径

//example.com/users?sort=desc
req.path
//=>"/users"

当从一个中间件中调用的时候,req.path不包含加载点,获取更多详细信息请看app.user()

req.protocol

请求协议的字符串。当通过TLS(安全传输层协议)请求时是“http”或者是“https”。当“trust proxy”设置为trusts the socket address,“X-Forwarded-Proto”头(http或者https)区域的值将会被信任如果存在的话。

req.protocol
//=>"http"

req.query
一个包含在路由中每个查询参数属性的对象,如果没有查询字符串,这就是一个空对象{}

//GET /search?q=tobi+ferret
req.query.q
//=>"tobi ferret"

//GET /shoes?order=desc&shoe[color]=blue&shoe[type]=converse
req.query.order
//=>"desc"

req.query.shoe.color
//=>"blue"

req.query.shoe.type
//=>"converse"

req.route

目前匹配的路由的字符串,例如

app.get("/user/:id?",function(req,res){
    console.log(req.route);
    res.send("GET");
})

以上代码输出:

{ path: '/user/:id?', 
  stack: 
    [ { handle: [Function: userIdHandler], 
        name: 'userIdHandler', 
        params: undefined, 
        path: undefined,
        keys: [], 
        regexp: /^\/?$/i, 
        method: 'get' } ], 
  methods: { get: true } }

req.secure

如果一个TLS连接已经建立这是布尔值true,相等于:

"https"==req.protocol

req.signedCookies

当使用cooki-parser中间件时,这个属性包含请求发送的已经签署的cookies,签署的cookies在一个表明开发者意图的对象。否则,恶意攻击很容易被置于req.cookie(很容易欺骗)中。签署一个cookie时并不是使它隐藏或者加密,仅仅是防止篡改(因为签署的密码是私有的)。如果发送的是一个没有签署的cookies,这个属性默认就是{}

//Cookie:user=tobi.CP7AWaXDfAKIRfH49dQzKJx7sKzzSoPq7/AcBBRVwlI3
req.signedCookies.user
//=>"tobi"

获取更多信息、问题、关注,请看cooki-parser

req.stale

表明请求是否是"stale",它是req.stale的相反面。获取更多信息,请看req.fresh

req.stale
//=>true

req.subdomains

请求中域名的子域名组成的数组

//Host:"tobi.ferrets.example.com"
req.subdomains
//=>["ferrets","tobi"]

req.xhr

如果请求“X-Requested-With”头字段是“XMLHttpRequest”,表明请求是由客户端发送的例如jQuery

req.xhr
//=>true
方法

req.accepts(types)

基于请求的Accept HTTP头字段,检查指定的内容类型是否可接受。这个方法返回最佳匹配或者如果指定的类型不可接受,则返回undefined(在这种情况下,应用程序响应为406"Not Acceptable")。

type值可以是一个单独的MIME类型的字符串(比如"application/json"),也可以是一个拓展名比如"json",一个逗号分隔的列表,或者一个数组。对于一个列表或是数组,这个方法返回最佳匹配(如果有的话)。

//Accept:text/html
req.accepts("html");
//=>"html"

//Accept:text/*,application/json
req.accepts("html");
//=>"html"
req.accepts("text/html");
//=>"text/html"
req.accepts(["json","text"]);
//=>"json"
req.accepts("application/json");
//=>"application/json"

//Accept:text/*;q=.5,application/json
req.accepts("image/png");
req.accept("png");
//=>undefined

//Accept:text/*;q=.5,application/json
req.accepts(["html","json"]);
//=>"json"

获取更多信息,或者你有问题或者关注,请看accepts

req.acceptsCharsets(charset[,...])

基于请求的“Accept-Charset” HTTP头字段,返回指定字符集的最初受理的字符。如果指定的字符没有可接受的,则返回false。

获取更多信息,或者你有问题或者关注,请看accepts

req.acceptsEncodings(encoding[,...])

基于请求的“Accept-Charset” HTTP头字段,返回指定编码的最初受理的编码。如果指定的编码没有可接受的,就返回false。

获取更多信息,或者你有问题或者关注,请看accepts

req.acceptsLanguages(lang[,...])

基于请求的“Accept-Charset” HTTP头字段,返回指定语言的最初受理的语言。如果指定的语言没有可接受的,就返回false。

获取更多信息,或者你有问题或者关注,请看accepts

req.get(field)

返回指定的HTTP请求头字段(不区分大小写)。Referrer和Referer字符是相同的。

req.get("Content-Type");
//=>"text/plain"

req.get("content-type");
//=>"text/plain"

req.get("Something");
//=>undefined

这个方法的别名是req.header(field)。

req.is(type)

如果即将到来的请求的“Content-Type”HTTP头字段与指定的MIME类型匹配,则返回true,否则返回false

//With Content-Type:text/html;charset=utf-8
req.is("html");
req.is("text/html");
req.is("text/*");
//=>true

//When Content-Type is application/json
req.is("json");
req.is("application/json");
req.is("application/*");
//=>true

req.is("html");
//=>false

获取更多信息,或者你有问题或者关注,请看type-is

req.param(name[,defaultValue])

已经过时,请使用req.params,req.body或者req.query代替。

返回name参数的现有值。

//?name=tobi
req.param("name")
//=>"tobi"

//POST name=tobi
req.param("name")
//=>"tobi"

// /user/tobi for /user/:name
req.param("name")
//=>"tobi"

查找按以下顺序进行:

  • req.params

  • rea.body

  • req.query

可选:如果参数在任何的请求对象中没有找到,你可以为其指定一个默认值。

直接访问应该使用req.body,req.params和req.query。

Body-parsing中间件应该为req.param加载以正常工作,查看req.body获取更详细的信息。

四、Response

当收到一个请求时,res对象代表Express应用程序发送的HTTP响应。

按照惯例,在这份文档中用res代替这个对象(HTTP request是req),但是它真正的名字是由工作的回调函数中的参数决定的。

例如:

app.get("/user/:id",function(req,res){
    res.send("user "+req.params.id);
});

但是你也可以像这样:

app.get("/user/:id",function(request,response){
    response.send("user "+ request.params.id);
});  
属性

res.app

这个属性持有一个正在使用中间件的Express应用程序的实例。

res.app和request对象中的req.app属性是一样的。

res.headersSent

一个布尔值表明应用程序是否发送HTTP响应头

app.get("/",function(req,res){
    console.log(res.headerSent);//false
    res.send("OK");
    console.log(res.headersSent);//true
})

res.locals

一个包含请求作用域的响应的本地变量,因此仅适用于在该请求/响应周期(如果存在)的渲染视图中。否则,这个属性和app.locals相同。

这个属性对于暴露请求级的信息很有用,比如请求的路径名、认证的用户、用户的设置等。

aap.use(function(req.res){
    res.locals.user=req.user;
    res.locals.authenticated=!req.user.anonymous;
    next();
})
方法

res.append(field[,value])

res.append()在Express v4.11.0版本之后才支持。

如果响应头还没有设置好,给HTTP响应头字段设置指定的值,它以特定的值创建头部。value可以是一个字符串或者是数组。

注意:在res.append()之后调用res.set()会重置之前设置好的头部值。

res.append("Link",["<http://localhost/>","<http:/localhost:3000>"]);
res.append("Set-Cookie","foo=bar;Path=/;HttpOnly");
res.append("Warning","199 Miscellaneous warning");

res.attachment([filename])

将HTTP响应头字段"Content-Disposition"为"attachment".如果filename已经给了,并基于后缀名通过res.type()设置了Content-Type,设置了Content-Disposition“filename=”parameter

res.attachment();
//Content-Disposition:attachment

res.attachment("path/to/logo.png");
//Content-Disposition:attachment;filename="logo.png"
//Content-Type:image/png

res.cookie(name,value[,options])

设置cookie的键值对,value参数可以是一个字符串或者是一个转化为JSON的对象。
options参数是一个可以具有以下属性的对象。

所有res.cookie()都没有用提供的选项设置HTTP Set-Cookie头部。任何选项未指定默认为RFC6265规定的值。

例如:

res.cookie("name","tobi",{damin:".example.com",path:"/admin",secure:true});
res.cookie("rememberme","1",{expires:new Date(Date.now()+900000),httpOnly;true});

maxAge选项是一个设置到期时间的简便选项,这个到期时间是相对于现在以毫秒为单位的时间。下面的例子相当于上面的第二个例子

res.cookie("rememberme","1",{maxAge:900000,httpOnly:true});

你可以传递一个对象给value参数,它会被序列化为JSON然后被bodyParser()中间件解析。

res.cookie("cart",{items:[1,2,3]});
res.cookie("cart",{items:[1,2,3]},{maxAge:900000});

当使用cookie-parser中间件的时候,这个方法也支持签署的cookie。仅仅需要吧signed选项设置为true,然后res.cookie()会传递密码给cookieParser(secret)去签署这个值

res.cookie("name","tobi",{signed:true});

然后可以通过req.signedCookie对象访问这个值。

res.clearCookie(name[,options])

通过那么清除指定的cookie,获取options对象更详细的内容,请看res.cookie()。

res.cookie("name","tobi",{path:"/admin"});
res.clearCookie("name",{path:"/admin"});

res.download(path[,filename][,fn])

将path路径上的文件传递为"attachment",一般情况下,浏览器会提示用户下载。默认情况下,Content-Disposition头“filename=”参数是path(这一般会在浏览器的弹出框中出现)。通过filename参数覆盖这个默认值。

当有错误出现或者传递完成时,这个方法会调用可选的回调函数fn,这方法用res.sendFile()传递文件

res.download("/report-12345.pdf");

res.download("/report-12345.pdf","report.pdf");

res.download("/report-12345.pdf","report.pdf",function(err){
    if(err){
        //Handle error, but keep in mind the response may be partially-sent
        //so check res.headersSent
    }else{
        //decrement a download credit, etc.
    }
});

res.end([data][,encoding])

终止响应进程。这个方法实际上来自于Node内核,具体在response.end() method of http.ServerResponse.
没有数据用这个方法可以迅速终止响应。如果你想要通过数据响应,用其他方法如res.send()和res.json()代替。

res.end();
res.status(404).end();

res.format(object)

在请求体中对Accept HTTP 头执行content-negotiation,如果存在的话。它用res.accepts()对请求选择一个处理方法,通过值的顺序基于可接受类型。如果头部没有指定,第一个回调函数会被调用。如果没有找到匹配的,服务器返回响应406“Not Acceptable”,或者调用默认的回调函数。
当一个回调函数已经选择了,Content-Type响应头就已经设置了。但是你可能会在回调函数中用方法比如res.set()或者res.type()。
当Accept头字段设置为"application/json"或者"/json"时,下面的例子会响应{"message":"hey"}(如果是"/*",响应会是"hey")。

res.format({
    "text/plain":function(){
        res.send("hey");
    },
    "text/html":function(){
        res.send("<p>hey</p>");
    },
    "applocation/json":function(){
        res.send({message:"hey"});
    },
    "default":function(){
        //log the request and respond with 406
        res.status(406).send("Not Acceptable");
    }
});

除了规范化的MIME类型,你也可以使用拓展名:

res.format({
    text:function(){
        res.send("hey");
    },

    html:function(){
        res.send("<p>hey</p>")
    },

    json:function(){
        res.send({message:"hey"});
    }
});

res.get(field)

根据field返回指定的HTTP响应头,匹配时忽略大小写

res.get("Content-Type");
//=>"text/plain"

res.json([body])

发送一个JSON响应。这个方法与res.send()方法使用对象或者数组作为参数是相同的。但是,你可以用这个方法将其他值转化为JSON,比如null和undefined(虽然这些从技术上来说不是有效的JSON)

res.json(null);
res.json({user:"tobi"});
res.status(500).json({error:"message"})

res.jsonp([body])

发送带有JSONP支持的JSON响应。这个方法和res.json()方法是相同的。除了它采用JSONP回调支持。

res.jsonp(null)
//=>null

res.jsonp({user:"tobi"})
//=>{"user":"tobi"}

res.status(500).jsonp({error:"message"})
//=>{"error":"message"}

默认情况下,JSONP回调名字仅仅是callback,通过jsonp callback name设置覆盖设置。

下面是使用相同的代码的JSONP响应的例子:

//?callback=foo
res.jsonp({user:"tobi"})
//=>foo({"user":"tobi"})

app.set("jsonp callback name","cb");

//?cb=foo
res.status(500).jsonp({error:"message"})
//=>foo({"error":"message"})

res.links(links)

连接提供的links作为参数的属性填入响应的Link HTTP头字段。

res.links({
    next:"http://api.example.com/users?page=2",
    last:"http://api.example.com/users?page=5"
})

输出:

Link:<http://api.example.com/users?page=2>;rel="next",
          <http://api.example.com/user?page=5>;rel="last"

res.location(path)

根据指定的path参数响应的Location HTTP响应头。

res.location("/foo/bar");
res.location("htttp://example.com");
res.location("back");

path值"back"有特殊的含义,它代指请求中的Referer请求头中指定的URL。如果Referer头没有指定,它指"/"。

没有任何验证或者操作,Express在Location头传递指定的URL字符串给浏览器,除了back情况。

浏览器从当前的URL或者引用的URL中派生出将要前往的URL,URL在Location头中指定,因此用户会重定向。

res.redirect([status,]path)

根据指定的paht进行重定向和指定的HTTP状态码。如果你没有指定status,状态码就是默认的302Found

res.redirect("/foo/bar");
res.redirect("http://example.com");
res.redirect(301,"http://example.com");
res.redirect("../login");

重定向可以是一个完整的URL重定向值一个不同的网站

res.resirect("http://google.com");

重定向可以是主机名的根目录的相对路径。例如,如果应用程序在<code>http://example.com/admin/post/new</code>下面的例子会重定向至<code>http://example.com/admin</code>

res.redirect("/admin");

重定向也可以是当前URL的相对路径,例如从路径<code>http://example.com/blog/admin/</code>(注意斜杠),下面的例子会重定向至<code>http://example.com/blog/admin/post/new</code>

res.resdirect("post/new");

从<code>http://example.com/blog/admin</code>(没有斜杠)重定向post/new会重定向至<code>http://example.com/blog/post/new</code>。

如果你发现上面的有些疑惑,就想想目录中的分隔(通过斜杠),这就会清楚。

相对路径重定向也有可能。如果你在<code>http://example.com/admin/post/new</code>,下面的例子就会重定向至<code>http://example.com/admin/post</code>

res.resirect("..");

back重定向至之前的rederer,当引用不见之时,默认为/

res.redirect("back");

res.render(view[,locals][,callback])

渲染一个视图并且将渲染好的HTML发送给客户端,可选的参数:

  • locals,一个对象,在本地定义的为视图使用的变量作为这个对象的属性

  • callback一个回调函数,如果提供,这个函数返回可能的错误和渲染的字符串,但是不自动执行响应。当一个错误出翔,这个方法内部调用next(err)。

本地变量cache允许视图缓存。把它设置为true,在开发环境中允许视图缓存,视图缓存在生产环境中默认使用。

//send the rendered view to the client
res.render("index");

//if a callback is specified, the rendered HTMLstring has to be sent explicitly
res.render("index",function(err,html){
    res.send("html");
});

//pass a local variable to the view
res.render("user",{name:"Tobi"},function(err,html){
    //...
});

res.send([body])

发送HTTP响应。

这个body对象可以是一个Buffer对象,一个字符串,一个对象,一个数组。例如:

res.send(new Buffer("whoop"));
res.sendf({some:"json"});
res.send("<p>some html</p>");
res.status(404).send("Sorry, we cannot find that!");
res.status(500).send({error:"something blew up:});

这个方法可以执行很多有用的简单不需要流的响应任务:例如,它可以自动指定Content-Length HTTP响应头字段(除非提前定义好了),提供自动的HEAD和高速缓存支持。

当参数是一个Buffer对象时,这个方法设置Content-Type响应头字段为"application/octet-stream",除非像下面一样提前定义好:

res.set("Content-Type","text/html");
res.send(new Buffer("<p>some html</p>"));

当参数是字符串时,这个方法设置"Content-Type"为"text/html":

res.send("<p>some html</p>");

当参数是一个数组或者是对象时,Express响应JSON格式:

res.send({user:"tobi"});
res.send([1,2,3]);

res.sendFile(path[,options][,fn]])

res.sendFile()在Express v4.8.0及之后版本支持。

传送文件给给定的path,基于文件名的后缀名设置Content-Type HTTP响应头。除非root选项在可选的对象中设置,path必须是文件的绝对路径。

options对象的详细信息在下面的表格中列出:

当传输完成或者出现错误的时候,这个方法会调用回调函数fn(err)。如果这个回调函数指定并且有错误发生,这个回调函数必需明确地指出解决方法,要么是终止request-response循环。要么把控制权传递给下一个路由。

下面是一个使用res.sendFile并包含所有参数的例子:

app.get("/file/:name",function(req,res,,next){
    
    var options={
        root:__dirname+"/public/",
        dotfiles:"deny",
        headers:{
            "x-timestamp":Date.now(),
            "x-sent":true
        }
    };

    var filename=req.params.name;
    res.sendFile(fileName,options,function(err){
        if(err){
            console.log(err);
            res.status(err.status).end();
        }
        else{
            console.log("Sent: ",fileName);
        }
    });

})

res.sendFile为文件服务提供了很详细的支持,下面的例子就像图示一样:

app.get("/user/:uid/photo/:file",function(req,res){
    var uid=req.params.uid,
          file=req.params.file;
    req.user.mayViewFilesFrom(uid,function(yes){
        if(yes){
            res.sendFile("/uploads/"+uid+"/"+file);
        }else{
            res.status(403).send("Sorry!you can't see that.");
        }
    });
})

获取更多信息,或者你有问题和关注,请看send

res.sendStatus(statusCode)

给statusCode设置HTTP状态码,并且发送其字符串代表响应主体。

res.sendStatus(200);//equivalent to res.status(200).send("OK")
res.sendStatus(403);//equivalent to res.status(403).send("Forbidden")
res.sendStatus(404);//equivalent to res.status(404).send("Not found")
res.sendStatus(500);//equivalent to res.status(500).send("Internal Server Error")

如果指定了一个不支持的状态码,这个状态码仍然会设置在statusCode中并且这个状态码以字符串形式发送给响应主体。

res.sendStatus(2000);//equivalent to res.status(2000).send("2000")

更多关于HTTP Status Codes

res.set(field[,value])

给HTPP响应头field设置为value值。一次性设置多个值,请传递一个对象做参数。

res.set("Content-Type","text/plain");

res.set({
    "Content-Type":"text/plain",
    "content-Length":"123",
    "ETag":"12345"
})

别名是res.header(field[,value])

res.status(code)

用这个方法设置HTTP响应状态码,这是一个可以链式调用的Node的response.statusCode的别名。

res.status(403).end();
res.status(400).send("Bad Request");
res.status(404).sendFile("/absolute/path/to/404.png");

res.type(type)

设置指定MIME类型的Content-Type HTTP响应头。如果type中包含"/"字符,则其设置Content-Type为type

res.type(".html");        //=>"text/html"
res.type("html");         //=>"text/html"
res.type("json");          //=>"application/json"
res.type("application/json")        //"application/sjon"
res.type("png");          //=>image/png

res.vary(field)

添加响应字段,如果这个字段不存在的话

res.vary("User-Agent").render("docs");

Router

一个router是一个中间件和路由器的独立实例。

你可以把它想象成一个迷你的应用程序,仅仅能够执行中间件和路由方法。每一个Express应用程序都有一个内置的app路由器。

一个路由器就像一个中间件一样,你可以把它作为app.use()的参数或者是另一个路由器use()方法的参数。
顶级的Express对象有一个Router()方法用来创建router对象。

Router([options])

像下面一样创建一个新的路由器:

var router=express.Router([options]);

可选的options参数指定路由器的行为:

你可以像应用程序一样给路由器添加中间件和HTTP方法(比如:get,put,post等等)

//invoked for any requests passed to this router
router.use(function(req,res,next){
    //.. some logic here .. like any other middleware
    next();
}):

//will handle any request that ends in /events
//depends on where the router is "use()"
router.get("/events",function(req,res,next){
    // ..
});

然后你可以用这种方法给特殊的root URL添加路由器,将其分成文件或者mini-app。

//only requests to /calendar/* will be sent to our "router"
app.use("/calendar",router)
方法

router.all(path,[callback,...]callback)

这个方法就像router.METHOD()方法,除了它匹配所有的HTTP方法。

对于特定的路径前缀或者是任意匹配映射全局逻辑。例如,你把下面的内容放在所有路由定义的前面,它要求所有从这个点开始的路由都需要加载认证和自动加载一个用户,请记住这些回调函数并不一点是终点,loadUser可以像一个任务一样,然后使用next()方法继续匹配接下来的路由。

router.all("*",requireAuthentication,loadUser);

相等于:

router.all("*",requireAuthentication);
router.all("*",loadUser);

另一个例子就是使用全局函数,这个例子很像前面的,但是这个把路径限制了以"/api"开头的路径

router.all("/api/*",requireAuthentication);

router,METHOD(path,[callback,...]callback)

router.METHOD()方法在Express中提供路由函数,METHOD是一个HTTP方法,比如GET,PUT,POST的小写版本等,因此实际的方法是router.get()、router.post()、router.put()等。

你可以提供很多的回调函数,就像中间件一样,除了这些回调可以调用next("route")来绕过接下来的路由回调,你可以用这种机制给一个路由设置一些前提条件,如果当前路由并不能满足条件,你可以将控制权传递给接下来的路由。
下面的代码片段展示了最简单的最可能的路由定义。Express内部将路径字符串转化为正则表达式,匹配即将到来的请求。执行这些匹配的时候,查询字符串不考虑,例如,“GET /”会匹配下面的路由,也会匹配“GET /?name=tobi”。

router.get("/",function(req,res){
    res.send("hello world");
})

你可以使用正则表达式,这在你有特定的限制的时候很有用,例如,下面代码会匹配"GET /commits/71dbb9c",也会匹配"GET /commits/71dbb9c..4c084f9"。

router.get(/^\/commit\/(\w+)(?:\.\.(\w+))?$/,function(req,res){
    var from=req.params[0];
    var to=req.params[1]  ||  "HEAD";
    res.send("commit range "+  from +".."+to);
});

router.param([name,]callback)

为路由参数添加一个触发回调函数,name是一个参数或者是一个name组成的数组,回调函数是一个方法。回调函数的参数是requset对象,response对象,next中间件和按照顺序的name的值。如果name是一个数组,回调函数触发器按照这个数组中每个参数的声明顺序进行挂载。此外,除数组中最后一个参数外,在回调函数中调用next会调用数组中下一个参数的回调函数,对于数组中的最后一个参数, 调用next会调用这个路由正在处理的下一个中间件,就像它是一个string一样例如,当:user在一个路由路径中,你可能需要匹配用户加载逻辑去自动提供req.user的路径或者展示参数输入验证。

router.param("user",function(req,res,next,id){
    //try to get the user details from the User model and attach it to request object
    User.find(id, function(err,user){
        if(err){
            next(err);
        }else if(user){
            req.user=user;
            next();
        }else{
            next(new Error("failed to load user"));
        }
    });
}): 

回调函数是他们本地定义的路由器,他们不是继承自挂接的app或者是路由器,因此,定义在app中的param回调函数只会被定义在app路由器中定义的参数触发。

所有的param回调函数都会在其他出现param的路由处理之前调用,并且再一次请求和响应循环中,他们只会被调用一次,即使这个参数匹配在很多路由中,就像下面的例子一样:

router.param("id",function(req,res,next,id){
    console.log("CALLED ONLY ONCE");
    next();
});

app.get("/user/:id",function(req,res,next){
    console.log("although this matches");
    next();
});

app.get("/user/:id",function(req,res){
    console.log("and this matches too");
    res.end();
})

On GET /user/42/3,打印出以下内容:

CALLED ONLY ONCE
although this matches
and this matches too

router.route(path)

返回一个单独路由器的实例,然后你可以用它和可选的中间件来处理HTTP操作。用app.route()避免重复的路由名字(和错字错误)。

以上面的例子为基础,下面的代码展示了如何用router.route()指定不一样的HTTP处理方法。

var router=express.Router();

router.param("user_id",function(req,res,next,id){
    //sample user, would actually fetch from DB, etc...
    req.user={
        id:id,
        name:"TJ"
     };
      next();
});

router.route("/users/:user_id").all(function(req,res,next){
    //runs for all HTTP verbs first
    //think o it as route specific middleware!
    next();
}).get(function(req,res,next){
    res.json(req.user);
}).put(function(req,res,next){
    //just an example of maybe updating the user
    req.user.name=req.params.name;
    // save user ... etc
    res.json(req.user);
}).post(function(req,res,next){
    next(new Error("not implemented"));
}).delete(function(req,res,next){
    next(new Error("not implemented"));
})

这个方法重新使用单路径"/user/:user_id"并且添加了多种HTTP处理方法。

router.use([path],[function,...]function)

使用给定的中间件function,给可选的路径path挂载,路径默认为"/"

这个方法像app.use(),一个简单的例子和使用例子如下描述,请看app.use()获取更多信息。

中间件就像一个垂直的水管,请求在你定义的第一个中间件开始,在往下工作匹配每一个中间件堆栈。

var express=require("express");
var app=express();
var router=express.Router();

//simple logger for this router's requests
//all requests to this router will first hit this middleware
router.use(function(req,res,next){
    console.log("%s %s %s",req.method,req.url,req.path);
    next();
});

//this will only be invoked if the path starts with /bar from the mount point
router.use("/bar",function(req,res,next){
    // ... maybe some additional /bar logging ...
    next();
});

//always invoked
router.use(fnction(req,res,next){
    res.send("Hello World");
});

app.use("/foo",router);
app.listen(3000);

mount路径被剥离,在中间件函数中不可见。这个的主要影响是加载中间件或许不需要更改代码操作,而不需管路径的前缀。

用router.use()定义中间件的顺序很重要,他们是按顺序调用的,因此顺序决定了优先权。例如,通常logger是你会使用的第一个中间件,所以每一个请求都有记录。

var logger=require("morgan");

router.use(logger());
router.use(express.static(__dirname+"/public"));
router.use(function(req,res){
    res.send('Hello");
});

现在设想你想要忽略对静态文件的记录,但是继续记录在logger()之后定义的路由和中间件,你仅仅需要把static()放在上面:

router.use(express.static(__dirname+"/public"));
router.use(logger());
router.use(function(req.res){
    res.send("Hello");
});

另一个具体的例子就是从很多目录中提供文件服务,给"./oublic"目录高于其他目录的优先权。

app.use(express.static(__dirname_"/public"));
app.use(express.static(__dirname_"/files"));
app.use(express.static(__dirname_"/uploads"));

router.use()方法同样支持已经命名的参数,所以你的其他路由器可以从之前会用命名的参数加载过的路由中获取便利。

最后是一个广告贴,最近新开了一个分享技术的公众号,欢迎大家关注👇

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

推荐阅读更多精彩内容