flutter 網(wǎng)絡(luò)請求封裝 dio(4.0.0)

dart版本:(stable) 2.2.2(空安全)
connectivity:^3.0.6
shared_preferences:^2.0.6
注:我是抄后改成適合自己的,不過網(wǎng)上太多一模一樣的胖缤,不知道誰才是原創(chuàng)澳骤,在此感謝原創(chuàng)作者。
現(xiàn)有功能反番。
就看http沙热,再分別一個(gè)個(gè)看叉钥,大概能看懂了。


image.png

代碼里有些做了解釋篙贸,其他有些我也是抄的投队,也不是很懂哈哈哈,就說了爵川,直接上代碼敷鸦。

api_response.dart

import 'app_exceptions.dart';
class ApiResponse<T> implements Exception {
  Status status;
  T? data;
  AppException? exception;
  ApiResponse.completed(this.data) : status = Status.COMPLETED;
  ApiResponse.error(this.exception) : status = Status.ERROR;

  @override
  String toString() {
    return "Status : $status \n Message : $exception \n Data : $data";
  }
}

enum Status { COMPLETED, ERROR }

app_exceptions.dart

import 'package:dio/dio.dart';

/// 自定義異常
class AppException implements Exception {
  final String? _message;
  final int? _code;
  AppException([
    this._code,
    this._message,
  ]);

  String toString() {
    return "$_message($_code)";
  }

  factory AppException.create(DioError error) {
    switch (error.type) {
      case DioErrorType.cancel:
        {
          return BadRequestException(-1, "請求取消");
        }
        break;
      case DioErrorType.connectTimeout:
        {
          return BadRequestException(-1, "連接超時(shí)");
        }
        break;
      case DioErrorType.sendTimeout:
        {
          return BadRequestException(-1, "請求超時(shí)");
        }
        break;
      case DioErrorType.receiveTimeout:
        {
          return BadRequestException(-1, "響應(yīng)超時(shí)");
        }
        break;
      case DioErrorType.response:
        {
          try {
            int? errCode = error.response?.statusCode!;
            // String errMsg = error.response.statusMessage;
            // return ErrorEntity(code: errCode, message: errMsg);
            switch (errCode) {
              case 400:
                {
                  return BadRequestException(errCode, "請求語法錯(cuò)誤");
                }
                break;
              case 401:
                {
                  return UnauthorisedException(errCode!, "沒有權(quán)限");
                }
                break;
              case 403:
                {
                  return UnauthorisedException(errCode!, "服務(wù)器拒絕執(zhí)行");
                }
                break;
              case 404:
                {
                  return UnauthorisedException(errCode!, "無法連接服務(wù)器");
                }
                break;
              case 405:
                {
                  return UnauthorisedException(errCode!, "請求方法被禁止");
                }
                break;
              case 500:
                {
                  return UnauthorisedException(errCode!, "服務(wù)器內(nèi)部錯(cuò)誤");
                }
                break;
              case 502:
                {
                  return UnauthorisedException(errCode!, "無效的請求");
                }
                break;
              case 503:
                {
                  return UnauthorisedException(errCode!, "服務(wù)器掛了");
                }
                break;
              case 505:
                {
                  return UnauthorisedException(errCode!, "不支持HTTP協(xié)議請求");
                }
                break;
              default:
                {
                  // return ErrorEntity(code: errCode, message: "未知錯(cuò)誤");
                  return AppException(
                      errCode, error.response?.statusMessage ?? '');
                }
            }
          } on Exception catch (_) {
            return AppException(-1, "未知錯(cuò)誤");
          }
        }
        break;
      default:
        {
          return AppException(-1, error.message);
        }
    }
  }
}

/// 請求錯(cuò)誤
class BadRequestException extends AppException {
  BadRequestException([int? code, String? message]) : super(code!, message!);
}

/// 未認(rèn)證異常
class UnauthorisedException extends AppException {
  UnauthorisedException([int code = -1, String message = ''])
      : super(code, message);
}

cache.dart

// 是否啟用緩存
const CACHE_ENABLE = true;

// 緩存的最長時(shí)間亮蛔,單位(秒)
const CACHE_MAXAGE = 1000;

// 最大緩存數(shù)
const CACHE_MAXCOUNT = 100;

connections_interceptor.dart

import 'package:connectivity/connectivity.dart';
import 'package:dio/dio.dart';

