如何優(yōu)雅的做數(shù)據(jù)校驗(yàn)-Hibernate Validator詳細(xì)使用說明

原文地址

數(shù)據(jù)校驗(yàn)是在平時的編碼過程中常做的工作醉顽,在系統(tǒng)的各個層可能都要去實(shí)現(xiàn)一些校驗(yàn)邏輯,再去做業(yè)務(wù)處理左痢。這些繁瑣的校驗(yàn)與我們的業(yè)務(wù)代碼在一塊就會顯得臃腫腾降。而且這些校驗(yàn)通常是業(yè)務(wù)無關(guān)的。也是在工作中使用到Hibernate Validator而晒,但卻發(fā)現(xiàn)有人沒有使用好它(竟然還能看到一些if else的校驗(yàn)代碼...)蝇狼,所以在這里決定整理下關(guān)于Hibernate Validator的使用

Bean Validation 2.0(JSR 380)定義了用于實(shí)體和方法驗(yàn)證的元數(shù)據(jù)模型和API,Hibernate Validator是目前最好的實(shí)現(xiàn)欣硼,這篇主要是說Hibernate Validator的使用

Hibernate Validator的使用
依賴
如果是Spring Boot項(xiàng)目题翰,那么spring-boot-starter-web中就已經(jīng)依賴hibernate-validator了

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
如果是Spring Mvc,那可以直接添加hibernate-validator依賴

<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.17.Final</version>
</dependency>
bean約束聲明和驗(yàn)證诈胜,Validator
先給我們的Java對象添加約束注解

@Data
@AllArgsConstructor
public class User {

private String id;

@NotBlank
@Size(max = 20)
private String name;

@NotNull
@Pattern(regexp = "[A-Z][a-z][0-9]")
private String password;

@NotNull
private Integer age;

@Max(10)
@Min(1)
private Integer level;

}
驗(yàn)證實(shí)體實(shí)例需要先獲取Validator實(shí)例

ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
Validator接口有三個方法豹障,可用于驗(yàn)證整個實(shí)體或僅驗(yàn)證實(shí)體的單個屬性

Validator#validate() 驗(yàn)證所有bean的所有約束
Validator#validateProperty() 驗(yàn)證單個屬性
Validator#validateValue() 檢查給定類的單個屬性是否可以成功驗(yàn)證
public class UserTest {

private static Validator validator;

@BeforeAll
public static void setUpValidator() {
    ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
    validator = factory.getValidator();
}

@Test
public void validatorTest() {
    User user = new User(null, "", "!@#$", null, 11);

    // 驗(yàn)證所有bean的所有約束
    Set<ConstraintViolation<User>> constraintViolations = validator.validate(user);
    // 驗(yàn)證單個屬性
    Set<ConstraintViolation<User>> constraintViolations2 = validator.validateProperty(user, "name");
    // 檢查給定類的單個屬性是否可以成功驗(yàn)證
    Set<ConstraintViolation<User>> constraintViolations3 = validator.validateValue(User.class, "password", "sa!");

    constraintViolations.forEach(constraintViolation -> System.out.println(constraintViolation.getMessage()));
    constraintViolations2.forEach(constraintViolation -> System.out.println(constraintViolation.getMessage()));
    constraintViolations3.forEach(constraintViolation -> System.out.println(constraintViolation.getMessage()));
}

}
測試結(jié)果

不能為空
最大不能超過10
需要匹配正則表達(dá)式"[A-Z][a-z][0-9]"
不能為null
不能為空
需要匹配正則表達(dá)式"[A-Z][a-z][0-9]"
方法約束聲明和驗(yàn)證,ExecutableValidator
從Bean Validation 1.1開始焦匈,約束不僅可以應(yīng)用于JavaBean及其屬性血公,而且可以應(yīng)用于任何Java類型的方法和構(gòu)造函數(shù)的參數(shù)和返回值,這里簡單看一個例子

public class RentalStation {

public RentalStation(@NotNull String name) {
    //...
}

public void rentCar(@NotNull @Future LocalDate startDate, @Min(1) int durationInDays) {
    //...
}

@NotNull
@Size(min = 1)
public List<@NotNull String> getCustomers() {
    //...
    return null;
}

}
ExecutableValidator接口可以完成方法約束的驗(yàn)證

ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
executableValidator = factory.getValidator().forExecutables();
該ExecutableValidator界面共有四種方法:

