spring mvc validation

是服務(wù)就需要對(duì)外提供接口枉证,否則該服務(wù)就沒有任何意義。接口需要指定具體的入?yún)⑶闆r概漱,以保證服務(wù)能夠正常地運(yùn)行啥纸。spring mvc通過controller中的method對(duì)外提供接口服務(wù),本文就如何在spring mvc中對(duì)RequestParam誓斥、PathVariableRequestBody三種類型的參數(shù)做參數(shù)校驗(yàn)做簡(jiǎn)單介紹综看。

為了更好地介紹上訴三種類型參數(shù)校驗(yàn)的方式,本文將通過一個(gè)簡(jiǎn)單的接口需求來完成相應(yīng)的接口校驗(yàn)岖食,相關(guān)的代碼在spring-demo 項(xiàng)目上红碑。

1 需求

現(xiàn)在有一個(gè)學(xué)生管理系統(tǒng)(假設(shè)所有學(xué)生的姓名都是唯一的),我們需要對(duì)學(xué)生信息進(jìn)行管理,即實(shí)現(xiàn)最常見的CURD操作析珊。學(xué)生類(Student)如下所示:

@JsonIgnoreProperties(ignoreUnknown = true)
public class Student {
    private String studentName;
    private int age;
    private int gender;
    private LocalDate birthDay;
    
    // ... getter setter toString
}

現(xiàn)在需要提供CURD四個(gè)接口羡鸥,接口的具體要求如下:

  1. 添加:采用POST的方式請(qǐng)求,姓名不能為空忠寻,年齡在1~200之間惧浴,性別用0和1表示,出生日期不能為空且只能是過去的時(shí)間奕剃。
  2. 刪除:根據(jù)姓名刪除學(xué)生衷旅,姓名不能為空
  3. 修改:修改指定學(xué)生的出生日期
  4. 查詢:查詢所有出生日期在指定范圍內(nèi)的學(xué)生

默認(rèn)情況下請(qǐng)求參數(shù)無法直接映射成LocalDate類型的,需要在spring中配置jacksonobjectmapper添加JavaTimeModule纵朋。

2 依賴項(xiàng)

2.1 JSR 380

JSR制定了許多Java開發(fā)的規(guī)范柿顶,其中JSR 380就制定Bean Validation的相關(guān)規(guī)范,可以在pom.xml中加入依賴引入相關(guān)API操软。

<!-- https://mvnrepository.com/artifact/javax.validation/validation-api -->
<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>2.0.1.Final</version>
</dependency>

JSR 380只是規(guī)范嘁锯,并沒有具體實(shí)現(xiàn)檢驗(yàn)的方法,如果直接使用validation-api進(jìn)行校驗(yàn)聂薪,會(huì)拋出javax.validation.NoProviderFoundException家乘,提示需要提供實(shí)現(xiàn)JSR 380的校驗(yàn)器

2.2 Hibernate Validator

Hibernate ValidatorJSR 380規(guī)范的具體實(shí)現(xiàn)藏澳,并且除了JSR 380中的校驗(yàn)器仁锯,它還提供了更多的自定義的校驗(yàn)器。
pom.xml中加入如下依賴引入Hibernate Validator

<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.0.13.Final</version>
</dependency>
<dependency>
    <groupId>org.glassfish</groupId>
    <artifactId>javax.el</artifactId>
    <version>3.0.1-b09</version>
</dependency>

實(shí)際上只需要添加2.2的依賴即可翔悠,2.1的依賴可以不用添加扑馁,因?yàn)?code>2.2中已經(jīng)包含了validation-api中的內(nèi)容。

3 應(yīng)用Hibernate Validator

Hibernate Validator實(shí)現(xiàn)了JSR 380的規(guī)范凉驻,提供了諸如@NotNull等的校驗(yàn)器,本文這里不具體介紹Hibernate Validator都提供了哪些校驗(yàn)器复罐,感興趣的話可以去Hibernate Validator官網(wǎng)查看相關(guān)的文檔涝登。

