Spring Boot Web開發(fā)快速入門

使用驗證注解來實現(xiàn)表單驗證

雖說前端的h5和js都可以完成表單的字段驗證,但是這只能是防止一些小白铸豁、誤操作而已。如果是一些別有用心的人菊碟,是很容易越過這些前端驗證的节芥,有句話就是說永遠(yuǎn)不要相信客戶端傳遞過來的數(shù)據(jù)。所以前端驗證之后逆害,后端也需要再次進行表單字段的驗證头镊,以確保數(shù)據(jù)到后端后是正確的、符合規(guī)范的魄幕。本節(jié)就簡單介紹一下相艇,在SpringBoot的時候如何進行表單驗證。

首先創(chuàng)建一個SpringBoot工程梅垄,其中pom.xml配置文件主要配置內(nèi)容如下:

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

創(chuàng)建一個pojo類厂捞,在該類中需要驗證的字段上加上驗證注解输玷。代碼如下:

package org.zero01.domain;

import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;

public class Student {

    @NotNull(message = "學(xué)生名字不能為空")
    private String sname;

    @Min(value = 18,message = "未成年禁止注冊")
    private int age;

    @NotNull(message = "性別不能為空")
    private String sex;

    @NotNull(message = "聯(lián)系地址不能為空")
    private String address;

    public String toString() {
        return "Student{" +
                "sname='" + sname + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                ", address='" + address + '\'' +
                '}';
    }

    ... getter setter 略 ...
}

創(chuàng)建一個Controller類:

package org.zero01.controller;

import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.zero01.domain.Student;

import javax.validation.Valid;

@RestController
public class StudentController {

    @PostMapping("register.do")
    public Student register(@Valid Student student, BindingResult bindingResult){
        if (bindingResult.hasErrors()) {
            // 打印錯誤信息
            System.out.println(bindingResult.getFieldError().getDefaultMessage());
            return null;
        }
        return student;
    }
}

啟動運行類,代碼如下:

package org.zero01;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SbWebApplication {

    public static void main(String[] args) {
        SpringApplication.run(SbWebApplication.class, args);
    }
}

使用postman進行測試靡馁,年齡不滿18歲的情況:


image.png

控制臺打印結(jié)果:

未成年禁止注冊

非空字段為空的情況:


image.png

控制臺打印結(jié)果:

學(xué)生名字不能為空

使用AOP記錄請求日志

我們都知道在Spring里的兩大核心模塊就是AOP和IOC欲鹏,其中AOP為面向切面編程,這是一種編程思想或者說范式臭墨,它并不是某一種語言所特有的語法赔嚎。

我們在開發(fā)業(yè)務(wù)代碼的時候,經(jīng)常有很多代碼是通用且重復(fù)的胧弛,這些代碼我們就可以作為一個切面提取出來尤误,放在一個切面類中,進行一個統(tǒng)一的處理结缚,這些處理就是指定在哪些切點織入哪些切面损晤。

例如,像日志記錄红竭,檢查用戶是否登錄尤勋,檢查用戶是否擁有管理員權(quán)限等十分通用且重復(fù)的功能代碼,就可以被作為一個切面提取出來茵宪。而框架中的AOP模塊最冰,可以幫助我們很方便的去實現(xiàn)AOP的編程方式,讓我們實現(xiàn)AOP更加簡單稀火。

本節(jié)將承接上一節(jié)暖哨,演示一下如何利用AOP實現(xiàn)簡單的http請求日志的記錄。首先創(chuàng)建一個切面類如下:

package org.zero01.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

@Aspect
@Component
public class HttpAspect {

    private static final Logger logger = LoggerFactory.getLogger(HttpAspect.class);

    @Pointcut("execution(public * org.zero01.controller.StudentController.*(..))")
    public void log() {
    }