validateParameters()和validateReturnValue()用于方法驗(yàn)證
validateConstructorParameters()和validateConstructorReturnValue()用于構(gòu)造函數(shù)驗(yàn)證
public class RentalStationTest {

private static ExecutableValidator executableValidator;

@BeforeAll
public static void setUpValidator() {
    ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
    executableValidator = factory.getValidator().forExecutables();
}

@Test
public void validatorTest() throws NoSuchMethodException {
    RentalStation rentalStation = new RentalStation("z");

    Method method = RentalStation.class.getMethod("rentCar", LocalDate.class, int.class);
    Object[] parameterValues = {LocalDate.now().minusDays(1), 0};
    Set<ConstraintViolation<RentalStation>> violations = executableValidator.validateParameters(
            rentalStation, method, parameterValues);

    violations.forEach(violation -> System.out.println(violation.getMessage()));
}

}
測試結(jié)果

需要是一個將來的時間
最小不能小于1
約束注解
validator-api-2.0的約束注解有22個缓熟,具體我們看下面表格

空與非空檢查
注解 支持Java類型 說明
@Null Object 為null
@NotNull Object 不為null
@NotBlank CharSequence 不為null累魔,且必須有一個非空格字符
@NotEmpty CharSequence、Collection够滑、Map垦写、Array 不為null,且不為空(length/size>0)
Boolean值檢查
注解 支持Java類型 說明 備注
@AssertTrue boolean彰触、Boolean 為true 為null有效
@AssertFalse boolean梯投、Boolean 為false 為null有效
日期檢查
注解 支持Java類型 說明 備注
@Future Date、Calendar况毅、Instant分蓖、LocalDate、LocalDateTime尔许、LocalTime么鹤、MonthDay、OffsetDateTime味廊、OffsetTime蒸甜、Year棠耕、YearMonth、ZonedDateTime柠新、HijrahDate昧辽、JapaneseDate、MinguoDate登颓、ThaiBuddhistDate 驗(yàn)證日期為當(dāng)前時間之后 為null有效
@FutureOrPresent Date、Calendar红氯、Instant框咙、LocalDate、LocalDateTime痢甘、LocalTime喇嘱、MonthDay、OffsetDateTime塞栅、OffsetTime者铜、Year、YearMonth放椰、ZonedDateTime作烟、HijrahDate、JapaneseDate砾医、MinguoDate拿撩、ThaiBuddhistDate 驗(yàn)證日期為當(dāng)前時間或之后 為null有效
@Past Date、Calendar如蚜、Instant压恒、LocalDate、LocalDateTime错邦、LocalTime探赫、MonthDay、OffsetDateTime撬呢、OffsetTime伦吠、Year、YearMonth倾芝、ZonedDateTime讨勤、HijrahDate、JapaneseDate晨另、MinguoDate潭千、ThaiBuddhistDate 驗(yàn)證日期為當(dāng)前時間之前 為null有效
@PastOrPresent Date、Calendar借尿、Instant刨晴、LocalDate屉来、LocalDateTime、LocalTime狈癞、MonthDay茄靠、OffsetDateTime、OffsetTime蝶桶、Year慨绳、YearMonth、ZonedDateTime真竖、HijrahDate脐雪、JapaneseDate、MinguoDate恢共、ThaiBuddhistDate 驗(yàn)證日期為當(dāng)前時間或之前 為null有效
數(shù)值檢查
注解 支持Java類型 說明 備注
@Max BigDecimal战秋、BigInteger,byte讨韭、short脂信、int、long以及包裝類 小于或等于 為null有效
@Min BigDecimal透硝、BigInteger狰闪,byte、short蹬铺、int尝哆、long以及包裝類 大于或等于 為null有效
@DecimalMax BigDecimal、BigInteger甜攀、CharSequence秋泄,byte、short规阀、int恒序、long以及包裝類 小于或等于 為null有效
@DecimalMin BigDecimal、BigInteger谁撼、CharSequence歧胁,byte、short厉碟、int喊巍、long以及包裝類 大于或等于 為null有效
@Negative BigDecimal、BigInteger箍鼓,byte崭参、short、int款咖、long何暮、float奄喂、double以及包裝類 負(fù)數(shù) 為null有效,0無效
@NegativeOrZero BigDecimal海洼、BigInteger跨新,byte、short坏逢、int域帐、long、float是整、double以及包裝類 負(fù)數(shù)或零 為null有效
@Positive BigDecimal俯树、BigInteger,byte贰盗、short、int阳欲、long舵盈、float、double以及包裝類 正數(shù) 為null有效球化,0無效
@PositiveOrZero BigDecimal秽晚、BigInteger,byte筒愚、short赴蝇、int、long巢掺、float句伶、double以及包裝類 正數(shù)或零 為null有效
@Digits(integer = 3, fraction = 2) BigDecimal、BigInteger陆淀、CharSequence考余,byte、short轧苫、int楚堤、long以及包裝類 整數(shù)位數(shù)和小數(shù)位數(shù)上限 為null有效
其他
注解 支持Java類型 說明 備注
@Pattern CharSequence 匹配指定的正則表達(dá)式 為null有效
@Email CharSequence 郵箱地址 為null有效,默認(rèn)正則 '.*'
@Size CharSequence含懊、Collection身冬、Map、Array 大小范圍(length/size>0) 為null有效
hibernate-validator擴(kuò)展約束(部分)
注解 支持Java類型 說明
@Length String 字符串長度范圍
@Range 數(shù)值類型和String 指定范圍
@URL URL地址驗(yàn)證
自定義約束注解
除了以上提供的約束注解(大部分情況都是能夠滿足的)岔乔,我們還可以根據(jù)自己的需求自定義自己的約束注解