class ConnectsInterceptor extends Interceptor {
  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    // TODO: implement onRequest
    super.onRequest(options, handler);
    _request();
  }

  //不知道是不是這樣寫吗货,網(wǎng)絡(luò)的
  _request() async {
    var connectivityResult = await (Connectivity().checkConnectivity());
    if (connectivityResult == ConnectivityResult.mobile) {
      // I am connected to a mobile network.
    } else if (connectivityResult == ConnectivityResult.wifi) {
      // I am connected to a wifi network.
    } else {
      print('沒有網(wǎng)絡(luò)');
      //在這里加一個(gè)錯(cuò)誤彈窗
    }
  }

  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) {
    // TODO: implement onResponse
    super.onResponse(response, handler);
  }

  @override
  void onError(DioError err, ErrorInterceptorHandler handler) {
    // TODO: implement onError
    super.onError(err, handler);
  }
}

http.dart

import 'dart:io';

import 'package:dio/adapter.dart';
import 'package:dio/dio.dart';
import 'package:pp/MyHttp/connections_interceptor.dart';
import 'package:pp/MyHttp/net_cache.dart';
import 'package:pp/MyHttp/proxy.dart';
import 'package:pp/MyHttp/request_interceptor.dart';

class Https {
  ///超時(shí)時(shí)間
  static const int CONNECT_TIMEOUT = 10000;
  static const int RECEIVE_TIMEOUT = 10000;

  static Https instance = Https._internal();

  factory Https() => instance;

  Dio dio = Dio();
  CancelToken _cancelToken = new CancelToken();
  Https._internal() {
    dio.options
      ..baseUrl = 'https://mobile.pku-hit.com/smc/'
      ..connectTimeout = CONNECT_TIMEOUT
      ..receiveTimeout = RECEIVE_TIMEOUT
      ..validateStatus = (int? status) {
        return status != null && status > 0;
      }
      ..headers = {};
    dio.interceptors.add(RequestInterceptor()); //自定義攔截
    dio.interceptors.add(ConnectsInterceptor());//攔截網(wǎng)絡(luò)
    dio.interceptors.add(LogInterceptor()); //打開日志
    dio.interceptors.add(NetCacheInterceptor()); //緩存

    // 在調(diào)試模式下需要抓包調(diào)試模聋,所以我們使用代理痊远,并禁用HTTPS證書校驗(yàn)
    if (PROXY_ENABLE) {
      (dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
          (client) {
        client.findProxy = (uri) {
          return "PROXY $PROXY_IP:$PROXY_PORT";
        };
        //代理工具會(huì)提供一個(gè)抓包的自簽名證書哩簿,會(huì)通不過證書校驗(yàn)后添,所以我們禁用證書校驗(yàn)
        client.badCertificateCallback =
            (X509Certificate cert, String host, int port) => true;
      };
    }
  }
  void init(//這個(gè)在main或者初始化的時(shí)候先調(diào)用一下
      {String? baseUrl,
      int? connectTimeout,
      int? receiveTimeout,
      List<Interceptor>? interceptors}) {
    dio.options = dio.options.copyWith(
      baseUrl: baseUrl,
      connectTimeout: connectTimeout,
      receiveTimeout: receiveTimeout,
    );
    if (interceptors != null && interceptors.isNotEmpty) {
      dio.interceptors.addAll(interceptors);
    }
  }

  /// 設(shè)置headers
  void setHeaders(Map<String, dynamic> map) {
    dio.options.headers.addAll(map);
  }

/*
   * 取消請求
   *
   * 同一個(gè)cancel token 可以用于多個(gè)請求物臂,當(dāng)一個(gè)cancel token取消時(shí)笛丙,所有使用該cancel token的請求都會(huì)被取消颇蜡。
   * 所以參數(shù)可選
   */
  void cancelRequests({CancelToken? token}) {
    token ?? _cancelToken.cancel("cancelled");
  }

  /// restful get 操作
  Future get(
    String path, {
    Map<String, dynamic>? params,
    Options? options,
    CancelToken? cancelToken,
    bool refresh = false,
    bool noCache = true,
    String? cacheKey,
    bool cacheDisk = false,
  }) async {
    Options requestOptions = options ?? Options();
    requestOptions = requestOptions.copyWith(extra: {
      "refresh": refresh,
      "noCache": noCache,
      "cacheKey": cacheKey,
      "cacheDisk": cacheDisk,
    });
    Response response;
    response = await dio.get(path,
        queryParameters: params,
        options: requestOptions,
        cancelToken: cancelToken ?? _cancelToken);
    return response.data;
  }

  /// restful post 操作
  Future post(
    String path, {
    Map<String, dynamic>? params,
    data,
    Options? options,
    CancelToken? cancelToken,
  }) async {
    Options requestOptions = options ?? Options();
    var response = await dio.post(path,
        data: data,
        queryParameters: params,
        options: requestOptions,
        cancelToken: cancelToken ?? _cancelToken);
    return response.data;
  }

  /// restful put 操作
  Future put(
    String path, {
    data,
    Map<String, dynamic>? params,
    Options? options,
    CancelToken? cancelToken,
  }) async {
    Options requestOptions = options ?? Options();

    var response = await dio.put(path,
        data: data,
        queryParameters: params,
        options: requestOptions,
        cancelToken: cancelToken ?? _cancelToken);
    return response.data;
  }

  /// restful patch 操作
  Future patch(
    String path, {
    data,
    Map<String, dynamic>? params,
    Options? options,
    CancelToken? cancelToken,
  }) async {
    Options requestOptions = options ?? Options();
    // Map<String, dynamic> _authorization = getAuthorizationHeader();
    // if (_authorization != null) {
    //   requestOptions = requestOptions.merge(headers: _authorization);
    // }
    var response = await dio.patch(path,
        data: data,
        queryParameters: params,
        options: requestOptions,
        cancelToken: cancelToken ?? _cancelToken);
    return response.data;
  }

  /// restful delete 操作
  Future delete(
    String path, {
    data,
    Map<String, dynamic>? params,
    Options? options,
    CancelToken? cancelToken,
  }) async {
    Options requestOptions = options ?? Options();
    var response = await dio.delete(path,
        data: data,
        queryParameters: params,
        options: requestOptions,
        cancelToken: cancelToken ?? _cancelToken);
    return response.data;
  }

  /// restful post form 表單提交操作
  Future postForm(
    String path, {
    Map<String, dynamic>? params,
    Options? options,
    CancelToken? cancelToken,
  }) async {
    Options requestOptions = options ?? Options();
    var data = FormData.fromMap(params!);
    var response = await dio.post(path,
        data: data,
        options: requestOptions,
        cancelToken: cancelToken ?? _cancelToken);
    return response.data;
  }
}

net_cache.dart

import 'dart:collection';
import 'package:dio/dio.dart';
import 'package:pp/MyHttp/cache.dart';
import 'package:pp/MyHttp/sp.dart';

class CacheObject {
  CacheObject(this.response)
      : timeStamp = DateTime.now().millisecondsSinceEpoch;
  Response response;
  int timeStamp;

