Flask學習之旅 --- 初級篇

1. Flask簡介:

flask是一款非常流行的Python Web框架,出生于2010年,作者是Armin Ronacher,本來這個項目只是作者在愚人節的一個玩笑,后來由于非常受歡迎,進而成為一個正式的項目。目前為止最新的版本是0.12.2。

flask自2010年發布第一個版本以來,大受歡迎,深得開發者的喜愛,并且在多個公司已經得到了應用,flask能如此流行的原因,可以分為以下幾點:

  • 微框架、簡潔、只做他需要做的,給開發者提供了很大的擴展性。
  • Flask和相關的依賴(Jinja2、Werkzeug)設計得非常優秀,用起來很爽。
  • 開發效率非常高,比如使用SQLAlchemyORM操作數據庫可以節省開發者大量書寫sql的時間。
  • 社會活躍度非常高。

Flask的靈活度非常之高,他不會幫你做太多的決策,即使做已經幫你做出選擇,你也能非常容易的更換成你需要的,比如:

  • 使用Flask開發數據庫的時候,具體是使用SQLAlchemy還是MongoEngine或者是不用ORM而直接基于MySQL-Python這樣的底層驅動進行開發都是可以的,選擇權完全掌握在你自己的手中。區別于DjangoDjango內置了非常完善和豐富的功能,并且如果你想替換成你自己想要的,要么不支持,要么非常麻煩。
  • 把默認的Jinija2模板引擎替換成Mako引擎或者是其他模板引擎都是非常容易的。

flask文檔

中文文檔: http://docs.jinkan.org/docs/flask/

英文文檔: http://flask.pocoo.org/docs/0.12/

2. 第一個flask程序:

pycharm新建一個flask項目,新建項目的截圖如下:


點擊create后創建一個新項目,然后在helloworld.py文件中書寫代碼:

    #coding: utf8    

    # 從flask框架中導入Flask類
    from flask import Flask

    # 傳入__name__初始化一個Flask實例
    app = Flask(__name__)

    # app.route裝飾器映射URL和執行的函數。這個設置將根URL映射到了hello_world函數上
    @app.route('/')
    def hello_world():
        return 'Hello World!'

    if __name__ == '__main__':
        # 運行本項目,host=0.0.0.0可以讓其他電腦也能訪問到該網站,port指定訪問的端口。默認的host是127.0.0.1,port為5000
        app.run(host='0.0.0.0',port=9000)

然后點擊運行,在瀏覽器中輸入http://127.0.0.1:9000就能看到hello world了。需要說明一點的是,app.run這種方式只適合于開發,如果在生產環境中,應該使用Gunicorn或者uWSGI來啟動。如果是在終端運行的,可以按ctrl+c來讓服務停止。

3. Flask創建app對象

3.1初始化參數

app = Flask()的可選參數:
import_name: 導入路徑(尋找靜態目錄與模板目錄位置的參數),一般寫__name__
static_url_path:訪問靜態資源的前綴,默認static,這個前綴目錄下都是靜態資源。
static_folder: 靜態目錄的名字,默認static,其實就是項目中項目目錄存放靜態資源文件夾的名字。
template_folder: 模板目錄的名字,默認templates, 其實就是項目中項目目錄存放模板文件文件夾的名字。

3.2 配置參數

有4種,一般使用app.config.from_pyfileapp.config.from_object,app.config[key]=valueapp.config.from_envvar是從虛擬環境中讀取配置參數(一般不用):

  1. 使用配置文件,項目里新建一個文件,把相關的配置寫到該文件中然后通過app對象的config屬性的from_pyfile 方式來導入配置文件。如在項目目錄中定義配置文件為“config.cfg”,在程序中引入配置文件方式:app.config.from_pyfile(“config.cfg”),路徑可以是相對路徑或者絕對路徑,這種方式,可以傳遞silent=True,那么這個靜態文件沒有找到的時候,不會拋出異常。
