Servlet

1.什么是Servlet?

sun公司制订的一种用来扩展web服务器功能的组件规范。

(1)用来扩展web服务器功能:

早期很多web服务器(比如apache,iis等)只能够处理静态资源的请求(需要事先将html文档准备好),
不能够处理动态资源的请求(即需要通过计算,生成html),所以需要扩展。
注:早期使用CGI(Common Gateway Interface通用网关接口)程序来扩展,因为CGI开发繁琐、并且不好移值,所以用得少了。
可以使用Servlet来扩展web服务器功能。

(2)组件规范:

a.什么是组件?
    符合规范、实现部分功能,并且需要部署到相应的容器当中才能运行的软件模块。
    Servlet就是一个符合Servlet规范的组件,需要部署到Servlet容器当中才能运行。

b.什么是容器?
    符合规范,提供组件的运行环境的程序。Servlet容器(比如Tomcat雄猫)为Servlet提供运行环境(主要是提供网络相关的服务)。

2.如何写一个Servlet?

step1. 写一个java类,实现Servlet接口或者继承HttpServlet类。

注: 一般继承HttpServlet类更方便。 

step2. 编译。 .java--->.class

step3. 打包。

建立一个具有如下结构的目录结构
    appname (应用名,自定义)
        WEB-INF 
            classes (放.class文件)
            lib (放.jar文件,可选)
            web.xml(部署描述文件,servlet3.0以上的版本可以不要)

step4.部署。

将step3创建好的整个文件夹拷贝到容器上。
注:也可以将step3创建好的整个文件夹使用jar命令压缩成".war"为后缀的文件,然后再拷贝。  

step5.启动容器,访问Servlet。

打开浏览器,在地址栏输入http://ip:port/appname/url-pattern
注:url-pattern是一个字符串,在web.xml中设置。

3.安装Tomcat并且与Eclipse集成。

4.Servlet是如何运行的。

比如,在浏览器地址栏输入http://ip:port/day01/hello?number=1

image.png

step1. 浏览器依据ip和port,建立连接。
step2. 浏览器创建请求数据包并发送。
step3.服务器解析请求数据包,并且将解析到的数据存放到request 对象里面,同时,创建response对象。
step4. 服务器创建Servlet对象,然后调用该对象的service方法。
注:服务器会将request和response作为参数传递给service方法。
step5.服务器从response对象中获取处理结果,然后创建响应数据包并 发送。
step6.浏览器解析响应数据包,然后依据解析到的数据生成相应的页面。

5.常见的错误

(1)404

含义: 404是一个状态码,表示服务器依据请求路径找不到对应的资源。
错误原因:

a.请求路径写错(没有按照http://ip:port/appname/url-pattern)来写请求地址。
b.没有部署该应用或者部署失败。

(2)500

含义: 表示服务器处理出错。
错误原因: a.没有严格按照规范来写代码。 比如没有继承HttpServlet,或者web.xml写错。

b.代码不严谨。
比如,对请求参数值没有做检查就做类型转换。

6. 如何获得请求参数值?

(1) String request.getParameter(String paramName);

注:
    请求参数名得与实际发送过来的请求参数名一致,如果不一致,会获得null值。
    文本输入框、密码输入框如果不填写任何数据,会获得""。

(2) String[] request.getParameterValues(String paramName);

注:
    当有多个请求参数名相同时,使用该方法。 对于多选框,如果用户没有选择任何选项,会获得null值。

练习

写一个Servlet(比如DateServlet),输出当前的系统日期

http://ip:port/day01-lab/date 返回 2018-12-25

提示

step1.创建一个maven工程,注意以下三点(分别如下图所示):

image.png
image.png
image.png

step2.在src/main/java下,添加一个java类(DateServlet),参考代码如下:

public class DateServlet extends HttpServlet{

@Override
protected void service(
        HttpServletRequest request, 
        HttpServletResponse response) 
                throws ServletException, 
                IOException {
    //获得系统时间
    Date date = new Date();
    //创建日期格式化对象
    SimpleDateFormat sdf = 
            new SimpleDateFormat(
                    "yyyy-MM-dd");
    //将日期格式化
    String dateInfo = 
            sdf.format(date);
    //设置响应头
    response.setContentType("text/html");
    PrintWriter out = 
            response.getWriter();
    //输出日期
    out.println(dateInfo);
    out.close();
    }
}

step3.在web.xml中,添加servlet的配置信息,参数配置如下:

<servlet>
    <servlet-name>dateServlet</servlet-name>
    <servlet-class>web.DateServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>dateServlet</servlet-name>
    <url-pattern>/date</url-pattern>
</servlet-mapping>

step4.运行,步骤如下图所示:

image.png
注:
    eclipse会将servlet编译成.class文件,然后在servlet容器上创建符合servlet规范的文件夹,将.class文件添加到WEB-INF下。
    也就是说eclipse会帮我们部署整个应用。

step5.打开浏览器,在地址栏填写访问地址

image.png

1. http协议

(1) 什么是http协议?

http协议:是一种网络应用层协议、规定了浏览器与web服务器之间如何通信及相应的数据包的结构。

TCP/IP: 传输层与网络层协议,负责将数据包可靠地传递。http协议需要依赖TCP/IP来传递数据包。
image.png

(2)数据包的结构

1)请求数据包