  @override
  bool operator ==(other) {
    return response.hashCode == other.hashCode;
  }

  @override
  int get hashCode => response.realUri.hashCode;
}

class NetCacheInterceptor extends Interceptor {
  var cache = LinkedHashMap<String, CacheObject>();
  @override
  Future<void> onRequest(
      RequestOptions options, RequestInterceptorHandler handler) async {
    // TODO: implement onRequest
    super.onRequest(options, handler);
    if (!CACHE_ENABLE) handler.next(options);

    // refresh標(biāo)記是否是刷新緩存
    bool refresh = options.extra["refresh"] == true;

    // 是否磁盤緩存
    bool cacheDisk = options.extra["cacheDisk"] == true;

    // 如果刷新价说,先刪除相關(guān)緩存
    if (refresh) {
      // 刪除uri相同的內(nèi)存緩存
      delete(options.uri.toString());

      // 刪除磁盤緩存
      if (cacheDisk) {
        await SpUtil().remove(options.uri.toString());
      }

      handler.next(options);
    }

    // get 請求,開啟緩存
    if (options.extra["noCache"] != true &&
        options.method.toLowerCase() == 'get') {
      String key = options.extra["cacheKey"] ?? options.uri.toString();

      // 策略 1 內(nèi)存緩存優(yōu)先风秤,2 然后才是磁盤緩存

      // 1 內(nèi)存緩存
      var ob = cache[key];
      if (ob != null) {
        //若緩存未過期鳖目,則返回緩存內(nèi)容
        if ((DateTime.now().millisecondsSinceEpoch - ob.timeStamp) / 1000 <
            CACHE_MAXAGE) {
          handler.resolve(cache[key]!.response);
        } else {
          //若已過期則刪除緩存,繼續(xù)向服務(wù)器請求
          cache.remove(key);
        }
      }

      // 2 磁盤緩存
      if (cacheDisk) {
        var cacheData = SpUtil().getJSON(key);
        if (cacheData != null) {
          handler.resolve(Response(
            statusCode: 200,
            data: cacheData,
            requestOptions: options,
          ));
        }
      }
    }
  }