app.config.from_pyfile('config.cfg',silent=True)
# silent=True表示如果配置文件不存在的時候不拋出異常,默認是為False,會拋出異常。
  1. 從對象中導入,一般是定義一個配置參數的類(類名可以自定義,一般類名是Config),再把相關的參數配置以類屬性的方式定義,然后通過app對象的config屬性的from_ object方式來導入。如定義一個Config類:
class Config(object):  
        DEBUG = True

導入方式:app.config.from_object(Config),可以不用實例化一個Config對象,因為類也是對象,flask會自動從Config類中讀取參數

  1. 可以通過app.config[key]=value,類似于字典的方式直接把相關的配置參數加上。如:
app = Flask(__name__)
app.config['DEBUG'] = True

3.3 在視圖讀取配置參數

app.config.get()按照字典取值的方法獲取配置參數的值或者是先導入current_app再用current_app.config.get()按照字典取值的方法獲取配置參數的值

3.4 app.run的參數

啟動app可以傳參數,比如host、port,debug,如果是配置參數,配置參數只能傳debug

app.run(host="0.0.0.0", port=5000,debug=True)

4. debug模式

4.1 為什么需要開啟DEBUG模式

  1. 如果開啟了DEBUG模式,那么在代碼中如果拋出了異常,在瀏覽器的頁面中可以看到具體的錯誤信息,以及具體的錯誤代碼位置。方便開發者調試。
  2. 如果開啟了DEBUG模式,那么以后在Python代碼中修改了任何代碼,只要按ctrl+s,flask就會自動的重新記載整個網站。不需要手動點擊重新運行。

4.2 debug配置的5種方式

  1. 使用配置文件,項目里新建一個文件,把相關的配置寫到該文件中然后通過app對象的config屬性的from_pyfile方式來導入配置文件。如在項目目錄中定義配置文件為“config.cfg”,在程序中引入配置文件方式:app.config.from_pyfile(“config.cfg”),路徑可以是相對路徑或者絕對路徑,這種方式,可以傳遞silent=True,那么這個靜態文件沒有找到的時候,不會拋出異常。

  2. 從對象中導入,一般是定義一個配置參數的類(類名可以自定義,一般類名是Config),再把相關的參數配置以類屬性的方式定義,然后通過app對象的config屬性的from_ object 方式來導入。如定義一個Config類:

class Config(object):  
        DEBUG = True 

導入方式:app.config.from_object(Config),可以不用實例化一個Config對象,因為類也是對象,flask會自動從Config類中讀取參數。

  1. 可以通過app.config[key]=value,類似于字典的方式直接把相關的配置參數加上。如app.config["DEBUG"]=True

  2. 直接通過app.debug = True的方式

  3. 通過給app.config添加參數的方式(類似于添加字典),app.config.update(DEBUG = True)

4.3 PIN碼

開啟了debug模式后,flask會自動生成一個PIN碼,PIN碼用于頁面調試如果想要在網頁上調試代碼,那么應該輸入PIN碼。

5. url詳解

5.1 在局域網中讓其他電腦訪問我的網站

如果想在同一個局域網下的其他電腦訪問自己電腦上的Flask網站,那么可以在app.run()中設置host='0.0.0.0'才能訪問得到。

5.2 指定端口號

Flask項目,默認使用5000端口。如果想更換端口,那么可以在app.run()中設置port=9000

5.3 url唯一

在定義url的時候,一定要記得在最后加一個斜杠。

  1. 如果不加斜杠,那么在瀏覽器中訪問這個url的時候,如果最后加了斜杠,那么就訪問不到。這樣用戶體驗不太好。
  2. 搜索引擎會將不加斜杠的和加斜杠的視為兩個不同的url。而其實加和不加斜杠的都是同一個url,那么就會給搜索引擎造成一個誤解。加了斜杠,就不會出現沒有斜杠的情況。

5.4 GET請求和POST請求