    @Before("log()")
    public void beforeLog(JoinPoint joinPoint) {
        // 日志格式:url method clientIp classMethod param
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        logger.info("url = {}", request.getRequestURL());
        logger.info("method = {}", request.getMethod());
        logger.info("clientIp = {}", request.getRemoteHost());
        logger.info("class_method = {}", joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
        logger.info("param = {}", joinPoint.getArgs());
    }

    @AfterReturning(returning = "object", pointcut = "log()")
    public void afterReturningLog(Object object) {
        // 打印方法返回值內(nèi)容
        logger.info("response = {}", object);
    }
}

使用PostMan訪問方式如下:


image.png

訪問成功后凰狞,控制臺輸出日志如下:


image.png

如此篇裁,我們就完成了http請求日志的記錄。


封裝統(tǒng)一的返回數(shù)據(jù)對象

我們在控制器類的方法中服球,總是需要返回各種不同類型的數(shù)據(jù)給客戶端茴恰。例如,有時候需要返回集合對象斩熊、有時候返回字符串、有時候返回自定義對象等等伐庭。而且在一個方法里可能會因為處理的結(jié)果不同粉渠,而返回不同的對象。那么當(dāng)一個方法中需要根據(jù)不同的處理結(jié)果返回不同的對象時圾另,我們應(yīng)該怎么辦呢霸株?可能有人會想到把方法的返回類型設(shè)定為Object不就可以了,的確是可以集乔,但是這樣返回的數(shù)據(jù)格式就不統(tǒng)一去件。前端接收到數(shù)據(jù)時,很不方便去展示,后端寫接口文檔的時候也不好寫尤溜。所以我們應(yīng)該統(tǒng)一返回數(shù)據(jù)的格式倔叼,而使用Object就無法做到這一點了。

所以我們需要將返回的數(shù)據(jù)統(tǒng)一封裝在一個對象里宫莱,然后統(tǒng)一在控制器類的方法中丈攒,把這個對象設(shè)定為返回值類型即可,這樣我們返回的數(shù)據(jù)格式就有了一個標(biāo)準(zhǔn)授霸。那么我們就來開發(fā)一個這樣的對象吧巡验,首先新建一個枚舉類,因為我們需要把一些通用的常量數(shù)據(jù)都封裝在枚舉類里碘耳,以后這些數(shù)據(jù)發(fā)生變動時显设,只需要修改枚舉類即可。如果將這些常量數(shù)據(jù)硬編碼寫在代碼里就得逐個去修改了辛辨,十分的難以維護敷硅。代碼如下:

package org.zero01.enums;

public enum ResultEnum {

    UNKONW_ERROR(-1, "未知錯誤"),
    SUCCESS(0, "SUCCESS"),
    ERROR(1, "ERROR"),
    PRIMARY_SCHOOL(100, "小學(xué)生"),
    MIDDLE_SCHOOL(101, "初中生");

    private Integer code;
    private String msg;

    ResultEnum(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public Integer getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }
}

然后就是創(chuàng)建我們的返回數(shù)據(jù)封裝對象了,在此之前愉阎,我們需要先定義好這個數(shù)據(jù)的一個標(biāo)準(zhǔn)格式绞蹦。我這里定義的格式如下:

{
    "code": 0,
    "msg": "注冊成功",
    "data": {
        "sname": "Max",
        "age": 18,
        "sex": "woman",
        "address": "湖南"
    }
}

明確了數(shù)據(jù)的格式后,就可以開發(fā)我們的返回數(shù)據(jù)封裝對象了榜旦。新建一個類幽七,代碼如下:

package org.zero01.domain;

import org.zero01.enums.ResultEnum;

/**
 * @program: sb-web
 * @description: 服務(wù)器統(tǒng)一的返回數(shù)據(jù)封裝對象
 * @author: 01
 * @create: 2018-05-05 18:03
 **/
public class Result<T> {

    // 錯誤/正確碼
    private Integer code;
    // 提示信息
    private String msg;
    // 返回的數(shù)據(jù)
    private T data;