  @override
  Future<void> onResponse(
      Response response, ResponseInterceptorHandler handler) async {
    // TODO: implement onResponse
    super.onResponse(response, handler);
    // 如果啟用緩存缤弦,將返回結(jié)果保存到緩存
    if (CACHE_ENABLE) {
      await _saveCache(response);
    }
  }

  @override
  void onError(DioError err, ErrorInterceptorHandler handler) {
    // TODO: implement onError
    super.onError(err, handler);
  }

  void delete(String key) {
    cache.remove(key);
  }

  Future<void> _saveCache(Response object) async {
    RequestOptions options = object.requestOptions;

    // 只緩存 get 的請求
    if (options.extra["noCache"] != true &&
        options.method.toLowerCase() == "get") {
      // 策略:內(nèi)存领迈、磁盤都寫緩存

      // 緩存key
      String key = options.extra["cacheKey"] ?? options.uri.toString();

      // 磁盤緩存
      if (options.extra["cacheDisk"] == true) {
        await SpUtil().setJSON(key, object.data);
      }

      // 內(nèi)存緩存
      // 如果緩存數(shù)量超過最大數(shù)量限制,則先移除最早的一條記錄
      if (cache.length == CACHE_MAXCOUNT) {
        cache.remove(cache[cache.keys.first]);
      }

      cache[key] = CacheObject(object);
    }
  }
}

proxy.dart

// 是否啟用代理
const PROXY_ENABLE = false;

/// 代理服務(wù)IP
const PROXY_IP = '192.168.2.237';

/// 代理服務(wù)端口
const PROXY_PORT = 8888;

request_interceptor.dart

import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'app_exceptions.dart';

/// 請求處理攔截器
class RequestInterceptor extends Interceptor {
  onRequest(options, handle) {
    debugPrint(
        '======================\n*** Request *** \nData:\n ${options.data.toString()} \nQuery:\n ${options.queryParameters.toString()} \n======================');
    // // 設(shè)置cookie
    // var cookie = SpUtil.getStringList('cookie');登錄時(shí)保存cookie

    // if (options.path != 'api/auth/login' &&
    //     cookie != null &&
    //     cookie.length > 0) {
    //   options.headers['cookie'] = cookie;//這里就是除了登錄的情況其他都加cookie
    // }
    // options.headers['User-Agent'] = 'gzzoc-1';//
    //
    // if (options.data?.runtimeType == FormData) {
    //   options.headers['content-type'] = 'multipart/form-data';//FormData這種情況改content-type
    // }

    // // 加載動(dòng)畫----這個(gè)就是請求頁面時(shí)的那個(gè)loading窗 --處理邏輯 我是用options?.data['showLoading']或options?.queryParameters['showLoading'],
    //就是我們在傳參的時(shí)候多加一個(gè)參數(shù)甸鸟,這個(gè)因?yàn)榍叭司瓦@樣做的惦费,也跟后端約定的,后端見showLoading不做處理抢韭。這樣不是很好薪贫,反正options是有其他字段加的
    // if (options?.data?.runtimeType == FormData) {
    //   Alert.showLoading();
    // } else if ((options?.data != null &&
    //         false == options?.data['showLoading']) ||
    //     (options?.queryParameters != null &&
    //         false == options?.queryParameters['showLoading'])) {
    //   // 不顯示加載動(dòng)畫
    // } else {
    //   Alert.showLoading();
    // }
    ///在這做請求時(shí)顯不顯示Loading的處理

    handle.next(options);
    //return super.onRequest(options);
  }