3.1 添加學(xué)生信息

  1. 根據(jù)1中所述的需求,現(xiàn)在對(duì)StudentModel的字段添加校驗(yàn)規(guī)則效诅,如下:

    public class StudentModel {
        @NotBlank(message = "studentName不能為空")
        private String studentName;
    
        @Min(value = 1, message = "參數(shù)age不能小于1")
        @Max(value = 200, message = "參數(shù)age不能大于200")
        @Range(min = 1, max = 200, message = "age只能在1到200之間")
        private int age;
        
        @Range(min = 0, max = 1, message = "gender只能取0或者1")
        private int gender;
    
        @Past(message = "birthDay只能是過去的時(shí)間")
        private LocalDate birthDay; 
    }
    
  2. 在方法的參數(shù)中對(duì)StudentModel通過@Validation進(jìn)行校驗(yàn)

    @PostMapping(value = "/add", consumes =      MediaType.APPLICATION_JSON_UTF8_VALUE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public Map<String, Object> add(@Valid @RequestBody StudentModel studentModel) {
      return OutPut.success(HttpStatusWrapper.OK,"成功", studentModel);
    }
    

    @ValidHibernate Validator中用來校驗(yàn)對(duì)象合法性的注解胀滚。
    請(qǐng)求運(yùn)行并且請(qǐng)求add接口的時(shí)候,當(dāng)post body中的數(shù)據(jù)不符合設(shè)置的校 驗(yàn)規(guī)則是乱投,系統(tǒng)并沒有返回對(duì)應(yīng)的錯(cuò)誤信息咽笼,而是輸出下面的信息:

    Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public java.util.Map<java.lang.String, java.lang.Object> com.lianglei.spring.demo.controller.StudentController.add(com.lianglei.spring.demo.model.StudentModel): [Field error in object 'studentModel' on field 'gender': rejected value [2]; codes [Range.studentModel.gender,Range.gender,Range.int,Range]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [studentModel.gender,gender]; arguments []; default message [gender],1,0]; default message [gender只能取0或者1]] ]
    

    從上面的異常信息中可以看出,gender要求只能是0或者1戚炫,但是輸入的2剑刑,被Hibernate Validatorrejected了。從中我們可以發(fā)現(xiàn),校驗(yàn)不通過的時(shí)候施掏,會(huì)拋出org.springframework.web.bind.MethodArgumentNotValidException異常钮惠。因此我們可以通過統(tǒng)一異常捕獲的方式處理校驗(yàn)不通過的情況,給出友好的接口返回七芭。

  3. 捕獲MethodArgumentNotValidException

    @RestControllerAdvice
    public class GlobalExceptionHandler {
        private static final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionHandler.class);
        
        /**
         * 接口參數(shù)校驗(yàn)異常
         * @param e
         * @return
         */
        @ExceptionHandler(value = {MethodArgumentNotValidException.class})
        public Map<String, Object> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
            LOGGER.error(e.getMessage(), e);
            final String message = e.getBindingResult().getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining());
            return OutPut.failure(HttpStatusWrapper.ILLEGAL_REQUEST_PARAMETERS, message);
        }
    }
    

    添加異常捕獲后的輸出如下:

    {
        "status": "Illegal request parameters",
        "code": 460,
        "msg": "gender只能取0或者1"
    }
    

    如果想在GlobalExceptionHandler中處理MethodArgumentNotValidException.class異常的話素挽,需要注意GlobalExceptionHandler不能繼承ResponseEntityExceptionHandler否則會(huì)發(fā)生沖突。

    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'handlerExceptionResolver' defined in org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.servlet.HandlerExceptionResolver]: Factory method 'handlerExceptionResolver' threw exception; nested exception is java.lang.IllegalStateException: Ambiguous @ExceptionHandler method mapped for [class org.springframework.web.bind.MethodArgumentNotValidException]: {public java.util.Map com.lianglei.spring.demo.exception.GlobalExceptionHandler.handleMethodArgumentNotValidException(org.springframework.web.bind.MethodArgumentNotValidException), public final org.springframework.http.ResponseEntity org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler.handleException(java.lang.Exception,org.springframework.web.context.request.WebRequest) throws java.lang.Exception}? 
    
  4. request body中參數(shù)名與StudentModel 不一致的情況
    在實(shí)際開發(fā)過程中狸驳,經(jīng)常會(huì)出現(xiàn)入?yún)⒌拿峙c請(qǐng)求類中的名字不一致的情況预明,比如說請(qǐng)求是student_name,而類中字段名為studentName耙箍。

    因?yàn)槭?code>POST請(qǐng)求撰糠,采用的是JSON的方式,所以只需要在studentName上通過@JsonProperty注解一下即可究西,如下:

    @JsonProperty("student_name")
    @NotBlank(message = "student_name不能為空")
    private String studentName;
    