请求行 (请求方式 请求资源路径  协议/版本)
若干消息头:
       消息头是一些键值对(使用": "隔开),用来传递一些特定的信息。如,浏览器可以发送"user-agent"消息头,告诉服务器,浏览器的类型和版本。
实体内容(消息正文):
       只有当请求方式为post时,才会有数据。

2)响应数据包


状态行 (协议/版本 状态码 状态描述):
        状态码是一个三位数字,表示服务器处理请求的一种状态,如下一些:
        200  正常
        500  服务器处理出错
        404  依据请求路径找不到对应的资源
若干消息头:
    服务器也可以发送一些消息头给浏览器,比如,发送"content-type",消息头,告诉浏览器,服务器返回的数据类型和编码。
实体内容:
    程序处理的结果,浏览器会解析出来,生成对应的页面。

(3)两种请求方式

1)get请求

a.哪一些情况下,浏览器会发送get请求?

a1.在浏览器地址直接填写某个地址
a2.点击链接
a3.表单默认的提交方式

b.特点:

b1.会将请求参数显示在浏览器地址栏,不安全。
注:有一些网络设备,比如路由器,会记录所有的请求地址。

b2.会将请求参数添加到请求资源路径的后面(即请求行里面),只能提交少量数据给服务器。
注:因为请求行大约只能存放2k左右的数据。

2)post请求

a.不会将请求参数显示在浏览器地址栏,相对安全一些。
http协议不会对数据包中的数据加密。所以,对于敏感数据 (比如帐号密码),需要加密处理(使用https协议)。
b.会将请求参数放到实体内容里面,可以提交大量的数据给服务器。

2.Servlet输出中文,如何处理?

(1)为什么会有乱码?

因为out.println方法在输出时,默认使用"iso-8859-1"来编码。

(2)如何解决?

response.setContentType("text/html;charset=utf-8");

3.表单包含有中文参数值,如何处理?

(1)为什么会有乱码?

当提交表单时,浏览器会对表单中的中文参数值进行编码,比如 使用"utf-8"来编码,而服务器端默认会使用"iso-8859-1"来解码。 所以会产生乱码。

注:浏览器会按照打开该表单所在的页面时的字符集来进行编码。

(2)如何处理?

1)post请求

request.setCharacterEncoding(String charset);

这行代码要添加到所有的request.getParameter方法的最前面。这行代码只针对post请求有效。

2)get请求

修改server.xml,添加 <Connector URIEncoding="utf-8"/>
注:
    只针对get请求有效。 tomcat8.0以上的版本,默认会使用utf-8来解码。

4.访问数据库

create database jsd1809db default character set utf8;

use jsd1809db;

create table t_user(
    id int primary key auto_increment,
    username varchar(50) unique,
    password varchar(30),
    email varchar(50)
);

step1.导包 (在pom.xml文件添加如下内容)