在網絡請求中有許多請求方式,比如:GET、POST、DELETE、PUT請求等。那么最常用的就是GETPOST請求了。

  1. GET請求:只會在服務器上獲取資源,不會更改服務器的狀態。這種請求方式推薦使用GET請求。

  2. POST請求:會給服務器提交一些數據或者文件。一般POST請求是會對服務器的狀態產生影響,那么這種請求推薦使用POST請求。

  3. 關于參數傳遞:

    • GET請求:把參數放到url中,通過?xx=xxx的形式傳遞的。因為會把參數放到url中,所以如果視力好,一眼就能看到你傳遞給服務器的參數。這樣不太安全。
    • POST請求:把參數放到Form Data中。會把參數放到Form Data中,避免了被偷瞄的風險,但是如果別人想要偷看你的密碼,那么其實可以通過抓包的形式。因為POST請求可以提交一些數據給服務器,比如可以發送文件,那么這就增加了很大的風險。所以POST請求,對于那些有經驗的黑客來講,其實是更不安全的。
  4. Flask中,route方法,默認將只能使用GET的方式請求這個url,如果想要設置自己的請求方式,那么應該傳遞一個methods參數來限制訪問方式。

@app.route('/sample', methods=['GET', 'POST'])
def my_list(page):
    return 'my list'

5.5 url與函數的映射

從之前的helloworld.py文件中,我們已經看到,一個URL要與執行函數進行映射,使用的是@app.route裝飾器。@app.route裝飾器中,可以指定URL的規則來進行更加詳細的映射,比如現在要映射一個文章詳情的URL,文章詳情的URL是/article/id/,id有可能為1、2、3...,那么可以通過以下方式:

   @app.route('/article/<id>/')
   def article(id):
       return '%s article detail' % id

其中<id>,尖括號是固定寫法,語法為<variable_name>,variable_name默認的數據類型是字符串。如果需要指定類型,則要寫成<converter:variable_name>,其中converter就是類型名稱,可以有以下幾種:
string: 默認的數據類型,接受沒有任何斜杠“/”的文本。
int: 接受整形。
float: 接受浮點類型。
path: 和string的類似,但是接受斜杠。
uuid: 只接受uuid字符串。
any:可以指定多種路徑,這個通過一個例子來進行說明:

  @app.route('/<any(article,blog):url_path>/')
  def item(url_path):
      return url_path

以上例子中,item這個函數可以接受兩個URL,一個是/article/,另一個是/blog/。并且,一定要傳url_path參數,當然這個url_path的名稱可以隨便。

如果不想定制子路徑來傳遞參數,也可以通過傳統的?=的形式來傳遞參數,例如:/article?id=xxx,這種情況下,可以通過request.args.get('id')來獲取id的值。如果是post方法,則可以通過request.form.get('id')來進行獲取。

6. url_for詳解

6.1 url_for的基本使用

url_for第一個參數,應該是視圖函數的名字的字符串。后面的參數就是傳遞給url。如果傳遞的參數之前在url中已經定義了,那么這個參數就會被當成path的形式給url。如果這個參數之前沒有在url中定義,那么將變成查詢字符串的形式放到url中。

@app.route('/post/list/<page>/')
def my_list(page):
    return 'my list'

print(url_for('my_list',page=1,count=2))
# 構建出來的url:/my_list/1/?count=2

6.2 為什么需要url_for

  1. 將來如果修改了URL,但沒有修改該URL對應的函數名,就不用到處去替換URL了。
  2. url_for會自動的處理那些特殊的字符,不需要手動去處理。
  3. 強烈建議以后在使用url的時候,使用url_for來反轉url。
 url = url_for('login',next='/')
    # 會自動的將/編碼,不需要手動去處理。
    # url=/login/?next=%2F

7. 動態路由

7.1 傳遞參數

傳遞參數的語法是:/<參數名>/。然后在視圖函數中,也要定義同名的參數。