定義自定義約束酥筝,有三個步驟

創(chuàng)建約束注解
實(shí)現(xiàn)一個驗(yàn)證器
定義默認(rèn)的錯誤信息
那么下面就直接來定義一個簡單的驗(yàn)證手機(jī)號碼的注解

@Documented
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Constraint(validatedBy = {MobileValidator.class})
@Retention(RUNTIME)
@Repeatable(Mobile.List.class)
public @interface Mobile {

/**
 * 錯誤提示信息,可以寫死,也可以填寫國際化的key
 */
String message() default "手機(jī)號碼不正確";

Class<?>[] groups() default {};

Class<? extends Payload>[] payload() default {};

String regexp() default "^1([38][0-9]|4[579]|5[0-3,5-9]|6[6]|7[0135678]|9[89])\\d{8}$";

@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
@Documented
@interface List {
    Mobile[] value();
}

}
關(guān)于注解的配置這里不說了重罪,自定義約束需要下面3個屬性

message 錯誤提示信息樱哼,可以寫死,也可以填寫國際化的key
groups 分組信息哀九,允許指定此約束所屬的驗(yàn)證組(下面會說到分組約束)
payload 有效負(fù)載,可以通過payload來標(biāo)記一些需要特殊處理的操作
@Repeatable注解和List定義可以讓該注解在同一個位置重復(fù)多次搅幅,通常是不同的配置(比如不同的分組和消息)

@Constraint(validatedBy = {MobileValidator.class})該注解是指明我們的自定義約束的驗(yàn)證器阅束,那下面就看一下驗(yàn)證器的寫法,需要實(shí)現(xiàn)javax.validation.ConstraintValidator接口

public class MobileValidator implements ConstraintValidator<Mobile, String> {

/**
 * 手機(jī)驗(yàn)證規(guī)則
 */
private Pattern pattern;

@Override
public void initialize(Mobile mobile) {
    pattern = Pattern.compile(mobile.regexp());
}

@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
    if (value == null) {
        return true;
    }

    return pattern.matcher(value).matches();
}

}
ConstraintValidator接口定義了在實(shí)現(xiàn)中設(shè)置的兩個類型參數(shù)茄唐。第一個指定要驗(yàn)證的注解類(如Mobile)息裸,第二個指定驗(yàn)證器可以處理的元素類型(如String);initialize()方法可以訪問約束注解的屬性值沪编;isValid()方法用于驗(yàn)證呼盆,返回true表示驗(yàn)證通過

