LookupWord - 用Vapor架設(shè)Swift伺服器 - 和我一起Swift

用來記錄你搜尋過的單字的伺服器

Preparation

  1. Vapor - 一個(gè)後端的Swift框架
  1. PostgreSQL - 使用的Database
  2. owlbot dictionary - 一個(gè)字典API
  3. Heroku - 可以發(fā)布Server的平臺(tái),以及提供Database

Steps

  1. 建立一個(gè)伺服器遵班,在自己電腦上運(yùn)作
  1. 處理HTTP請求
  2. owlbot字典 要查詢單字
  3. 儲(chǔ)存新增的單字脱茉,以及查詢到的單字資訊

Step 1 - 建立一個(gè)伺服器坯苹,在自己電腦上運(yùn)作

聽到要用Swift開發(fā)資料庫,不用緊張画机,在我們的這個(gè)時(shí)代,很多事情都有很好引導(dǎo),我們重要的是要學(xué)會(huì)使用現(xiàn)有的工具芋簿。跟著我一步一步完成這個(gè)伺服器吧。

哦璃饱,不只跟著我与斤,在這次關(guān)於Vapor的教程中,我會(huì)參考Ray Wenderlich的教程,每個(gè)影片基本上都在10分鐘之內(nèi)撩穿,快又有內(nèi)容的教學(xué)磷支,值得一看,但我們會(huì)做一些修改以符合我們的功能食寡。

介紹Vapor - 讓我們隨著這個(gè)影片來開始一個(gè)我們的Vapor專案吧雾狈!

首先,去到你的Terminal

vapor new LookupWord

去到該目錄

cd LookupWord 

用Vapor執(zhí)行Xcode

vapor xcode

然後稍等一下之後冻河,在對話看選擇y打開Xcode

Select the `App` scheme to run.
Open Xcode project?
y/n> y

讓我們先來看看這個(gè)Xcode的專案是不是正常運(yùn)作箍邮,重新選擇Target之後執(zhí)行??專案。

Change target and run!
Change target and run!
it works

喔耶叨叙!It's works!


Step 2 - 處理HTTP請求*

好了锭弊,現(xiàn)在我們需要來寫點(diǎn)程式了,

回到Xocde擂错,然後去到main.swift

你可以按住?Shift加上 ?Command然後按O來快速打開檔案味滞。

好的,到了main.swift之後钮呀,把程式碼變成像下面呈現(xiàn)的一樣剑鞍。

import Vapor

let drop = Droplet()

drop.get("word", String.self) { req, word in
    
        // We need to add some code later, to get the definition.

        // return a json.
    return try JSON(node: [
        "new word": word
        ])
}

drop.run()

經(jīng)過改動(dòng)之後,讓我們再看看這個(gè)程序的功能爽醋,在執(zhí)行?? 一次蚁署,

這段程式碼會(huì)處理送到0.0.0.0:8080/word/[the word you want to search]的請求。

現(xiàn)在這個(gè)階段蚂四,他會(huì)回傳給瀏覽器一個(gè)JSON格式的資訊光戈,例如:

現(xiàn)在用你的瀏覽器(Chrome)前往http://0.0.0.0:8080/word/swift
你會(huì)看到他回傳{"new word":"swift"},那這樣就遂赠,對啦久妆!

太好了,這樣我們第二部也完成了跷睦。


Step 3 - 跟owlbot字典 要查詢單字

關(guān)於如何在傳送HTTP的請求, 可以參考這裡筷弦。

在這裡,我們需要從owlbot dictionary拿到字的定義抑诸,所以我們要加一些程式碼來替代// We need to add some code later, to get the definition.烂琴,你的main.swfit`會(huì)變成:

import Vapor
import Foundation

let drop = Droplet()