<dependencies>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.6</version>
    </dependency>

    <dependency>
        <groupId>commons-dbcp</groupId>
        <artifactId>commons-dbcp</artifactId>
        <version>1.4</version>
    </dependency>
</dependencies>

step2.添加DBUtils类(可以从jdbc02工程直接拷贝过来)

public class DBUtils {

private static BasicDataSource dataSource;

static{
    //读取属性配置文件的对象
    Properties prop = new Properties();
    //得到文件输入流
    InputStream ips = DBUtils.class.getClassLoader()
            .getResourceAsStream("jdbc.properties");
    //把文件流加载到prop对象中
    try {
        prop.load(ips);
        String driver = prop.getProperty("driver");
        String url = prop.getProperty("url");
        String username = prop.getProperty("username");
        String password = prop.getProperty("password");
        String initSize = prop.getProperty("initSize");
        String maxSize = prop.getProperty("maxSize");
        System.out.println(driver+url+username+password+initSize+maxSize);
        //创建连接池数据源对象
        dataSource = new BasicDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        //设置连接池策略信息
        dataSource.setInitialSize(Integer.parseInt(initSize));
        dataSource.setMaxActive(Integer.parseInt(maxSize));

        } catch (IOException e) {
        e.printStackTrace();
        }
    }
    public static Connection getConn() 
                throws SQLException{
        return dataSource.getConnection();
    }
}

step3.添加jdbc.properties文件

    driver=com.mysql.jdbc.Driver
    url=jdbc:mysql://localhost:3306/jsd1809db?useUnicode=true&characterEncoding=UTF-8
    username=root
    password=root
    initSize=3
    maxSize=3

step4.测试DBUtils,看能否获得连接。

    public class Test2 {

        public static void main(String[] args)
                throws SQLException {
            System.out.println(
                    DBUtils.getConn());
        }

    }

step5.在Servlet类当中,使用jdbc api访问数据库。

1.重定向

(1)什么是重定向?

服务器通知浏览器重新向某个地址发送请求。

注:
    服务器可以通过发送302状态码及Location消息头(该消息头的值是一个地址,一般称之为重定向地址)给浏览器,
浏览器收到之后会立即向重定向地址发送请求。
image.png

(2)如何重定向?

response.sendRedirect(String url);

注:
    url是重定向地址。重定向之前,容器会清空response对象上存放的所有数据。

(3)重定向的特点?

a.重定向地址是任意的。
b.重定向之后,浏览器地址栏的地址会发生变化。

2. jsp

(1)什么是jsp?

sun公司制订的一种服务器端的动态页面技术规范。

注:
    因为虽然可以使用servlet生成动态页面,但是过于繁琐(需要使用out.println输出),
并且不利于页面的维护(比如,修改页面就必须修改java代码),所以,sun才制订了jsp规范。
jsp是一个以".jsp"为后缀的文件,该文件主要内容是html(包括css,js)及少量的java代码,
容器会将jsp转换成一个对应的servlet然后执行。也就是说,jsp的本质就是一个servlet。

(2)如何写一个jsp文件?

step1. 添加一个以.jsp为后缀的文件。

step2. 可以在该文件里面,使用如下元素:

1)html (css,js)

直接写即可。

2)java代码

a. java代码片断

<%  java代码  %>

b. jsp表达式

<%= java表达式 %>

3)隐含对象

a. 什么是隐含对象?

在jsp里面,可以直接使用的对象,比如out、request、response。

b.为什么可以直接使用这些隐含对象?

因为容器会生成获得这些对象的代码。

4)指令

a.什么是指令?

可以使用指令来告诉容器,在将jsp转换成servlet时做一些额外的处理,比如导包。

b.语法

<%@ 指令名 属性=值 属性=值%>
注:
    属性可以有多个,属性之间使用空格隔开。

c.page指令

import属性:导包,比如 <%@ page import="java.util.*"%>
<%@ page import="java.util.*,java.text.*"%>

contentType属性:设置response.setContentType方法的值。
pageEncoding属性:设置jsp文件的编码。告诉容器,在读取jsp文件的内容时,使用指定的字符集来解码。

image.png