3.2 刪除學(xué)生信息

采用DELETE刪除指定姓名的學(xué)生窗慎,需要判斷姓名不能為空,這里采用@RequestParam獲取student_name參數(shù)卤材。

  1. 直接通過@NotBlank校驗(yàn)

    @DeleteMapping(value = "/delete", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public Map<String, Object> delete(@NotBlank(message = "student_name不可以為空") @RequestParam(name = "student_name") String name) {
        return OutPut.success(HttpStatusWrapper.OK,"成功", name);
    }
    

    這里通過@NotBlank要求student_name不可以為空(null或者trim之后的"")遮斥,運(yùn)行后程序正常運(yùn)行,但是@NotBlank并沒有生效--student_name即使填了空白也沒有報(bào)錯(cuò)

    這是因?yàn)椴幌?code>@Valid可以直接作用在@RequestBody參數(shù)上扇丛,@NotBlank并不會(huì)直接在@RequestParam參數(shù)上生效术吗。

  2. Controller上添加@Validated配合@NotBlank校驗(yàn)

    參考Validating RequestParams and PathVariables in Spring MVC這篇文章,了解到@RequestParam上的validation需要在類上標(biāo)注@Validated注解(即在StudentController上注解)

    然而添加改注解運(yùn)行后帆精,@NotBlank仍然沒有生效较屿。原因是沒有為@RequestParam配置注解器。

  3. spring mvc配置校驗(yàn)器

    @Configuration
    @EnableWebMvc
    @EnableAspectJAutoProxy
    @EnableScheduling
    @ComponentScan(basePackages = "com.lianglei.spring.demo")
    public class ApplicationConfig implements WebMvcConfigurer {
        @Bean
        public Validator validator() {
            ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
                    .configure()
                    .failFast(true)
                    .buildValidatorFactory();
    
            return validatorFactory.getValidator();
        }
    
        @Bean
        public MethodValidationPostProcessor methodValidationPostProcessor() {
            MethodValidationPostProcessor methodValidationPostProcessor = new MethodValidationPostProcessor();
            methodValidationPostProcessor.setValidator(validator());
            return methodValidationPostProcessor;
        }
    
    }
    

    failFast的意思只要出現(xiàn)校驗(yàn)失敗的情況卓练,就立即結(jié)束校驗(yàn)隘蝎,不再進(jìn)行后續(xù)的校驗(yàn)。

    如何在spring中配置bean襟企,若有疑問請(qǐng)參看Spring中的Bean配置方式一文

    配置成功后嘱么,再次運(yùn)行服務(wù)進(jìn)行delete請(qǐng)求,系統(tǒng)拋出如下異常:

    [ERROR] 2018-12-23 09:51:10,243 method:com.lianglei.spring.demo.exception.GlobalExceptionHandler.handleException(GlobalExceptionHandler.java:34)
    delete.arg0: name不可以為空
    javax.validation.ConstraintViolationException: delete.arg0: name不可以為空
        at org.springframework.validation.beanvalidation.MethodValidationInterceptor.invoke(MethodValidationInterceptor.java:116)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688)
        at com.lianglei.spring.demo.controller.StudentController$$EnhancerBySpringCGLIB$$2cfc55af.delete(<generated>)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:215)
        at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:142)
        at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:800)
        at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1038)
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:998)
        at org.springframework.web.servlet.FrameworkServlet.doDelete(FrameworkServlet.java:923)
        ...
    

    從這里可以看出顽悼,@RequestParam上validate失敗后拋出的異常是javax.validation.ConstraintViolationException而不是@RequestBody中的MethodArgumentNotValidException異常曼振。

  4. 捕獲ConstraintViolationException異常

    @ExceptionHandler(value = {ConstraintViolationException.class})
    public Map<String, Object> handleConstraintViolationException(ConstraintViolationException e) {
        LOGGER.error(e.getMessage(), e);
        final String message = e.getConstraintViolations().stream()
                .map(ConstraintViolation::getMessage)
                .collect(Collectors.joining());
        return OutPut.failure(HttpStatusWrapper.ILLEGAL_REQUEST_PARAMETERS, message);
    }
    

    自此,@RequestParam就能夠自動(dòng)生效了蔚龙。

    {
        "status": "Illegal request parameters",
        "code": 460,
        "msg": "student_name不可以為空"
    }
    