drop.get("word", String.self) { req, word in

    // get the shared URLSession
    let session = URLSession.shared

    // define the URL
    let wordURLString: String = "https://owlbot.info/api/v1/dictionary/\(word)"
    guard let url = URL(string: wordURLString) else {
        print("Error: cannot create URL")
        return try JSON(node: [
            "error": "Error: cannot create URL"
            ])
    }

    // create the session task
    let task = session.dataTask(with: url, completionHandler: { (data, response, error) in

        // check for any errors
        guard error == nil else {
            print("error calling GET on /todos/1")
            print(error!)
            return
        }
        // make sure we got data
        guard let responseData = data else {
            print("Error: did not receive data")
            return
        }

        // transform to JSON object
        let json = try? JSONSerialization.jsonObject(with: responseData, options: [])
        // cast JSON to Array
        guard let jsonArray = json as? [Any] else {
            print("Error: wrong data type")
            return
        }

        // get each definition
        for jsonDict in jsonArray {

            if let wordDict = jsonDict as? [String:String]  {
                print("\n \(word) \n")
                let definition = wordDict["defenition"] ?? "no definition"
                let example = wordDict["example"] ?? "no example"
                let type = wordDict["type"] ?? "no type"

                print("definition : \(definition)")
                print("example : \(example)")
                print("type : \(type)")

            } else {
                print("Error: wrong data type")
            }

        }

    })
    task.resume()
    session.finishTasksAndInvalidate()
    
    return try JSON(node: [
        "new word": word
        ])
}


drop.run()

現(xiàn)在,再回到瀏覽器哼鬓,前往http://0.0.0.0:8080/word/success监右,然後你會(huì)看到以下的訊息出現(xiàn)在Xcode的Console裡。

GET /word/success

 success 

definition : the accomplishment of an aim or purpose.
example : "the president had some <b>success in</b> restoring confidence"
type : noun

 success 

definition : archaic
example : "the good or ill success of their maritime enterprises"
type : noun


Step 4 - 儲(chǔ)存新增的單字异希,以及查詢到的單字資訊

好了健盒!現(xiàn)在我們到了最後一個(gè)步驟了绒瘦,我們將要在本地建立一個(gè)資料庫,先測試扣癣,然後再將我們的伺服器發(fā)佈至Heroku的平臺(tái)惰帽,一個(gè)免費(fèi)提供空間給限制使用量的平臺(tái),他也會(huì)提供資料庫給我們使用父虑。

請參考vapor/postgresql

為了要先設(shè)定資料庫该酗,這邊有另一個(gè)Ray Wenderlich的教學(xué),非常有用也很清楚士嚎。

To install database, we need to open the terminal
First, install Homebrew

首先呜魄,要安裝資料庫,我們會(huì)使用到Homebrew莱衩,如果你已經(jīng)安裝過爵嗅,可以跳過這個(gè)步驟。

先開啟Terminal笨蚁,並安裝Homebrew:

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

接著確保它有最新的內(nèi)容

brew update

在安裝postgres資料庫

brew postgre

開始postgres的程式

postgres -D /usr/local/var/postgres

再用你自己的使用者名稱建立一個(gè)資料庫睹晒,whoami是一個(gè)指令將會(huì)回傳你的使用者名稱

createdb `whoami`

好了,讓我們來看看資料庫建立好了沒

psql

如果你看到以下資訊括细,那就沒問題啦

psql (9.6.1)
Type "help" for help.

lee=#

輸入\q離開資料庫存取程式伪很。

所以現(xiàn)在,你已經(jīng)有一個(gè)資料庫在你的電腦上工作了奋单,酷吧锉试!


接著要做幾件事情,來設(shè)定Vapor去使用這個(gè)資料庫:

  1. Import他的相關(guān)程序包
  2. 設(shè)置Droplet使用這個(gè)資料庫
  3. 建立一個(gè)配置的檔案

First, check out the provider page

讓我們?nèi)タ匆幌耉aporprovider的頁面

複製這個(gè)連結(jié)加入Package.swift的檔案裡:

.Package(url: "https://github.com/vapor/postgresql-provider", majorVersion: 1, minor: 0)

所以你的Package.swift會(huì)看起來像是:

import PackageDescription

let package = Package(
    name: "LookupWord",
    dependencies: [
        .Package(url: "https://github.com/vapor/vapor.git", majorVersion: 1, minor: 3),
        .Package(url: "https://github.com/vapor/postgresql-provider", majorVersion: 1, minor: 0)
    ],
    exclude: [
        "Config",
        "Database",
        "Localization",
        "Public",
        "Resources",
        "Tests",
    ]
)