(3)jsp是如何执行的?

step1. 容器会将.jsp文件转换成一个.java文件(将jsp转换成servlet)

html(css,js) ----->  在service方法里面,使用out.write输出。
注(了解):write方法会将null转换成""输出,而println方法会输出null。

<%     %>    ------>  照搬到service方法里面。
<%=    %>    ------>  在service方法里面,使用out.print输出。

step2.容器调用该servlet。

容器会将servlet编译,然后实例化,并调用service方法。

1.转发

(1)什么是转发?

一个web组件将未完成的处理交给另外一个web组件继续做。

注:
    web组件(servlet/jsp)最常见的情况:一个Servlet获得数据,然后将这些数据转发给一个jsp来展现。
image.png

(2)如何转发?

step1. 将数据绑订到request对象上。

request.setAtribute(String name,Object obj);
注:底层的实现 map.put(name,obj);

step2. 获得转发器。

RequestDispatcher rd =  request.getRequestDispatcher(String uri);
注:
RequestDispatcher是一个接口。转发的本质就是一个web组件通知容器去调用另外一个web组件,
可以将RequestDispatcher(转发器)当作是一个媒介。

step3. 转发

rd.forward(request,response);

(3)特点

a.转发之后,浏览器地址栏的地址不变。
b.转发的地址有限制(要求属于同一个应用)。

2. include指令

(1)用法

<%@ include file="header.jsp"%>
注:
    容器在将jsp转换成servlet时,会将file属性所指定的文件的内容插入到该指令所在的位置。
    被包含的文件并没有真正执行,只是负责提供内容。被包含的文件类型可以是其它的,比如包含一个html文件。

1.比较转发与重定向

(1)浏览器地址栏的地址有无变化?

转发之后,浏览器地址栏的地址不变,重定向会变。

(2)目的地(地址)有无限制?

转发有限制(要求属于同一个应用),重定向地址无任何限制。

(3)能否共享request对象?

转发可以,重定向不行。

注:
    当容器收到请求之后,会立即创建request对象和response对象,当容器发送响应之后,会立即销毁这两个对象。
也就是,request和response的生存时间是一次请求和响应期间存在。

(4)一件事是否做完?

转发是一件没有做完,让另外一个web组件继续做;重定向是一件事已经完成,然后再做另外一件独立的事件。

2.状态管理

(1)什么是状态管理?

将浏览器与web服务器之间多次交互当做一个整体来处理,并且将多次交互 所涉及的数据(即状态)保存下来。

(2)如何进行状态管理?

a.将状态保存在浏览器端(Cookie)。

b.将状态保存在服务器端(Session)。

(3)Cookie

1)什么是Cookie?

服务器临时保存在浏览器端的少量数据,用于保存用户的状态。当浏览器第一次访问服务器,
服务器会将少量数据以set-cookie消息头的形式发送给浏览器,浏览器会将这些数据临时保存下来。
当浏览器再次访问服务器时,会将这些数据以cookie消息头的形式发送给服务器。
image.png

2)如何添加Cookie?

Cookie c = new Cookie(String name,String value);
注:Cookie必须有一个名字,值必须是一个字符串。
response.addCookie(c);

3)如何读取浏览器发送过来的Cookie?

Cookie[] request.getCookies();
注:
    该方法的作用是用来获得浏览器发送过来的所有cookie, 一个Cookie对象封装了cookie的所有信息。该方法有可能返回null。
String cookie.getName();
String cookie.getValue();

4)Cookie的生存时间

默认情况下,浏览器会将cookie保存在内存里面。浏览器关闭,cookie会被删除。
可以调用setMaxAge方法来设置Cookie的生存时间。
cookie.setMaxAge(int seconds);
注:
    单位是秒。比如要保存一个月 
        cookie.setMaxAge(30 *24 * 60 * 60);
    值 >0:
        浏览器会将cookie保存在硬盘上,超过指定时间,cookie会被删除。   
    值 <0:
        默认值(浏览器会将cookie保存在内存里面)
    值 =0:
        立即删除cookie。比如,要删除一个名称为"username"的cookie:
        Cookie c = new Cookie("username","");
        c.setMaxAge(0);
        response.addCookie(c);