3.3 修改學(xué)生信息

修改學(xué)生信息一般通過@RequestBody將參數(shù)傳遞給StudentModel冰评,這時(shí)候校驗(yàn)方式同添加學(xué)生信息中所述。但是木羹,如果需要通過StudentModel mapping 原先的@RequestParam參數(shù)甲雅,又該如何呢?

  1. 請(qǐng)求中直接通過@Valid校驗(yàn)StudentModel

    @PutMapping(value = "/update", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public Map<String, Object> update(@Valid StudentModel studentModel) {
        return OutPut.success(HttpStatusWrapper.OK,"成功", studentModel);
    }
    

    執(zhí)行請(qǐng)求:

    localhost:8080/student/update?student_name=wangwu&age=1&gender=0&birth_day=1994-06-15
    

    異常如下:

    Field error in object 'studentModel' on field 'studentName': rejected value [null]; codes [NotBlank.studentModel.studentName,NotBlank.studentName,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [studentModel.studentName,studentName]; arguments []; default message [studentName]]; default message [studentName不能為空]
        at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:164)
        at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:124)
        at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:165)
        at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
        at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:800)
        at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1038)
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:998)
        at org.springframework.web.servlet.FrameworkServlet.doPut(FrameworkServlet.java:912)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:710)
        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:875)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
        at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:848)
    

    可見student_name并沒有映射到studentName上,導(dǎo)致studentNamenull务荆。這也是必然的妆距,因?yàn)?code>@JsonProperty是用來處理json字符串轉(zhuǎn)對(duì)象的,而請(qǐng)求中并沒有json格式的學(xué)生信息函匕。當(dāng)把請(qǐng)求中的student_name改回studentName后娱据,studentName就會(huì)被正確賦值。
    那么盅惜,如何才能解決非POST json形式下中剩,請(qǐng)求參數(shù)和對(duì)象的屬性名不一致的情況呢?

    1. 請(qǐng)求及接口修改成PSOT json的形式
    2. 參考 How to customize parameter names when binding spring mvc command objects中的討論或者綁定SpringMvc GET請(qǐng)求對(duì)象時(shí)自定義參數(shù)名總結(jié)的方法。
    3. 拆分對(duì)象字段到接口參數(shù)中抒寂,通過@RequestParam結(jié)合Hibernate Validator完成驗(yàn)證

3.4 查詢學(xué)生信息

通過GET方式查詢學(xué)生信息结啼,student_name@PathVariable的方式進(jìn)行賦值。

@GetMapping(value = "/get/{student_name}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public Map<String, Object> get(@NotBlank(message = "student_name不可以為空") @Size(min = 3, message = "student_name長(zhǎng)度不能小于3") @PathVariable(name = "student_name") String name) {
    return OutPut.success(HttpStatusWrapper.OK,"成功", name);
}
  1. 正常請(qǐng)求

    localhost:8080/student/get/wangwu
    

    返回

    {
        "status": "OK",
        "code": 200,
        "msg": "成功",
        "data": "wangwu"
    }
    
  2. 異常請(qǐng)求

    localhost:8080/student/get/yy
    

    返回

    {
        "status": "Illegal request parameters",
        "code": 460,
        "msg": "student_name長(zhǎng)度不能小于3"
    }
    

    拋出的與@RequestParam方式一樣的ConstraintViolationException異常屈芜。