然後我們需要重新生成一次Xcode的相依性览濒,所以在執(zhí)行一次以下命令

vapor xcode

為了要設(shè)定Droplet使用它键痛,我們再一次打開main.swift,然後import VaporPostgreSQL

import VaporPostgreSQL

並且加上一行程式碼在let drop = Droplet()之後

try drop.addProvider(VaporPostgreSQL.Provider)

main.swift會(huì)看起來像是:

import Vapor
import VaporPostgreSQL
import Foundation

let drop = Droplet()
try drop.addProvider(VaporPostgreSQL.Provider)

// test the connection of database
drop.get("version") { req in
    if let db = drop.database?.driver as? PostgreSQLDriver {
        let version = try db.raw("SELECT version()")
        return try JSON(node: version)
    } else {
        return "No db connection"
    }

}

drop.get("word", String.self) { req, word in

    // get the shared URLSession
    let session = URLSession.shared

    // define the URL
    let wordURLString: String = "https://owlbot.info/api/v1/dictionary/\(word)"
    guard let url = URL(string: wordURLString) else {
        print("Error: cannot create URL")
        return try JSON(node: [
            "error": "Error: cannot create URL"
            ])
    }

    // create the session task
    let task = session.dataTask(with: url, completionHandler: { (data, response, error) in

        // check for any errors
        guard error == nil else {
            print("error calling GET on /todos/1")
            print(error!)
            return
        }
        // make sure we got data
        guard let responseData = data else {
            print("Error: did not receive data")
            return
        }

        // transform to JSON object
        let json = try? JSONSerialization.jsonObject(with: responseData, options: [])
        // cast JSON to Array
        guard let jsonArray = json as? [Any] else {
            print("Error: wrong data type")
            return
        }

        // get each definition
        for jsonDict in jsonArray {

            if let wordDict = jsonDict as? [String:String]  {
                print("\n \(word) \n")
                let definition = wordDict["defenition"] ?? "no definition"
                let example = wordDict["example"] ?? "no example"
                let type = wordDict["type"] ?? "no type"

                print("definition : \(definition)")
                print("example : \(example)")
                print("type : \(type)")

            } else {
                print("Error: wrong data type")
            }

        }

    })
    task.resume()
    session.finishTasksAndInvalidate()
    
    return try JSON(node: [
        "new word": word
        ])
}


drop.run()

接下來匾七,要新增一個(gè)配置檔案,

先建立在這個(gè)專案底下找到config江兢,

並在這個(gè)資料夾體下載新增一個(gè)叫做secret的資料夾昨忆,

接著新增一個(gè)檔案叫做postgresql.json,也就是我們的配置檔案杉允。

講你自己的資料替換"user"還有"database"

{
    "host": "127.0.0.1",
    "user": "Your user name",
    "password": "",
    "database": "Your user name",
    "port": 5432
}

好了邑贴,那就讓我們再來執(zhí)行一次,如果你前往瀏覽器去到以下的網(wǎng)址

http://0.0.0.0:8080/version

你會(huì)看到這個(gè)資料庫的相關(guān)版本資訊叔磷,你就可以確定你已經(jīng)讓Vapor使用這個(gè)資料庫了拢驾!

[{"version":"PostgreSQL 9.6.1 on x86_64-apple-darwin16.1.0, compiled by Apple LLVM version 8.0.0 (clang-800.0.42.1), 64-bit"}]

現(xiàn)在我們需要加上兩個(gè)Model給我們的資料庫,

前往Terminal, 然後去到你的專案的路徑改基,

接著用以下的指令新增兩個(gè)Model:

touch Sources/App/Models/Word.swift
touch Sources/App/Models/Definition.swift

接著再重新生成一次專案的相依性繁疤,透過指令:

vapor xcode

然後再把相對應(yīng)的程式碼,放到該檔案裡面:

Word.swift

import Vapor
import Fluent
import Foundation

final class Word: Model {
    var id: Node?
    var exists: Bool = false