5)Cookie的编码问题

a.什么是Cookie的编码问题?

Cookie只能存放合法的ascii字符,如果是非asicc字符(比如中文),需要转换成合法的ascii字符的形式。

b.如何处理?

String URLEncoder.encode(String str,String charset);
String URLDecoder.decode(String str,String charset);        

c.建议,在添加cookie时,统一使用encode方法来编码。

6)Cookie的路径问题

a.什么是Cookie的路径问题?

浏览器在向服务器发请求时,会比较请求路径是否与cookie的路径匹配,只有匹配的cookie才会被发送。

b.Cookie的默认路径

默认等于添加该cookie的web组件的路径,比如/day06/biz01/addCookie.jsp添加了一个cookie,则该cookie的默认路径是"/day06/biz01"。

c.匹配规则

请求路径要么等于cookie的路径,要么是其子路径,符合这个条件的
cookie会被发送。
比如  cookie的路径是"/day06/biz01"
    /day06/findCookie1.jsp   no
    /day06/biz01/findCookie2.jsp   yes
    /day06/biz01/aaa/findCookie3.jsp  yes
image.png

d.修改cookie的路径

cookie.setPath(String path);

写一个servlet,统计用户访问该servlet的次数。

image.png

1.Cookie的限制

a.可以被用户禁止
b.不安全,对于敏感数据,一定要加密。
c.只能存放少量数据,大约4k左右
d.数量也有限制,浏览器大约能存放几百个cookie
e.只能存放字符串

2. Session(会话)

(1)什么是Session?

服务器端为了保存用户的状态而创建的一个特殊的对象(即session对象)。

    当浏览器第一次访问服务器时,服务器会创建session对象(该对象有一个唯一的id,一般称之为sessionId),
接下来服务器会将sessionId以cookie的方式发送给浏览器。
    当浏览器再次访问服务器时,会将sessionId发送过来,服务器就可以依据sessionId找到对应的sessinon对象。

(2)如何获得session对象?

1)HttpSession s = request.getSession(boolean flag);

HttpSession是一个接口。
a.当flag为true时,先查看请求当中有没有sessionId,如果没有,就会创建一个session对象。
如果有sessionId,就会依据sessionId去查找对应的session对象,如果找到了就返回该对象,找不到则会创建一个新的session对象。
b.当flag为false时,先查看请求当中有没有sessionId,如果没有,返回null。
如果有sessionId,就会依据sessionId去查找对应的session对象,如果找到了就返回该对象,找不到,返回null。
image.png

2)HttpSession s = request.getSession();

等价于 request.getSession(true);

(3)常用方法

session.setAttribute(String name,Object obj);
Object session.getAttribute(String name);
session.removeAttribute(String name);
image.png

(4)session超时

a.什么是session超时?

服务器会将空闲时间过长的session对象删除掉。注:为了节省内存空间。大部分服务器默认的超时时间长度为30分钟。

b.如何修改超时时间长度?

方式一 修改web.xml
    <session-config>
        <session-timeout>30</session-timeout>
    </session-config>
方式二 编程的方式
    session.setMaxInactiveInterval(int seconds);
    设置两次请求之间最大的时间间隔

(5)删除session

session.invalidate(); 

(6)session验证

step1. 登录成功之后,在session对象上绑订一些数据,比如

session.setAttribute("user",user);

step2.当用户访问需要保护的资源时,进行session验证,比如

Object obj = session.getAttribute("user");
if(obj == null){
    //没有登录
    response.sendRedirect("login.jsp");
}

image.png

(7)比较session与cookie

session相对于cookie,优点:安全、可以存放大量的数据、支持更丰富的数据类型。
缺点是session会占用服务器端的内存空间,如果session对象过多,会占用过多的内存空间。

1.路径问题

(1)什么是路径问题?

<a href="addUser.jsp"> 
<form action="add"> 
response.sendRedirect("login.jsp")
request.getRequestDispatcher("listUsers.jsp")

(2)什么是相对路径?

不以"/"开头的路径

(3)什么是绝对路径?