Bean驗(yàn)證規(guī)范建議將空值視為有效。如果null不是元素的有效值蚁廓,則應(yīng)使用@NotNull 顯式注釋

到這里我們自定義的約束就寫好了访圃,可以用個例子來測試一下

public class MobileTest {

public void setMobile(@Mobile String mobile){
    // to do
}

private static ExecutableValidator executableValidator;

@BeforeAll
public static void setUpValidator() {
    ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
    executableValidator = factory.getValidator().forExecutables();
}

@Test
public void manufacturerIsNull() throws NoSuchMethodException {
    MobileTest mobileTest = new MobileTest();

    Method method = MobileTest.class.getMethod("setMobile", String.class);
    Object[] parameterValues = {"1111111"};
    Set<ConstraintViolation<MobileTest>> violations = executableValidator.validateParameters(
            mobileTest, method, parameterValues);

    violations.forEach(violation -> System.out.println(violation.getMessage()));
}

}
手機(jī)號碼不正確
分組約束
在上面的自定義約束中,有個groups屬性是用來指定驗(yàn)證約束的分組相嵌,我們在為屬性加上注解的時候腿时,如果沒有配置分組信息,那么默認(rèn)會采用默認(rèn)分組 javax.validation.groups.Default

分組是用接口定義的饭宾,用做標(biāo)識批糟,這里創(chuàng)建兩個標(biāo)識AddGroup和UpdateGroup,分別標(biāo)識新增和修改

public interface AddGroup {
}

public interface UpdateGroup {
}
然后對我們的User對象的id屬性做分組標(biāo)識

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {

@Null(groups = AddGroup.class)
@NotBlank(groups = UpdateGroup.class)
private String id;

// ... 省略了其他屬性

}
我們看下如何使用

@Test
public void validatorGroupTest() {
User user = new User();

// 檢查給定類的單個屬性是否可以成功驗(yàn)證
Set<ConstraintViolation<User>> constraintViolations = validator.validateValue(User.class, "id", "", UpdateGroup.class);
Set<ConstraintViolation<User>> constraintViolations2 = validator.validateValue(User.class, "id", "");
Set<ConstraintViolation<User>> constraintViolations3 = validator.validateValue(User.class, "id", "", AddGroup.class);

constraintViolations.forEach(constraintViolation -> System.out.println(constraintViolation.getMessage()));
constraintViolations2.forEach(constraintViolation -> System.out.println(constraintViolation.getMessage()));
constraintViolations3.forEach(constraintViolation -> System.out.println(constraintViolation.getMessage()));

}
上面的測試只有加了UpdateGroug分組才會驗(yàn)證看铆,返回錯誤信息徽鼎,而下面的constraintViolations2并不會去驗(yàn)證,因?yàn)槟J(rèn)會采用Default分組弹惦。如果想要不標(biāo)記分組的時候否淤,也會去驗(yàn)證Default分組,可以去繼承默認(rèn)分組

public interface AddGroup extends Default {
}
在Spring中使用Hibernate Validator
上面介紹了Validator的一些使用棠隐,還有注解的介紹叹括,那么在Spring中我們怎么去使用Hibernate Validator做驗(yàn)證呢?或者說再Web項(xiàng)目中怎么使用Hibernate Validator宵荒?

spring-boot-starter-web中是添加了hibernate-validator依賴的汁雷,說明Spring Boot本身也是使用到了Hibernate Validator驗(yàn)證框架的

配置Validator
@Configuration
public class ValidatorConfig {

/**
 * 配置驗(yàn)證器
 *
 * @return validator
 */
@Bean
public Validator validator() {
    ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
            .configure()
            // 快速失敗模式
            .failFast(true)
            // .addProperty( "hibernate.validator.fail_fast", "true" )
            .buildValidatorFactory();
    return validatorFactory.getValidator();
}

}
可以通過方法 failFast(true)或 addProperty("hibernate.validator.fail_fast", "true")設(shè)置為快速失敗模式,快速失敗模式在校驗(yàn)過程中报咳,當(dāng)遇到第一個不滿足條件的參數(shù)時就立即返回侠讯,不再繼續(xù)后面參數(shù)的校驗(yàn)。否則會一次性校驗(yàn)所有參數(shù)暑刃,并返回所有不符合要求的錯誤信息