    var word: String


    init(word: String) {
        self.id = nil
        self.word = word

    }

    init(node: Node, in context: Context) throws {
        id = try node.extract("id")
        word = try node.extract("word")

    }

    func makeNode(context: Context) throws -> Node {
        return try Node(node: [
            "id": id,
            "word": word,
            ])
    }
}

extension Word: Preparation {
    static func prepare(_ database: Database) throws {
        try database.create("words", closure: { words in
            words.id()
            words.string("word")
        })
    }

    static func revert(_ database: Database) throws {
        try database.delete("words")
    }
}

extension Word {
    func definitions() throws -> Children<Definition> {
        return children()
    }
}

Definition.swift

import Vapor
import Fluent
import Foundation

final class Definition: Model {
    var id: Node?
    var exists: Bool = false

    var word_id: Node?
    var definition: String
    var example: String
    var type: String

    init(word_id: Node,definition: String, example: String, type: String) {
        self.id = nil
        self.word_id = word_id
        self.definition = definition
        self.example = example
        self.type = type

    }

    init(node: Node, in context: Context) throws {
        id = try node.extract("id")
        word_id = try node.extract("word_id")
        definition = try node.extract("definition")
        example = try node.extract("example")
        type = try node.extract("type")

    }

    func makeNode(context: Context) throws -> Node {
        return try Node(node: [
            "id": id,
            "word_id": word_id,
            "definition": definition,
            "example": example,
            "type": type,
            ])
    }
}

extension Definition: Preparation {
    static func prepare(_ database: Database) throws {
        try database.create("definitions", closure: { definitions in
            definitions.id()
            definitions.parent(Word.self, optional: false, unique: false, default: nil)
            definitions.string("definition")
            definitions.string("example")
            definitions.string("type")
        })
    }

    static func revert(_ database: Database) throws {
        try database.delete("definitions")
    }
}

extension Definition {
    func word() throws -> Parent<Word> {
        return try parent(word_id)
    }
}

最後,讓我們的Droplet使用這兩個(gè)Model

let drop = Droplet()
try drop.addProvider(VaporPostgreSQL.Provider)
drop.preparations += Word.self
drop.preparations += Definition.self

這樣就可以了稠腊,讓我們來加上一些程式碼來測試一下躁染,把程式碼修改成下的內(nèi)容:

這邊有做一些修改,相較於前面的程式碼架忌,這邊使用drop.client去跟字典要資料

import Vapor
import VaporPostgreSQL
import HTTP

let drop = Droplet()
try drop.addProvider(VaporPostgreSQL.Provider)
drop.preparations += Word.self
drop.preparations += Definition.self


// test the connection of database
drop.get("version") { req in
    if let db = drop.database?.driver as? PostgreSQLDriver {
        let version = try db.raw("SELECT version()")
        return try JSON(node: version)
    } else {
        return "No db connection"
    }

}

//Redirect to word
drop.get() { req in

    // change to your URL
    return Response(redirect: req.uri.appendingPathComponent("word").path)
}

// Show all the words
drop.get("word") { req in
    return try JSON(node: Word.all().makeNode())
}

// Show single word
drop.get("word", String.self) { req, wordString in

    // Check if the word exist
    if let word = try Word.query().filter("word", wordString).first() {

        // if exist, show all the definition
        return try JSON(node: word.definitions().all().makeNode())

    } else {

        // create a new word and save
        var word = Word(word: wordString)
        try word.save()

        let wordDictResponse = try drop.client.get("https://owlbot.info/api/v1/dictionary/\(wordString)")

        print(wordDictResponse.json?.array ?? "no response")

        if let jsonArray = wordDictResponse.json?.array {

            for jsonDict in jsonArray {
                print(jsonDict)
                if let jsonDefinition = jsonDict as? JSON {
                    let definition = jsonDefinition["defenition"]?.string ?? "no definition"
                    let example = jsonDefinition["example"]?.string ?? " "
                    let type = jsonDefinition["type"]?.string ?? "no type"

                    //create Definition
                    var newDefinition = Definition(word_id: word.id!, definition: definition, example: example, type: type)
                    try! newDefinition.save()
                }

            }
        }
        
        return try JSON(node: word.definitions().all().makeNode())
    
    }
    
}