7.2 參數的數據類型

  1. 如果沒有指定具體的數據類型,那么默認就是使用string數據類型。
  2. int數據類型只能傳遞int類型。
  3. float數據類型只能傳遞float類型。
  4. path數據類型和string有點類似,都是可以接收任意的字符串,但是path可以接收路徑,也就是說可以包含斜杠。
  5. uuid數據類型只能接收符合uuid的字符串。uuid是一個全宇宙都唯一的字符串,一般可以用來作為表的主鍵。
  6. any數據類型可以在一個url中指定多個路徑。例如:
    @app.route('/<any(blog,article):url_path>/<id>/')
    def detail(url_path,id):
        if url_path == 'blog':
            return '博客詳情:%s' % id
        else:
            return '博客詳情:%s' % id
    
    # 路由傳遞的參數默認當做string處理,這里指定int,尖括號中冒號后面的內容是動態的
    @app.route('/user/<int:id>')
    def hello_itcast(id):
        return 'hello word %d' %id
    

7.3 接收用戶傳遞的參數

  1. 第一種:使用path的形式(將參數嵌入到路徑中),就是上面講的。
  2. 第二種:使用查詢字符串的方式,就是通過?key=value的形式傳遞的。
    @app.route('/d/')
    def d():
        wd = request.args.get('wd')
        return '您通過查詢字符串的方式傳遞的參數是:%s' % wd
    
  3. 如果你的這個頁面的想要做SEO優化,就是被搜索引擎搜索到,那么推薦使用第一種形式(path的形式)。如果不在乎搜索引擎優化,那么就可以使用第二種(查詢字符串的形式)。