  @override
  onResponse(response, handle) {
    debugPrint(
        '======================\n*** Response *** \n${response.toString()}');
    if (response.data != null &&
        response.data is Map &&
        response.data['code'] == '0') {// 這個(gè)條件也是根據(jù)自己情況加的
      ///    Alert.hide();請求成功后關(guān)閉loading窗

      // 登錄請求
      if (response.requestOptions.path == 'api/auth/login') {
        // 緩存cookie
        var cookie = response.headers['set-cookie'];
        //   SpUtil.putStringList('cookie', cookie!);緩存cookie
      }
      handle.next(response);
      //     return super.onResponse(response);
    } else if (response.requestOptions.path ==
            'api/auth/login' && // 登錄登錄成功, 但沒有默認(rèn)就診人// 緩存cookie以便后續(xù)創(chuàng)建默認(rèn)就診人(需求)
        response.data != null &&
        response.data is Map &&
        response.data['code'] == '11') {
      // 緩存cookie
      var cookie = response.headers['set-cookie'];
      //    SpUtil.putStringList('cookie', cookie!);

      //     Alert.hide();
      handle.next(response);
    } else {
      handle.reject(DioError(
          requestOptions: response.requestOptions,
          error: response.data != null &&
                  response.data is Map &&
                  response.data['msg'] != null &&
                  response.data['msg'].length > 0
              ? response.data['msg']
              : '未知錯(cuò)誤',
          response: response));
    }
  }

  @override
  onError(err, handle) {
    // Alert.hide();關(guān)閉彈窗

    // 賬戶登錄異常
    if (err.response != null &&
        err.response?.data != null &&
        err.response?.data is Map &&
        err.response?.data != null &&
        err.response?.data['code'] == '2') {
      // Alert.showAlert(
      //   message: err.message ?? '未知錯(cuò)誤',
      //   showCancel: false,
      //   onConfirm: () {
      //     // 清除賬號緩存
      //     SpUtil.putString("account_phone", '');
      //     SpUtil.putString("account_password", '');
      //     SpUtil.putObject("account", '');
      //
      //     // 退出到登錄頁面
      //     //  push(Routes.login, replace: true, clearStack: true);
      //   },
      // );
    } else {
      //    Alert.showAlert(message: err.message ?? '未知錯(cuò)誤', showCancel: false);//在頁面顯示一個(gè)錯(cuò)誤彈窗
    }
    AppException appException = AppException.create(err);
    err.error = appException;
    return super.onError(err, handle);
  }
}

sp.dart

import 'dart:convert';

import 'package:shared_preferences/shared_preferences.dart';

/// 本地存儲(chǔ)
class SpUtil {
  static SpUtil _instance = new SpUtil._();
  factory SpUtil() => _instance;
  static SharedPreferences? _prefs;

  SpUtil._();

  static Future<void> init() async {
    if (_prefs == null) {
      _prefs = await SharedPreferences.getInstance();
    }
  }

  Future<bool> setJSON(String key, dynamic jsonVal) {
    String jsonString = jsonEncode(jsonVal);
    return _prefs!.setString(key, jsonString);
  }

  dynamic getJSON(String key) {
    String? jsonString = _prefs?.getString(key);
    return jsonDecode(jsonString!);
  }

  Future<bool> setBool(String key, bool val) {
    return _prefs!.setBool(key, val);
  }

  bool? getBool(String key) {
    bool? val = _prefs?.getBool(key);
    return val;
  }

  Future<bool> remove(String key) {
    return _prefs!.remove(key);
  }
}

下面就是使用了:
首先先建個(gè)model(為了方便哪種)我一般用https://javiercbk.github.io/json_to_dart/建滴。
get_science_article_entity.dart

class GetScienceArticleEntity {
  String? code;
  String? msg;
  Data? data;

  GetScienceArticleEntity({this.code, this.msg, this.data});

  GetScienceArticleEntity.fromJson(Map<String, dynamic> json) {
    code = json['code'];
    msg = json['msg'];
    data = json['data'] != null ? new Data.fromJson(json['data']) : null;
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['code'] = this.code;
    data['msg'] = this.msg;
    if (this.data != null) {
      data['data'] = this.data?.toJson();
    }
    return data;
  }
}

class Data {
  int? pageCount;
  int? pageTotal;
  int? pageSize;
  List<PageList>? pageList;
  int? pageNum;

  Data(
      {this.pageCount,
      this.pageTotal,
      this.pageSize,
      this.pageList,
      this.pageNum});