以"/"开头的路径

(4)如何写绝对路径?

链接、表单提交、重定向从应用名开始写,转发从应用名之后开始写。

注:不要将应用名直接写在路径里面,而应该使用下面的方法
来获得实际部署时的应用名。
String request.getContextPath();

2.容器如何处理请求资源路径?

比如,在浏览器地址栏输入http://ip:port/day08-2/abc.html

step1.容器默认认为访问的是一个servlet。

容器将请求资源路径("/day08-2/abc.html")中的应用名除掉,
得到"/abc.html",然后查找web.xml配置文件,看<url-pattern>
有没有与之匹配的servlet。

匹配方式有三种:
    第一种: 精确匹配 
    第二种: 通配符匹配,即使用"*"配置任意的零个或者多个字符,比如
         <url-pattern>/*</url-pattern>
         <url-pattern>/demo/*</url-pattern>
    第三种:后缀匹配,使用"*."开头,后接任意的一个后缀,比如
         <url-pattern>*.do</url-pattern>
         以上配置,会匹配所有以".do"结尾的请求。
         <url-pattern>*.action</url-pattern>

step2.如果找不到对应的servlet,容器会查找对应位置的文件。

    找到了,就返回该文件的内容,找不到,返回404。

3.如何让一个servlet处理多种请求?

step1. 使用后缀匹配,比如

<servlet-mapping>
    <servlet-name>someServlet</servlet-name>
    <url-pattern>*.do</url-pattern>
</servlet-mapping>

step2. 分析请求资源路径,调用对应的分支来处理。

4.Servlet上下文

(1)什么是Servlet上下文?

容器启动之后,会为每一个web应用创建唯一的一个符合ServletContext接口要求的对象,该对象一般称之为Servlet上下文。

特点:
    唯一性:一个web应用对应一个Servlet上下文。
    持久性: 只要容器没有关闭,应用没有被卸载,Servlet上下文就会一直存在。

(2)如何获得Servlet上下文?

HttpSession,GenericServlet(HttpServlet的父类)提供了getServletContext方法来获得上下文。

(3)作用: 绑订数据

request,session,Servlet上下文都提供了绑订数据相关的方法, 区别如下:

生存时间不一样,在满足使用条件的情况下,优先使用生命周期短的(节省内存)。
request < session < servlet上下文,这三种方式提供了在不同范围内传递数据的机制,具体使用取决于您的需求。
如果需要在一次请求中传递数据,使用HttpServletRequest;
如果需要在整个用户会话期间共享数据,使用HttpSession;
如果需要在整个应用程序范围内共享数据,使用ServletContext。
image.png
b.可访问的范围不一样。
image.png

5.利用Servlet上下文读取全局的初始化参数

step1.配置全局的初始化参数

<!--配置全局的初始化参数 -->
<context-param>
    <param-name>company</param-name>
    <param-value>北京达内垃圾有限公司</param-value>
</context-param> 

step2.读取

/*
 * 通过ServletContext提供的方法来
 * 读取全局的初始化参数
 */
String company =  sctx.getInitParameter("company");

1.Servlet线程安全问题

(1)为什么说Servlet会有线程安全问题?

Servlet是线程不安全的。因为Servlet是(单例模式)的,当不同的请求都调用这一个Servlet,只在堆里申请了一个对象空间,即只实例化了一次。如果多个请求在同一时刻出现,就会并发执行,因而导致线程不安全。

(2)如何解决?

1.添加synchronized锁,将我们有并发问题的代码转化为同步代码,但是synchronized锁是一个线程访问时,其它线程只能等待上一个线程操作完后获取锁对象。遇到高并发的情况下,用户可能会因为排队问题造成等待时间较长的问题,影响用户体验,需权衡使用。

2.使用局部变量,多线程并不会共享局部变量,每个用户的每个请求都会调用service方法,而局部变量在service方法中,每一次都是新的空间

3.使用线程安全的集合类:如果必须使用实例变量或者共享资源,可以使用Java提供的线程安全的集合类(如ConcurrentHashMap、CopyOnWriteArrayList等)来确保多线程访问的安全性。

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