7.4 app.url_map`查看所有路由

通過app.url_map可以查看整個flask中的路由信息

7.5 同一路由裝飾多個視圖函數

如果兩個路由的請求方式相同,那么先定義的視圖函數將會覆后定義的視圖函數,如果不同則根據請求方式來調用視圖

@app.route('/index')
def index():
    return "hello"
@app.route('/index')
def index1():
    return "hello1"

上面這種情況,在瀏覽器訪問index,只會執行第一個視圖函數,第二個視圖函數將會被覆蓋

@app.route('/index', methods=['POST'])
def index():
    return "hello"
@app.route('/index', methods=['GET'])
def index1():
    return "hello1"

上面這種方式,會根據請求方式來決定執行那個視圖

7.6 同一視圖多個路由裝飾器

裝飾器層疊就可以

@app.route('/index')
@app.route('/index1')
def index():
    return "hello"

8. 自定義URL轉換器

8.1 自定義URL轉換器的方式

  1. 轉換器是一個類,且必須繼承自werkzeug.routing.BaseConverter。
  2. 在轉換器類中,實現to_python(self,value)方法,這個方法的返回值,將會傳遞到view函數中作為參數。
  3. 在轉換器類中,實現to_url(self,values)方法,這個方法的返回值,將會在調用url_for函數的時候生成符合要求的URL形式。
  4. 在自定義的類中,重寫regex,也就是這個變量的正則表達式。
  5. 將自定義的類,映射到app.url_map.converters上。比如:
    app.url_map.converters['tel'] = TelephoneConverter
    

8.2 to_python的作用

這個方法的返回值,將會傳遞到view函數中作為參數。

8.3 to_url的作用

這個方法的返回值,將會在調用url_for函數的時候生成符合要求的url形式。

8.4 萬能轉換器

可以定義一個正則的萬能表達式,可以根據傳入的正則表達式提取相關參數:

from flask import Flask
from werkzeug.routing import BaseConverter

class Regex_url(BaseConverter):
    def __init__(self,url_map,*args):
        super(Regex_url,self).__init__(url_map)
        self.regex = args[0]

app = Flask(__name__)
app.url_map.converters['re'] = Regex_url

@app.route('/user/<re("[a-z]{3}"):id>')
def hello_itcast(id):
    return 'hello %s' %id

9. redirect重定向

9.1 重定向分類

重定向分為永久性重定向和暫時性重定向,在頁面上體現的操作就是瀏覽器會從一個頁面自動跳轉到另外一個頁面。比如用戶訪問了一個需要權限的頁面,但是該用戶當前并沒有登錄,因此我們應該給他重定向到登錄頁面。

  • 永久性重定向:http的狀態碼是301,多用于舊網址被廢棄了要轉到一個新的網址確保用戶的訪問,最經典的就是京東網站,你輸入www.jingdong.com的時候,會被重定向到www.jd.com,因為jingdong.com這個網址已經被廢棄了,被改成jd.com,所以這種情況下應該用永久重定向。

  • 暫時性重定向:http的狀態碼是302,表示頁面的暫時性跳轉。比如訪問一個需要權限的網址,如果當前用戶沒有登錄,應該重定向到登錄頁面,這種情況下,應該用暫時性重定向。

9.2 flask中重定向

flask中有一個函數叫做redirect,可以重定向到指定的頁面。示例代碼如下:

# 先導入redirect
from flask import Flask,request,redirect,url_for

app = Flask(__name__)

@app.route('/login/')
def login():
    return '這是登錄頁面'

@app.route('/profile/')
def profile():
    if request.args.get('name'):
        return '個人中心頁面'
    else:
        # redirect 重定向
        return redirect(url_for('login'))

10. response詳解

10.1 視圖函數中可以返回哪些值

  1. 可以返回字符串:返回的字符串其實底層將這個字符串包裝成了一個Response對象。
  2. 可以返回元組:這樣的元組必須是 (response, status, headers)的形式,且至少包含一個元素。 status值會覆蓋狀態代碼,headers可以是一個列表或字典,作為額外的消息標頭值。元組的形式是(響應體,狀態碼,頭部信息),也不一定三個都要寫,寫兩個也是可以的。返回的元組,其實在底層也是包裝成了一個Response對象。
  3. 可以返回Response及其子類。

10.2 實現一個自定義的Response對象

  1. 繼承自Response類。
  2. 實現方法force_type(cls,rv,environ=None)
  3. 指定app.response_class為你自定義的Response對象。
  4. 如果視圖函數返回的數據,不是字符串,也不是元組,也不是Response對象,那么就會將返回值傳給force_type,然后再將force_type的返回值返回給前端。
from flask import Flask,Response,jsonify,render_template
# flask = werkzeug+sqlalchemy+jinja2
import json

app = Flask(__name__)

# 將視圖函數中返回的字典,轉換成json對象,然后返回
# restful-api
class JSONResponse(Response):

    @classmethod
    def force_type(cls, response, environ=None):
        """
        這個方法只有視圖函數返回非字符、非元組、非Response對象
        才會調用
        response:視圖函數的返回值
        """
        if isinstance(response,dict):
            # jsonify除了將字典轉換成json對象,還將改對象包裝成了一個Response對象
            response = jsonify(response)
        return super(JSONResponse, cls).force_type(response,environ)

app.response_class = JSONResponse


@app.route('/')
def hello_world():
    # Response('Hello World!',status=200,mimetype='text/html')
    return 'Hello World!'

@app.route('/list1/')
def list1():
    resp = Response('list1')
    resp.set_cookie('country','china')
    return resp

@app.route('/list2/')
def list2():
    return 'list2',200,{'X-NAME':'zhiliao'}

@app.route('/list3/')
def list3():
    return {'username':'zhiliao','age':18}

if __name__ == '__main__':
    app.run(debug=True,port=8000)

10.3 abort函數

  1. abort() : 立即停止視圖函數的執行,并且把相對應的信息返回到前端中使用abort函數可以立即終止視圖函數的執行 并可以返回給前端特定的信息abort()函數可以傳遞兩種方式的信息,

    (1) ①:一種是傳遞狀態碼信息,例如:abort(404),前端就會報出已經默認好的404錯誤信息
    ②:自定義錯誤信息,(用裝飾器)定義一個視圖函數這個函數的返回值會是前端所看到的最終的結果,這個視圖函數的裝飾器來定義錯誤的類型

    @app.errorhandler(404)   # 定義404的錯誤類型
    

    自定義的錯誤類型,要和abort()里面定義的錯誤類型一致

    (2) 另外一種方式是傳遞響應體的信息,abort(Response("內容")),Response() ,里面的信息會在前端上面顯示出來

    代碼:404錯誤...加上自定義的錯誤信息如果使用傳遞響應體信息的話就會只有響應體信息的錯誤異常

from flask import Flask, request, abort,Response

app = Flask(__name__)


@app.route("/login", methods=["GET"])
def login():
    name = ""
    pwd = ""
    if name != "zhangsan" or pwd != "admin":
        """
            使用abort函數可以立即終止視圖函數的執行
            并可以返回給前端特定的信息
            1. 傳遞狀態碼信息,必須是標準的http狀態碼
            可以接受狀態碼信息,返回給前端已經默認好的信息
        """
        abort(404)
        """
            2. 傳遞響應體的信息, Response()里面的內容會在前端上顯示出來
            resp = Response("login failed")
            abort(resp)
        """
    return "login success"


"""自定義異常處理"""
@app.errorhandler(404)
def handle_404_error(error):  # 接受一個錯誤信息
    """自定義的處理404錯誤方法"""
    '''這個函數的返回值會是前端所看到的最終的結果'''
    return "出現了404錯誤,錯誤信息:%s"%error


if __name__ == '__main__':
    app.run(debug=True)

10.4 自定義異常處理

自定義錯誤信息,(用裝飾器)定義一個視圖函數這個函數的返回值會是前端所看到的最終的結果,這個視圖函數的裝飾器來定義錯誤的類型

@app.errorhandler(404)
def error(e):
    return '您請求的頁面不存在了,請確認后再次訪問!%s'%e

10.5 make_response()詳解

make_response(),相當于DJango中的HttpResponse
1.返回內容

from flask import make_response

@blue.route('/makeresponse/')
def make_response_function():
    response = make_response('<h2>羞羞噠</h2>')
    return response, 404

2.返回頁面

from flask import make_response

@blue.route('/makeresponse/')
def make_response_function():
    temp = render_template('hello.html')
    response = make_response(temp)
    return response
  • 注意:make_response 想要返回頁面,不能直接寫做:make_response('hello.html'),必須用render_template('hello.html')形式。

3.返回狀態碼

  • 方式一:在make_response()中傳入狀態碼
from flask import make_response

@blue.route('/makeresponse/')
def make_response_function():
    temp = render_template('hello.html')
    response = make_response(temp, 200)
    return response
  • 方式二:直接return狀態碼
from flask import make_response

@blue.route('/makeresponse/')
def make_response_function():
    temp = render_template('hello.html')
    response = make_response(temp)
    return response, 200

11. 獲取請求參數

11.1 request詳解

獲取請求參數時首先要導入request模塊

from flask import request

就是 Flask 中表示當前請求的request對象,request對象中保存了一次HTTP請求的一切信息。

11.2 request的常用屬性

  1. methods
    默認的請求方式只有GET,其他請求都需要通過參數methods進行指定。
methods=['GET', 'POST']
  1. args:獲取GET請求參數
    flask中,要獲取get請求的參數,不是通過request.GET.get(),而是通過request.args.get()獲取。
request.agrs.get('參數名')
  1. form:獲取POST請求參數
    flask中,要獲取get請求的參數,不是通過request.POST.get(),而是通過request.form.get()獲取。
request.form.get('參數名')

11.3 上傳文件

已上傳的文件存儲在內存或是文件系統中一個臨時的位置。你可以通過請求對象的files屬性訪問它們。每個上傳的文件都會存儲在這個字典里。它表現近乎為一個標準的 Python file對象,但它還有一個 save()方法,這個方法允許你把文件保存到服務器的文件系統上。這里是一個用它保存文件的例子:

from flask import request

@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        f = request.files['the_file']
        f.save('/var/www/uploads/uploaded_file.txt')
    ...

如果你想知道上傳前文件在客戶端的文件名是什么,你可以訪問filename屬性。但請記住, 永遠不要信任這個值,這個值是可以偽造的。如果你要把文件按客戶端提供的文件名存儲在服務器上,那么請把它傳遞給Werkzeug提供的secure_filename()函數:

from flask import request
from werkzeug import secure_filename

@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        f = request.files['the_file']
        f.save('/var/www/uploads/' + secure_filename(f.filename))
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,443評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,530評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,407評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,981評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,759評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,204評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,263評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,415評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,955評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,650評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,892評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,675評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,967評論 2 374