drop.run()

為了從我們的Server發(fā)出HTTP請求吞彤,我們需要做一些小調(diào)整,前往Config/clients.json叹放,然後將verifyHost以及verifyCertificates改成false

Warning Note: Use extreme caution when modifying these settings.

Config/clients.json

{
    "tls": {
        "verifyHost": false,
        "verifyCertificates": false,
        "certificates": "mozilla"
    }
}

Hurrah!! now go test the application again by run it and use browser the lookup a word.

好的饰恕,讓我們再一次測試一下我們的伺服器,重新執(zhí)行??井仰,並且前往瀏覽器:

輸入例如http://0.0.0.0:8080/word/happy

如果一切都正確埋嵌,他將會(huì)回傳:

[{"definition":"feeling or showing pleasure or contentment.","example":"\"Melissa came in looking happy and excited\"","id":1,"type":"adjective","word_id":1},
{"definition":"fortunate and convenient.","example":"\"he had the happy knack of making people like him\"","id":2,"type":"adjective","word_id":1},
{"definition":"informal","example":"\"they tended to be grenade-happy\"","id":3,"type":"adjective","word_id":1}]

恭喜你!糕档!代表你成功啦莉恼!

現(xiàn)在我們擁有我們所需要的功能了:

  1. 透過對我們的伺服器發(fā)送HTTP請求,查詢字的定義
  1. 我們的伺服器從owlbot dictionary拿到定義
  2. 伺服器將新的單字以及定義儲(chǔ)存在資料庫

發(fā)佈至Heroku

不過速那,雖然功能都有了俐银,但現(xiàn)在都還是在本地端執(zhí)行,在我們自己的電腦裡端仰,這樣子的話沒有辦法讓我們用手機(jī)來拿到資料捶惜,

所以我們要把它發(fā)佈到雲(yún)端,

再次借用Ray Wenderlich的發(fā)佈教學(xué)

首先荔烧,先建立Git的repository(讓我們可以記錄開發(fā)檔案的任何變動(dòng))

git init

依照Git的運(yùn)作吱七,新增所有檔案,

git add .

並且commit確認(rèn)發(fā)送鹤竭。

git commit -m "init"

最後踊餐,透過Vapor的指令,上傳到Heroku

vapor heroku init

維持一切預(yù)設(shè)的設(shè)定臀稚,所以被詢問是否要修改都回答N

Would you like to provide a custom Heroku app name?
y/n>n
https://boiling-ocean-81373.herokuapp.com/ | https://git.heroku.com/boiling-ocean-81373.git

Would you like to provide a custom Heroku buildpack?
y/n>n
Setting buildpack...
Are you using a custom Executable name?
y/n>n
Setting procfile...
Committing procfile...
Would you like to push to Heroku now?
y/n>n
You may push to Heroku later using:
git push heroku master
Don't forget to scale up dynos:
heroku ps:scale web=1

接著吝岭,建立雲(yún)端的資料庫

heroku addons:create heroku-postgresql:hobby-dev

執(zhí)行以下指令:

heroku config

會(huì)按看到資料庫的相關(guān)地址,待會(huì)會(huì)用到這個(gè)地址吧寺。

DATABASE_URL: postgres://yfghktvrmwrael:6e48ccb331711093e9ee11bc89d2ef49db4d2bde8a9b596f7b5275e8fb2c3bfc@ec2-107-20-149-243.compute-1.amazonaws.com:5432/d5cds2laqgtqqu

接著窜管,設(shè)定Procfile檔案裡面,

vi Procfile

i來插入編輯

新增這些資訊在最後

--config:servers.default.port=$PORT --config:postgresql.url=$DATABASE_URL

整個(gè)檔案會(huì)看起來像是這樣:

web: App --env=production --workdir="./"
web: App --env=production --workdir=./ --config:servers.default.port=$PORT --config:postgresql.url=$DATABASE_URL

最後回到Xcode

編輯Config/secret/postgresql.json如下:

根據(jù)你拿到的地址稚机,

{
    "url": "postgres://yfghktvrmwrael:6e48ccb331711093e9ee11bc89d2ef49db4d2bde8a9b596f7b5275e8fb2c3bfc@ec2-107-20-149-243.compute-1.amazonaws.com:5432/d5cds2laqgtqqu"
}

然後回到Terminal

commit所以變動(dòng):

git add .
git commit -m "modified Procfile"

最後的最後幕帆,發(fā)佈至Heroku

git push heroku master

接著,等待...


經(jīng)過一段漫長時(shí)間等待之後赖条,讓我們來測試一下失乾!

remote: -----> Compressing...
remote:        Done: 64.2M
remote: -----> Launching...
remote:        Released v10
remote:        https://[your app].herokuapp.com/ deployed to Heroku
remote:
remote: Verifying deploy... done.
To https://git.heroku.com/[your app].git
   1b544e4..6a8e4df  master -> master

前往你拿到的網(wǎng)址https://[your app].herokuapp.com/

恭喜你3N酢!仗扬!你現(xiàn)在有一個(gè)自己的伺服器症概,會(huì)記錄所有你問他的單字,

感謝你早芭,和我一起Swift彼城。

更多相關(guān)資訊,請前往我的Blog

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末退个,一起剝皮案震驚了整個(gè)濱河市募壕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌语盈,老刑警劉巖舱馅,帶你破解...
    沈念sama閱讀 217,084評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異刀荒,居然都是意外死亡代嗤,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評論 3 392
  • 文/潘曉璐 我一進(jìn)店門缠借,熙熙樓的掌柜王于貴愁眉苦臉地迎上來干毅,“玉大人,你說我怎么就攤上這事泼返∠醴辏” “怎么了?”我有些...
    開封第一講書人閱讀 163,450評論 0 353
  • 文/不壞的土叔 我叫張陵绅喉,是天一觀的道長渠鸽。 經(jīng)常有香客問我,道長柴罐,這世上最難降的妖魔是什么徽缚? 我笑而不...
    開封第一講書人閱讀 58,322評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮革屠,結(jié)果婚禮上猎拨,老公的妹妹穿的比我還像新娘。我一直安慰自己屠阻,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評論 6 390
  • 文/花漫 我一把揭開白布额各。 她就那樣靜靜地躺著国觉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪虾啦。 梳的紋絲不亂的頭發(fā)上麻诀,一...
    開封第一講書人閱讀 51,274評論 1 300
  • 那天痕寓,我揣著相機(jī)與錄音,去河邊找鬼蝇闭。 笑死呻率,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的呻引。 我是一名探鬼主播礼仗,決...
    沈念sama閱讀 40,126評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼逻悠!你這毒婦竟也來了元践?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,980評論 0 275
  • 序言:老撾萬榮一對情侶失蹤童谒,失蹤者是張志新(化名)和其女友劉穎单旁,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體饥伊,經(jīng)...
    沈念sama閱讀 45,414評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡象浑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了琅豆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片愉豺。...
    茶點(diǎn)故事閱讀 39,773評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖趋距,靈堂內(nèi)的尸體忽然破棺而出粒氧,到底是詐尸還是另有隱情,我是刑警寧澤节腐,帶...
    沈念sama閱讀 35,470評論 5 344
  • 正文 年R本政府宣布外盯,位于F島的核電站,受9級特大地震影響翼雀,放射性物質(zhì)發(fā)生泄漏饱苟。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評論 3 327
  • 文/蒙蒙 一狼渊、第九天 我趴在偏房一處隱蔽的房頂上張望箱熬。 院中可真熱鬧,春花似錦狈邑、人聲如沸城须。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽糕伐。三九已至,卻和暖如春蘸嘶,著一層夾襖步出監(jiān)牢的瞬間良瞧,已是汗流浹背陪汽。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留褥蚯,地道東北人挚冤。 一個(gè)月前我還...
    沈念sama閱讀 47,865評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像赞庶,于是被迫代替她去往敵國和親训挡。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評論 2 354

推薦閱讀更多精彩內(nèi)容