    private Result(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    private Result(Integer code) {
        this.code = code;
    }

    private Result(Integer code, String msg, T data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

    private Result() {
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public static <T> Result<T> createBySuccessResultMessage(String msg) {
        return new Result<T>(ResultEnum.SUCCESS.getCode(), msg);
    }

    public static <T> Result<T> createBySuccessCodeResult(Integer code, String msg) {
        return new Result<T>(code, msg);
    }

    public static <T> Result<T> createBySuccessResult(String msg, T data) {
        return new Result<T>(ResultEnum.SUCCESS.getCode(), msg, data);
    }

    public static <T> Result<T> createBySuccessResult() {
        return new Result<T>(ResultEnum.SUCCESS.getCode());
    }

    public static <T> Result<T> createByErrorResult() {
        return new Result<T>(ResultEnum.ERROR.getCode());
    }

    public static <T> Result<T> createByErrorResult(String msg, T data) {
        return new Result<T>(ResultEnum.ERROR.getCode(), msg, data);
    }

    public static <T> Result<T> createByErrorCodeResult(Integer errorCode, String msg) {
        return new Result<T>(errorCode, msg);
    }

    public static <T> Result<T> createByErrorResultMessage(String msg) {
        return new Result<T>(ResultEnum.ERROR.getCode(), msg);
    }
}

接著修改我們之前的注冊接口代碼如下:

@PostMapping("register.do")
public Result<Student> register(@Valid Student student, BindingResult bindingResult) {
    if (bindingResult.hasErrors()) {
        return Result.createByErrorResultMessage(bindingResult.getFieldError().getDefaultMessage());
    }
    return Result.createBySuccessResult("注冊成功", student);
}

使用PostMan進行測試,數(shù)據(jù)正常的情況:


image.png

學(xué)生姓名為空的情況:


image.png

如上溅呢,可以看到澡屡,返回的數(shù)據(jù)格式都是一樣的,code字段的值用于判斷是一個success的結(jié)果還是一個error的結(jié)果咐旧,msg字段的值是提示信息碳褒,data字段則是存儲具體的數(shù)據(jù)。有這樣一個統(tǒng)一的格式后礁苗,前端也好解析這個json數(shù)據(jù)嗽上,我們后端在寫接口文檔的時候也好寫了。


統(tǒng)一異常處理

一個系統(tǒng)或應(yīng)用程序在運行的過程中伊约,由于種種因素姚淆,肯定是會有拋異常的情況的。在系統(tǒng)出現(xiàn)異常時屡律,由于服務(wù)的中斷腌逢,數(shù)據(jù)可能會得不到返回,亦或者返回的是一個與我們定義的數(shù)據(jù)格式不相符的一個數(shù)據(jù)超埋,這是我們不希望出現(xiàn)的問題搏讶。所以我們得進行一個全局統(tǒng)一的異常處理佳鳖,攔截系統(tǒng)中會出現(xiàn)的異常,并進行處理媒惕。下面我們用一個小例子來做為演示系吩。

例如,現(xiàn)在有一個業(yè)務(wù)需求如下:

  • 獲取某學(xué)生的年齡進行判斷吓笙,小于10淑玫,拋出異常并返回“小學(xué)生”提示信息,大于10且小于16面睛,拋出異常并返回“初中生”提示信息絮蒿。

首先我們需要自定義一個異常,因為默認(rèn)的異常構(gòu)造器只接受一個字符串類型的數(shù)據(jù)叁鉴,而我們返回的數(shù)據(jù)中有一個code土涝,所以我們得自己定義個異常類。代碼如下:

package org.zero01.exception;

/**
 * @program: sb-web
 * @description: 自定義異常
 * @author: 01
 * @create: 2018-05-05 19:01
 **/
public class StudentException extends RuntimeException {

    private Integer code;

    public StudentException(Integer code, String msg) {
        super(msg);
        this.code = code;
    }

    public Integer getCode() {
        return code;
    }
}

新建一個 ErrorHandler 類幌墓,用于全局異常的攔截及處理但壮。代碼如下:

package org.zero01.handle;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.zero01.domain.Result;
import org.zero01.enums.ResultEnum;
import org.zero01.exception.StudentException;

/**
 * @program: sb-web
 * @description: 全局異常處理類
 * @author: 01
 * @create: 2018-05-05 18:48
 **/
// 定義全局異常處理類
@ControllerAdvice
// Lombok的一個注解,用于日志打印
@Slf4j
public class ErrorHandler {

    // 聲明異常處理方法常侣,傳遞哪一個異常對象的class蜡饵,就代表該方法會攔截哪一個異常對象包括其子類
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public Result exceptionHandle(Exception e) {
        if (e instanceof StudentException) {
            StudentException studentException = (StudentException) e;
            // 返回統(tǒng)一的數(shù)據(jù)格式
            return Result.createByErrorCodeResult(studentException.getCode(), studentException.getMessage());
        }
        // 打印異常日志
        log.error("[系統(tǒng)異常]{}", e);
        // 返回統(tǒng)一的數(shù)據(jù)格式
        return Result.createByErrorCodeResult(ResultEnum.UNKONW_ERROR.getCode(), "服務(wù)器內(nèi)部出現(xiàn)未知錯誤");
    }
}

注:我這里使用到了Lombok,如果對Lombok不熟悉的話胳施,可以參考我之前寫的一篇Lombok快速入門

在之前的控制類中溯祸,增加如下代碼:

@Autowired
private IStudentService iStudentService;

@GetMapping("check_age.do")
public void checkAge(Integer age) throws Exception {
    iStudentService.checkAge(age);
    age.toString();
}

我們都知道具體的邏輯都是寫在service層的,所以新建一個service包舞肆,在該包中新建一個接口焦辅。代碼如下:

package org.zero01.service;

public interface IStudentService {
    void checkAge(Integer age) throws Exception;
}

然后新建一個類,實現(xiàn)該接口椿胯。代碼如下:

package org.zero01.service;

import org.springframework.stereotype.Service;
import org.zero01.enums.ResultEnum;
import org.zero01.exception.StudentException;

@Service("iStudentService")
public class StudentService implements IStudentService {

    public void checkAge(Integer age) throws StudentException {
        if (age < 10) {
            throw new StudentException(ResultEnum.PRIMARY_SCHOOL.getCode(), ResultEnum.PRIMARY_SCHOOL.getMsg());
        } else if (age > 10 && age < 16) {
            throw new StudentException(ResultEnum.MIDDLE_SCHOOL.getCode(), ResultEnum.MIDDLE_SCHOOL.getMsg());
        }
    }
}

完成以上的代碼編寫后筷登,就可以開始進行測試了。age < 10 的情況:

image.png

age > 10 && age < 16 的情況:

image.png

age字段為空哩盲,出現(xiàn)系統(tǒng)異常的情況:


image.png

因為我們打印了日志前方,所以出現(xiàn)系統(tǒng)異常的時候也會輸出日志信息,不至于我們無法定位到異常:


image.png

從以上的測試結(jié)果中可以看到种冬,即便拋出了異常镣丑,我們返回的數(shù)據(jù)格式依舊是固定的,這樣就不會由于系統(tǒng)出現(xiàn)異常而返回不一樣的數(shù)據(jù)格式娱两。


單元測試

我們一般會在開發(fā)完項目中的某一個功能的時候,就會進行一個單元測試金吗。以確保交付項目時十兢,我們的代碼都是通過測試并且功能正常的趣竣,這是一個開發(fā)人員基本的素養(yǎng)。所以本節(jié)將簡單介紹service層的測試與controller層的測試方式旱物。

首先是service層的測試方式遥缕,service層的單元測試和我們平時寫的測試沒太大區(qū)別。在工程的test目錄下宵呛,新建一個測試類单匣,代碼如下:

package org.zero01;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.zero01.domain.Result;
import org.zero01.domain.Student;
import org.zero01.service.IStudentService;

/**
 * @program: sb-web
 * @description: Student測試類
 * @author: 01
 * @create: 2018-05-05 21:46
 **/
@RunWith(SpringRunner.class)
@SpringBootTest
public class StudentServiceTest {

    @Autowired
    private IStudentService iStudentService;

    @Test
    public void findOneTest() {
        Result<Student> result = iStudentService.findOne(1);
        Student student = result.getData();
        Assert.assertEquals(18, student.getAge());
    }
}

執(zhí)行該測試用例,運行結(jié)果如下:


image.png

我們修改一下年齡為15宝穗,以此模擬一下測試不通過的情況:


image.png

service層的測試比較簡單户秤,就介紹到這。接下來我們看一下controller層的測試方式逮矛。IDEA中有一個比較方便的功能可以幫我們生成測試方法鸡号,到需要被測試的controller類中,按 Ctrl + Shift + t 就可以快速創(chuàng)建測試方法须鼎。如下鲸伴,點擊Create New Test:


image.png

然后選擇需要測試的方法:


image.png

生成的測試用例代碼如下:

package org.zero01.controller;

import org.junit.Test;

import static org.junit.Assert.*;

public class StudentControllerTest {

    @Test
    public void checkAge() {
    }
}

接著我們來完成這個測試代碼,controller層的測試和service層不太一樣晋控,因為需要訪問url汞窗,而不是直接調(diào)用方法進行測試。測試代碼如下:

package org.zero01.controller;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class StudentControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void checkAge() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("/check_age.do")  // 使用get請求
                .param("age","18"))  // url參數(shù)
                .andExpect(MockMvcResultMatchers.status().isOk());  // 判斷返回的狀態(tài)是否正常
    }
}

運行該測試用例赡译,因為我們之前實現(xiàn)了一個記錄http訪問日志的功能仲吏,所以可以直接通過控制臺的輸出日志來判斷接口是否有被請求到:


image.png

單元測試就介紹到這,畢竟一般我們不會在代碼上測試controller層捶朵,而是使用postman或者restlet client等工具進行測試蜘矢。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市综看,隨后出現(xiàn)的幾起案子品腹,更是在濱河造成了極大的恐慌,老刑警劉巖红碑,帶你破解...
    沈念sama閱讀 223,126評論 6 520
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件舞吭,死亡現(xiàn)場離奇詭異,居然都是意外死亡析珊,警方通過查閱死者的電腦和手機羡鸥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,421評論 3 400
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來忠寻,“玉大人惧浴,你說我怎么就攤上這事∞忍辏” “怎么了衷旅?”我有些...
    開封第一講書人閱讀 169,941評論 0 366
  • 文/不壞的土叔 我叫張陵捐腿,是天一觀的道長。 經(jīng)常有香客問我柿顶,道長茄袖,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,294評論 1 300
  • 正文 為了忘掉前任嘁锯,我火速辦了婚禮宪祥,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘家乘。我一直安慰自己蝗羊,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 69,295評論 6 398
  • 文/花漫 我一把揭開白布烤低。 她就那樣靜靜地躺著肘交,像睡著了一般。 火紅的嫁衣襯著肌膚如雪扑馁。 梳的紋絲不亂的頭發(fā)上涯呻,一...
    開封第一講書人閱讀 52,874評論 1 314
  • 那天,我揣著相機與錄音腻要,去河邊找鬼复罐。 笑死,一個胖子當(dāng)著我的面吹牛雄家,可吹牛的內(nèi)容都是我干的效诅。 我是一名探鬼主播,決...
    沈念sama閱讀 41,285評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼趟济,長吁一口氣:“原來是場噩夢啊……” “哼乱投!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起顷编,我...
    開封第一講書人閱讀 40,249評論 0 277
  • 序言:老撾萬榮一對情侶失蹤戚炫,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后媳纬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體双肤,經(jīng)...
    沈念sama閱讀 46,760評論 1 321
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,840評論 3 343
  • 正文 我和宋清朗相戀三年钮惠,在試婚紗的時候發(fā)現(xiàn)自己被綠了茅糜。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,973評論 1 354
  • 序言:一個原本活蹦亂跳的男人離奇死亡素挽,死狀恐怖蔑赘,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤米死,帶...
    沈念sama閱讀 36,631評論 5 351
  • 正文 年R本政府宣布锌历,位于F島的核電站贮庞,受9級特大地震影響峦筒,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜窗慎,卻給世界環(huán)境...
    茶點故事閱讀 42,315評論 3 336
  • 文/蒙蒙 一物喷、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧遮斥,春花似錦峦失、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,797評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至较屿,卻和暖如春隧魄,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背隘蝎。 一陣腳步聲響...
    開封第一講書人閱讀 33,926評論 1 275
  • 我被黑心中介騙來泰國打工购啄, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人嘱么。 一個月前我還...
    沈念sama閱讀 49,431評論 3 379
  • 正文 我出身青樓狮含,卻偏偏與公主長得像,于是被迫代替她去往敵國和親曼振。 傳聞我的和親對象是個殘疾皇子几迄,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,982評論 2 361

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