4 總結(jié)

通過上面的示例演示郊愧,對(duì)于spring mvc中的參數(shù)校驗(yàn),可以得出如下結(jié)論:

  1. 如果接口參數(shù)對(duì)應(yīng)的是請(qǐng)求中請(qǐng)求體部分(@RequestBody)井佑,且請(qǐng)求體的格式為json属铁,可以將請(qǐng)求參數(shù)封裝到一個(gè)類中,在類中通過@NotNull等標(biāo)注設(shè)置校驗(yàn)規(guī)則躬翁,在接口中通過@Valid表明需要進(jìn)行校驗(yàn)焦蘑,校驗(yàn)失敗后會(huì)拋出MethodArgumentNotValidException異常。如果請(qǐng)求中參數(shù)的名稱和接口參數(shù)中字段的名稱不一致盒发,可以通過@JsonProperty標(biāo)注進(jìn)行重命名例嘱。
  2. 如果接口參數(shù)中對(duì)應(yīng)的是請(qǐng)求參數(shù)(@RequestParam)或者請(qǐng)求路徑中的變量(@PathVariable),則可以通過對(duì)應(yīng)的@RequestParam或者@PathVariable結(jié)合@NotNull進(jìn)行參數(shù)檢驗(yàn)宁舰,注意這里需要在Controller上添加@Validated注解拼卵,并且需要給spring配置MethodValidationPostProcessor才能工作。如果校驗(yàn)失敗蛮艰,會(huì)拋出ConstraintViolationException異常腋腮。如果需要將這些參數(shù)封裝到一個(gè)類中,那么請(qǐng)求中的參數(shù)名必須和類中的字段一致印荔,否則會(huì)匹配不上。當(dāng)然详羡,可以通過額外的配置滿足這個(gè)需求仍律,但是比較麻煩而且不支持繼承的類。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末实柠,一起剝皮案震驚了整個(gè)濱河市水泉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖草则,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件钢拧,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡炕横,警方通過查閱死者的電腦和手機(jī)源内,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門豺撑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來腮敌,“玉大人,你說我怎么就攤上這事醋拧∏涑埃” “怎么了颂斜?”我有些...
    開封第一講書人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)拾枣。 經(jīng)常有香客問我沃疮,道長(zhǎng),這世上最難降的妖魔是什么梅肤? 我笑而不...
    開封第一講書人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任司蔬,我火速辦了婚禮,結(jié)果婚禮上凭语,老公的妹妹穿的比我還像新娘葱她。我一直安慰自己,他們只是感情好似扔,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開白布吨些。 她就那樣靜靜地躺著,像睡著了一般炒辉。 火紅的嫁衣襯著肌膚如雪豪墅。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,096評(píng)論 1 291
  • 那天黔寇,我揣著相機(jī)與錄音偶器,去河邊找鬼。 笑死缝裤,一個(gè)胖子當(dāng)著我的面吹牛屏轰,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播憋飞,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼霎苗,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了榛做?” 一聲冷哼從身側(cè)響起唁盏,我...
    開封第一講書人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤内狸,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后厘擂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體昆淡,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年刽严,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了昂灵。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡港庄,死狀恐怖倔既,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情鹏氧,我是刑警寧澤渤涌,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布,位于F島的核電站把还,受9級(jí)特大地震影響实蓬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜吊履,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一安皱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧艇炎,春花似錦酌伊、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至驴娃,卻和暖如春奏候,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背唇敞。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來泰國(guó)打工蔗草, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人疆柔。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓咒精,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親旷档。 傳聞我的和親對(duì)象是個(gè)殘疾皇子模叙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351

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