如果是Spring MVC的話厢漩,需要xml配置可參考下面的配置

<mvc:annotation-driven validator="validator"/>


<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>

<property name="validationMessageSource" ref="messageSource"/>
</bean>

<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource" name="messageSource">

<property name="useCodeAsDefaultMessage" value="false" />
<property name="defaultEncoding" value="UTF-8" />
<property name="cacheSeconds" value="60" />
</bean>
請求參數(shù)bean驗(yàn)證
接口上的Bean驗(yàn)證,需要在參數(shù)前加上@Valid或Spring的 @Validated注解岩臣,這兩種注釋都會導(dǎo)致應(yīng)用標(biāo)準(zhǔn)Bean驗(yàn)證溜嗜。如果驗(yàn)證不通過會拋出BindException異常宵膨,并變成400(BAD_REQUEST)響應(yīng);或者可以通過Errors或BindingResult參數(shù)在控制器內(nèi)本地處理驗(yàn)證錯誤炸宵。另外辟躏,如果參數(shù)前有@RequestBody注解,驗(yàn)證錯誤會拋出MethodArgumentNotValidException異常土全。

@RestController
public class UserController {

@PostMapping("/user")
public R handle(@Valid @RequestBody User user, BindingResult result) {
    // 在控制器內(nèi)本地處理驗(yàn)證錯誤
    if (result.hasErrors()) {
        result.getAllErrors().forEach(s -> System.out.println(s.getDefaultMessage()));
         return R.fail(result.getAllErrors().get(0).getDefaultMessage());
    }
    // ...
    return R.success();
}

@PostMapping("/user2")
public R handle2(@Valid User user, BindingResult result) {
    // 在控制器內(nèi)本地處理驗(yàn)證錯誤
    if (result.hasErrors()) {
        result.getAllErrors().forEach(s -> System.out.println(s.getDefaultMessage()));
         return R.fail(result.getAllErrors().get(0).getDefaultMessage());
    }
    // ...
    return R.success();
}

/**
 * 驗(yàn)證不通過拋出 `MethodArgumentNotValidException`
 */
@PostMapping("/user3")
public R handle3(@Valid @RequestBody User user) {
    // ...
    return R.success();
}

/**
 * 驗(yàn)證不通過拋出 `BindException`
 */
@PostMapping("/user4")
public R handle4(@Valid User user) {
    // ...
    return R.success();
}

}
配合Spring的BindingResult參數(shù)捎琐,我們是可以在控制器中去處理驗(yàn)證錯誤,不過通常也是把驗(yàn)證錯誤的消息轉(zhuǎn)成我們自己的返回格式裹匙,那么在每個方法中都去做這樣的驗(yàn)證錯誤處理瑞凑,顯然是沒有必要的姻乓。我們可以利用驗(yàn)證不通過的異常來做統(tǒng)一的錯誤處理

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

/**
 * hibernate validator 數(shù)據(jù)綁定驗(yàn)證異常攔截
 *
 * @param e 綁定驗(yàn)證異常
 * @return 錯誤返回消息
 */
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(BindException.class)
public R validateErrorHandler(BindException e) {
    ObjectError error = e.getAllErrors().get(0);
    log.info("數(shù)據(jù)驗(yàn)證異常:{}", error.getDefaultMessage());
    return R.fail(error.getDefaultMessage());
}

/**
 * hibernate validator 數(shù)據(jù)綁定驗(yàn)證異常攔截
 *
 * @param e 綁定驗(yàn)證異常
 * @return 錯誤返回消息
 */
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(MethodArgumentNotValidException.class)
public R validateErrorHandler(MethodArgumentNotValidException e) {
    ObjectError error = e.getBindingResult().getAllErrors().get(0);
    log.info("數(shù)據(jù)驗(yàn)證異常:{}", error.getDefaultMessage());
    return R.fail(error.getDefaultMessage());
}

}
方法參數(shù)驗(yàn)證
配置
Hibernate Validator是可以在方法級驗(yàn)證參數(shù)的顶瞒,Spring中當(dāng)然也是有實(shí)現(xiàn)的。