  Data.fromJson(Map<String, dynamic> json) {
    pageCount = json['pageCount'];
    pageTotal = json['pageTotal'];
    pageSize = json['pageSize'];
    if (json['pageList'] != null) {
      pageList = <PageList>[];
      json['pageList'].forEach((v) {
        pageList?.add(new PageList.fromJson(v));
      });
    }
    pageNum = json['pageNum'];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['pageCount'] = this.pageCount;
    data['pageTotal'] = this.pageTotal;
    data['pageSize'] = this.pageSize;
    if (this.pageList != null) {
      data['pageList'] = this.pageList?.map((v) => v.toJson()).toList();
    }
    data['pageNum'] = this.pageNum;
    return data;
  }
}

class PageList {
  int? articleId;
  String? articleTitle;
  int? articleType;
  String? articleContent;
  int? articleContentType;
  int? articleStatus;
  String? articleCreateTime;
  String? articleUpdateTime;
  int? articleSort;

  PageList(
      {this.articleId,
      this.articleTitle,
      this.articleType,
      this.articleContent,
      this.articleContentType,
      this.articleStatus,
      this.articleCreateTime,
      this.articleUpdateTime,
      this.articleSort});

  PageList.fromJson(Map<String, dynamic> json) {
    articleId = json['articleId'];
    articleTitle = json['articleTitle'];
    articleType = json['articleType'];
    articleContent = json['articleContent'];
    articleContentType = json['articleContentType'];
    articleStatus = json['articleStatus'];
    articleCreateTime = json['articleCreateTime'];
    articleUpdateTime = json['articleUpdateTime'];
    articleSort = json['articleSort'];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = new Map<String, dynamic>();
    data['articleId'] = this.articleId;
    data['articleTitle'] = this.articleTitle;
    data['articleType'] = this.articleType;
    data['articleContent'] = this.articleContent;
    data['articleContentType'] = this.articleContentType;
    data['articleStatus'] = this.articleStatus;
    data['articleCreateTime'] = this.articleCreateTime;
    data['articleUpdateTime'] = this.articleUpdateTime;
    data['articleSort'] = this.articleSort;
    return data;
  }
}

創(chuàng)一個(gè)類(為了方便而已)
testApi.dart

import 'package:dio/dio.dart';
import 'package:pp/MyHttp/api_response.dart';
import 'package:pp/MyHttp/http.dart';
import 'package:pp/get_science_article_entity.dart';

class TestApi {
  static String _article = 'api/article/getScienceArticle';

  static Future<ApiResponse<GetScienceArticleEntity>> getScienceArticle(
      {int? pageNum}) async {
    try {
      final response =
          await Https.instance.get(_article, params: {"pageNum": pageNum});
      var data = GetScienceArticleEntity.fromJson(response);
      return ApiResponse.completed(data);
    } on DioError catch (e) {
      return ApiResponse.error(e.error);
    }
  }
}

使用:

  void _do() async {
    ApiResponse<GetScienceArticleEntity> res =
        await TestApi.getScienceArticle();
    if (res.status != Status.COMPLETED) return;
    print(res.data?.data?.pageCount);
  }

結(jié)果:


171625219598_.pic_hd.jpg
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末刻恭,一起剝皮案震驚了整個(gè)濱河市瞧省,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌鳍贾,老刑警劉巖鞍匾,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異骑科,居然都是意外死亡橡淑,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進(jìn)店門咆爽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來梁棠,“玉大人置森,你說我怎么就攤上這事》” “怎么了凫海?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長男娄。 經(jīng)常有香客問我行贪,道長,這世上最難降的妖魔是什么模闲? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任建瘫,我火速辦了婚禮,結(jié)果婚禮上尸折,老公的妹妹穿的比我還像新娘暖混。我一直安慰自己,他們只是感情好翁授,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著晾咪,像睡著了一般收擦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上谍倦,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天塞赂,我揣著相機(jī)與錄音,去河邊找鬼昼蛀。 笑死宴猾,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的叼旋。 我是一名探鬼主播仇哆,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼夫植!你這毒婦竟也來了讹剔?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤详民,失蹤者是張志新(化名)和其女友劉穎延欠,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體沈跨,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡由捎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了饿凛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片狞玛。...
    茶點(diǎn)故事閱讀 39,795評論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡软驰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出为居,到底是詐尸還是另有隱情碌宴,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布蒙畴,位于F島的核電站贰镣,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏膳凝。R本人自食惡果不足惜碑隆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蹬音。 院中可真熱鬧上煤,春花似錦、人聲如沸著淆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽永部。三九已至独泞,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間苔埋,已是汗流浹背懦砂。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留组橄,地道東北人荞膘。 一個(gè)月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像玉工,于是被迫代替她去往敵國和親羽资。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評論 2 354

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