我們在Validator的配置中枉阵,添加MethodValidationPostProcessorBean惰匙,在上面的ValidatorConfig.java中添加一下配置

/**

  • 設(shè)置方法參數(shù)驗(yàn)證器
    */
    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor() {
    MethodValidationPostProcessor postProcessor = new MethodValidationPostProcessor();
    // 設(shè)置validator模式為快速失敗返回
    postProcessor.setValidator(validator());
    return postProcessor;
    }
    如果是Spring Mvc篱蝇,那么要在spring-mvc.xml中聲明bean信息,不然在Controller里面是無效的


<bean id="methodValidationPostProcessor" class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor">
<property name="validator" ref="validator"/>
</bean>
使用
配置了上面的MethodValidationPostProcessor徽曲,我們就可以在方法參數(shù)或返回值使用約束注解了,要注意的是麸塞,在要使用參數(shù)驗(yàn)證的類上一定要加上@Validated注解秃臣,否則無效

/**

  • 一定要加上 @Validated 注解
    */
    @Validated
    @RestController
    public class UserController {

    @GetMapping("/user")
    public R handle(@Mobile String mobile) {
    // ...
    return R.success();
    }
    }
    如果驗(yàn)證不通過,會拋出ConstraintViolationException異常哪工,同樣的奥此,我們可以在全局的異常處理器里面處理驗(yàn)證錯誤,在GlobalExceptionHandler中添加一下代碼

/**

  • spring validator 方法參數(shù)驗(yàn)證異常攔截
  • @param e 綁定驗(yàn)證異常
  • @return 錯誤返回消息
    */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(ConstraintViolationException.class)
    public R defaultErrorHandler(ConstraintViolationException e) {
    Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
    ConstraintViolation<?> violation = violations.iterator().next();
    log.info("數(shù)據(jù)驗(yàn)證異常:{}", violation.getMessage());
    return R.fail(violation.getMessage());
    }
    分組
    Spring的@Validate注解是可以支持分組驗(yàn)證的

@PostMapping("/user")
public R handle(@Validated(AddGroup.class) @RequestBody User user) {
// ...
return R.success();
}

作者:TurboSnail
鏈接:http://www.reibang.com/p/3267689ebf1b
來源:簡書
著作權(quán)歸作者所有雁比。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán)稚虎,非商業(yè)轉(zhuǎn)載請注明出處。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末偎捎,一起剝皮案震驚了整個濱河市蠢终,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌茴她,老刑警劉巖寻拂,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異丈牢,居然都是意外死亡祭钉,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進(jìn)店門己沛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來慌核,“玉大人距境,你說我怎么就攤上這事】遄浚” “怎么了垫桂?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長扒接。 經(jīng)常有香客問我伪货,道長,這世上最難降的妖魔是什么钾怔? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任碱呼,我火速辦了婚禮,結(jié)果婚禮上宗侦,老公的妹妹穿的比我還像新娘愚臀。我一直安慰自己,他們只是感情好矾利,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布姑裂。 她就那樣靜靜地躺著,像睡著了一般男旗。 火紅的嫁衣襯著肌膚如雪舶斧。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天察皇,我揣著相機(jī)與錄音茴厉,去河邊找鬼。 笑死什荣,一個胖子當(dāng)著我的面吹牛矾缓,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播稻爬,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼嗜闻,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了桅锄?” 一聲冷哼從身側(cè)響起琉雳,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎友瘤,沒想到半個月后咐吼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡商佑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年锯茄,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡肌幽,死狀恐怖晚碾,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情喂急,我是刑警寧澤格嘁,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站廊移,受9級特大地震影響糕簿,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜狡孔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一懂诗、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧苗膝,春花似錦殃恒、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至问窃,卻和暖如春亥鬓,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背域庇。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工嵌戈, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人较剃。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像技健,于是被迫代替她去往敵國和親写穴。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評論 2 355

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