iOS 封装AFNetworking

1.请求配置类

//
//  SFRequestConfig.h
//  template
//
//  Created by shefeng on 2024/6/20.
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface SFRequestConfig : NSObject

/**
 *  是否允许无效的证书
 */
@property (assign, nonatomic) BOOL allowInvalidCertificates;

/**
 *  超时时间
 */
@property (assign, nonatomic) NSInteger timeoutInterval;

/**
 *  是否允许无效的证书
 */
@property (assign, nonatomic) BOOL validatesDomainName;

/**
 *  可接收的ContentType类型
 */
@property (strong, nonatomic) NSSet <NSString *> *acceptableContentTypes;


/**
 *  单例
 */
+ (instancetype)sharedConfig;

@end

NS_ASSUME_NONNULL_END

//
//  SFRequestConfig.m
//  template
//
//  Created by shefeng on 2024/6/20.
//

#import "SFRequestConfig.h"

@implementation SFRequestConfig

+ (instancetype)sharedConfig{
    static dispatch_once_t onceToken;
    static SFRequestConfig *instance;
    dispatch_once(&onceToken, ^{
        instance = [[SFRequestConfig alloc] init];
    });
    return instance;
}

- (NSSet<NSString *> *)acceptableContentTypes{
    if (_acceptableContentTypes == nil) {
        _acceptableContentTypes = [NSSet setWithObjects:@"text/html",@"image/jpeg",@"image/png",@"text/plain",@"application/json",nil];
    }
    return _acceptableContentTypes;
}

@end

2.响应配置类

//
//  SFResponseConfig.h
//  template
//
//  Created by shefeng on 2024/6/24.
//

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface SFResponseConfig : NSObject

/**
 *  code的key
 */
@property (strong, nonatomic) NSString *codeKey;

/**
 *  data的key
 */
@property (strong, nonatomic) NSString *dataKey;

/**
 *  message的key
 */
@property (strong, nonatomic) NSString *messageKey;

/**
 *  成功的状态码
 */
@property (strong, nonatomic) NSNumber *successedCode;

/**
 *  失败的状态码
 */
@property (strong, nonatomic) NSNumber *failedCode;

/**
 *  默认的错误提示语
 */
@property (strong, nonatomic) NSString *defaultMessageValue;

/**
 *  单例
 */
+ (instancetype)sharedConfig;

@end

NS_ASSUME_NONNULL_END

//
//  SFResponseConfig.m
//  template
//
//  Created by shefeng on 2024/6/24.
//

#import "SFResponseConfig.h"

#define ResponseCodeDefaultKey @"code"
#define ResponseDataDefaultKey @"data"
#define ResponseMessageDefaultKey @"message"
#define ResponseCodeSuccessedDefaultValue @0
#define ResponseCodeFailedDefaultValue @500
#define ResponseMessageDefaultValue @"请求失败"

@implementation SFResponseConfig

+ (instancetype)sharedConfig{
    static dispatch_once_t onceToken;
    static SFResponseConfig *instance;
    dispatch_once(&onceToken, ^{
        instance = [[SFResponseConfig alloc] init];
    });
    return instance;
}

- (NSString *)codeKey{
    if (_codeKey == nil || [@"" isEqualToString:_codeKey]) {
        return ResponseCodeDefaultKey;
    }
    return _codeKey;
}

- (NSString *)dataKey{
    if (_dataKey == nil || [@"" isEqualToString:_dataKey]) {
        return ResponseDataDefaultKey;
    }
    return _dataKey;
}

- (NSString *)messageKey{
    if (_messageKey == nil || [@"" isEqualToString:_messageKey]) {
        return ResponseMessageDefaultKey;
    }
    return _messageKey;
}

- (NSNumber *)successedCode{
    if (_successedCode == nil) {
        return ResponseCodeSuccessedDefaultValue;
    }
    return _successedCode;
}

- (NSNumber *)failedCode{
    if (_failedCode == nil) {
        return ResponseCodeFailedDefaultValue;
    }
    return _failedCode;
}

- (NSString *)defaultMessageValue{
    if (_defaultMessageValue == nil || [@"" isEqualToString:_defaultMessageValue]) {
        return ResponseMessageDefaultValue;
    }
    return _defaultMessageValue;
}

@end

3.网络请求基类

//
//  SFBaseRequest.h
//  template
//
//  Created by shefeng on 2024/6/20.
//

#import <Foundation/Foundation.h>
#import <AFNetworking/AFNetworking.h>

NS_ASSUME_NONNULL_BEGIN

typedef NS_ENUM(NSInteger, SFContentType){
    ApplicationJson,
    ApplicationXWwwFormUrlencoded,
    MultipartFormData 
};

@interface SFBaseRequest : NSObject

+ (AFHTTPSessionManager *)sharedManager;

/**
 *  GET请求
 *
 *  @param urlString 请求url
 *  @param params 请求参数
 *  @param headers header参数
 *  @param completionBlock 完成的block
 */
+ (void)GET:(NSString *)urlString params:(NSDictionary *)params headers:(NSDictionary *)headers completionBlock:(void(^)(NSInteger code,id data,NSString *message))completionBlock;

/**
 *  POST请求
 *
 *  @param urlString 请求url
 *  @param params 请求参数
 *  @param headers header参数
 *  @param completionBlock 完成的block
 */
+ (void)POST:(NSString *)urlString params:(NSDictionary *)params headers:(NSDictionary *)headers completionBlock:(void(^)(NSInteger code,id data,NSString *message))completionBlock;

/**
 *  POST请求
 *
 *  @param urlString 请求url
 *  @param params 请求参数
 *  @param headers header参数  
 *  @param contentType 请求Content-Type
 *  @param completionBlock 完成的block
 */
+ (void)POST:(NSString *)urlString params:(NSDictionary *)params headers:(NSDictionary *)headers contentType:(SFContentType)contentType completionBlock:(void(^)(NSInteger code,id data,NSString *message))completionBlock;

@end

NS_ASSUME_NONNULL_END
//
//  SFBaseRequest.m
//  template
//
//  Created by shefeng on 2024/6/20.
//

#import "SFBaseRequest.h"
#import <MJExtension/MJExtension.h>
#import "SFRequestConfig.h"
#import "SFResponseConfig.h"

#define ResponseFailedDefaultValue @{[SFResponseConfig sharedConfig].codeKey:[SFResponseConfig sharedConfig].failedCode,[SFResponseConfig sharedConfig].messageKey:[SFResponseConfig sharedConfig].defaultMessageValue}

@implementation SFBaseRequest

+ (AFHTTPSessionManager *)sharedManager {
    static AFHTTPSessionManager *instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [AFHTTPSessionManager manager];
        
        AFSecurityPolicy *securityPolicy = [AFSecurityPolicy defaultPolicy];
        // 客户端是否信任非法证书
        securityPolicy.allowInvalidCertificates = [SFRequestConfig sharedConfig].allowInvalidCertificates;
        // 是否在证书域字段中验证域名
        securityPolicy.validatesDomainName = [SFRequestConfig sharedConfig].validatesDomainName;
        instance.securityPolicy = securityPolicy;
        instance.requestSerializer.timeoutInterval = [SFRequestConfig sharedConfig].timeoutInterval;
        instance.responseSerializer.acceptableContentTypes = [SFRequestConfig sharedConfig].acceptableContentTypes;
    });
    return instance;
}

/**
 *  GET请求
 *
 *  @param urlString 请求url
 *  @param params 请求参数
 *  @param headers header参数
 *  @param completionBlock 完成的block
 */
+ (void)GET:(NSString *)urlString params:(NSDictionary *)params headers:(NSDictionary *)headers completionBlock:(void(^)(NSInteger code,id data,NSString *message))completionBlock{
    [[SFBaseRequest sharedManager] GET:urlString parameters:params headers:headers progress:^(NSProgress * _Nonnull downloadProgress) {
    } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        [self handleSuccess:urlString params:params responseObject:responseObject completionBlock:completionBlock];
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        [self handleError:urlString params:params errMsg:error.localizedDescription completionBlock:completionBlock];
    }];
}

/**
 *  POST请求
 *
 *  @param urlString 请求url
 *  @param params 请求参数
 *  @param headers header参数
 *  @param completionBlock 完成的block
 */
+ (void)POST:(NSString *)urlString params:(NSDictionary *)params headers:(NSDictionary *)headers completionBlock:(void(^)(NSInteger code,id data,NSString *message))completionBlock{
    [self POST:urlString params:params headers:headers contentType:ApplicationXWwwFormUrlencoded completionBlock:completionBlock];
}

/**
 *  POST请求
 *
 *  @param urlString 请求url
 *  @param params 请求参数
 *  @param headers header参数
 *  @param contentType 请求Content-Type
 *  @param completionBlock 完成的block
 */
+ (void)POST:(NSString *)urlString params:(NSDictionary *)params headers:(NSDictionary *)headers contentType:(SFContentType)contentType completionBlock:(void(^)(NSInteger code,id data,NSString *message))completionBlock{
    if (contentType == ApplicationXWwwFormUrlencoded) {
        [[self sharedManager].requestSerializer setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
        
        [[SFBaseRequest sharedManager] POST:urlString parameters:params headers:headers progress:^(NSProgress * _Nonnull uploadProgress) {
        } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
            [self handleSuccess:urlString params:params responseObject:responseObject completionBlock:completionBlock];
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            [self handleError:urlString params:params errMsg:error.localizedDescription completionBlock:completionBlock];
        }];
    }else if (contentType == ApplicationJson) {
        [[self sharedManager].requestSerializer setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
        
        NSMutableURLRequest *request = [[AFJSONRequestSerializer serializer] requestWithMethod:@"POST" URLString:urlString parameters:params error:nil];

        [request setValue:@"application/json"forHTTPHeaderField:@"Content-Type"];
        
        if(headers != nil && headers.allKeys.count != 0){
            for (NSString *key in headers.allKeys) {
                NSString *value = [headers objectForKey:key];
                [request addValue:value forHTTPHeaderField:key];
            }
        }

        NSData *body = [NSJSONSerialization dataWithJSONObject:params options:NSJSONWritingPrettyPrinted error:nil];

        [request setHTTPBody:body];
        
        [[[SFBaseRequest sharedManager] dataTaskWithRequest:request uploadProgress:^(NSProgress * _Nonnull uploadProgress) {
                    
        } downloadProgress:^(NSProgress * _Nonnull downloadProgress) {
            
        } completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {
            if (!error) {
                [self handleSuccess:urlString params:params responseObject:responseObject completionBlock:completionBlock];
            }else{
                [self handleError:urlString params:params errMsg:error.localizedDescription completionBlock:completionBlock];
            }
        }] resume];
    }else if (contentType == MultipartFormData) {
        [[self sharedManager].requestSerializer setValue:@"multipart/form-data" forHTTPHeaderField:@"Content-Type"];
        
        [[SFBaseRequest sharedManager] POST:urlString parameters:params headers:headers constructingBodyWithBlock:^(id<AFMultipartFormData>  _Nonnull formData) {
            NSArray *keyArray = [params allKeys];
            
            for (NSString *key in keyArray) {
                id value = params[key];
                if ([value isKindOfClass:[NSString class]]) {
                   [formData appendPartWithFormData:[value dataUsingEncoding:NSUTF8StringEncoding] name:key];
                }else if ([value isKindOfClass:[NSMutableArray class]]) {
                    [formData appendPartWithFormData:[NSKeyedArchiver archivedDataWithRootObject:value requiringSecureCoding:YES error:nil] name:key];
                }else if ([value isKindOfClass:[NSArray class]]) {
                    [formData appendPartWithFormData:[NSKeyedArchiver archivedDataWithRootObject:value requiringSecureCoding:YES error:nil] name:key];
                }
                
            }
        } progress:^(NSProgress * _Nonnull uploadProgress) {
        } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
            [self handleSuccess:urlString params:params responseObject:responseObject completionBlock:completionBlock];
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            [self handleError:urlString params:params errMsg:error.localizedDescription completionBlock:completionBlock];
        }];
    }
}

/**
 *  处理错误响应
 *
 *  @param url 请求地址
 *  @param params 请求参数
 *  @param errMsg 错误信息
 *  @param completionBlock 完成的Block
 */
+ (void)handleError:(NSString *)url params:(NSDictionary *)params errMsg:(NSString *)errMsg completionBlock:(void(^)(NSInteger code,id data,NSString *message))completionBlock{
    [self printLog:url params:params info:errMsg];
    completionBlock([SFResponseConfig sharedConfig].failedCode.integerValue,nil,errMsg);
}

#pragma mark - private
/**
 *  处理成功响应
 *
 *  @param url 请求地址
 *  @param params 请求参数
 *  @param responseObject 响应数据
 *  @param completionBlock 完成的Block
 */
+ (void)handleSuccess:(NSString *)url params:(NSDictionary *)params responseObject:(id)responseObject completionBlock:(void(^)(NSInteger code,id data,NSString *message))completionBlock{
    if(responseObject == nil){
        [self handleError:url params:params errMsg:@"请求无响应" completionBlock:completionBlock];
    }else {
        if ([responseObject isKindOfClass:[NSDictionary class]]) {
            NSDictionary *responseDict = (NSDictionary *)responseObject;
            if ([responseDict.allKeys containsObject:[SFResponseConfig sharedConfig].codeKey] 
                && [responseDict.allKeys containsObject:[SFResponseConfig sharedConfig].messageKey]
                && [responseDict.allKeys containsObject:[SFResponseConfig sharedConfig].dataKey]) {
                NSInteger code = [[responseDict objectForKey:[SFResponseConfig sharedConfig].codeKey] integerValue];
                id data = [responseDict objectForKey:[SFResponseConfig sharedConfig].dataKey];
                NSString *message = [responseDict objectForKey:[SFResponseConfig sharedConfig].messageKey];
                [self printLog:url params:params info:[responseDict mj_JSONString]];
                completionBlock(code,data,message);
            }else{
                [self handleError:url params:params errMsg:@"返回数据格式不正确" completionBlock:completionBlock];
            }
        }else{
            [self handleError:url params:params errMsg:@"返回数据格式不正确" completionBlock:completionBlock];
        }
    }
}

/**
 *  打印log
 *
 *  @param url 请求地址
 *  @param params 请求参数
 *  @param info 响应信息
 */
+ (void)printLog:(NSString *)url params:(NSDictionary *)params info:(NSString *)info{
#ifdef DEBUG
    NSString *urlInfo = [NSString stringWithFormat:@"请求地址:%@",url];
    NSString *paramInfo = [NSString stringWithFormat:@"请求参数:%@",(params == nil ? @"" : [params mj_JSONString])];
    NSString *responseInfo = [NSString stringWithFormat:@"请求响应:%@",info];
    
    NSInteger maxLength = urlInfo.length;
    
    if (paramInfo.length > maxLength) {
        maxLength = paramInfo.length;
    }
    
    if (responseInfo.length > maxLength) {
        maxLength = responseInfo.length;
    }
    
    NSLog(@"%@",[self buildLogString:maxLength info:nil]);
    NSLog(@"%@",[self buildLogString:maxLength info:urlInfo]);
    NSLog(@"%@",[self buildLogString:maxLength info:paramInfo]);
    NSLog(@"%@",[self buildLogString:maxLength info:responseInfo]);
    NSLog(@"%@",[self buildLogString:maxLength info:nil]);
#endif
}

/**
 *  构建打印信息
 *
 *  @param maxLength 最大长度
 *  @param info 信息
 *
 */
+ (NSString *)buildLogString:(NSInteger)maxLength info:(NSString *)info{
    //非第一行和最后一行的左侧*数量
    NSInteger leftStarLength = 0;
    //非第一行和最后一行的左侧*后的空格数量
    NSInteger leftSpaceLength = 0;
    
    //实际最大长度
    maxLength += (leftStarLength + leftSpaceLength) * 2;
    
    NSMutableString *mutableStr = [[NSMutableString alloc] init];
    
    if(info == nil){
        for (int i = 0; i < maxLength; i++) {
            [mutableStr appendString:@"="];
        }
    }else{
        for (int i = 0; i < leftStarLength; i++) {
            [mutableStr appendString:@""];
        }
        
        for (int i = 0; i < leftSpaceLength; i++) {
            [mutableStr appendString:@""];
        }
        
        [mutableStr appendString:info];
        
        NSInteger rightSpaceLength = maxLength - leftStarLength - leftSpaceLength - info.length - leftStarLength;
        
        for (int i = 0; i < rightSpaceLength; i++) {
            [mutableStr appendString:@""];
        }
        
        for (int i = 0; i < leftStarLength; i++) {
            [mutableStr appendString:@""];
        }
    }
    
    return mutableStr;
}

@end

4.Java后台相关代码


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class TestObj {

    private String name;
    private String address;
}

package com.geenju.bcard.modules.app.controller;

import com.geenju.bcard.common.api.CommonResult;
import com.geenju.bcard.modules.app.dto.TestObj;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

@Slf4j
@RestController
@RequestMapping("/api/test")
public class TestController {

    @RequestMapping(value = "/get", method = RequestMethod.GET)
    @ResponseBody
    public CommonResult get(@RequestParam(value = "argsA") String argsA) {
        log.info("get - argsA=" + argsA);
        return CommonResult.success("");
    }

    @RequestMapping(value = "/postFormData", method = RequestMethod.POST)
    @ResponseBody
    public CommonResult postFormData(@RequestParam(value = "argsA") String argsA) {
        log.info("postFormData - argsA=" + argsA);
        return CommonResult.success("");
    }

    @RequestMapping(value = "/postXWwwFormUrlencoded", method = RequestMethod.POST)
    @ResponseBody
    public CommonResult postXWwwFormUrlencoded(@RequestParam(value = "argsA") String argsA) {
        log.info("postXWwwFormUrlencoded - argsA=" + argsA);
        return CommonResult.success("");
    }

    @RequestMapping(value = "/postJson", method = RequestMethod.POST)
    @ResponseBody
    public CommonResult postJson(@RequestBody TestObj testObj) {
        log.info("postJson - testObj.name=" + testObj.getName() + " testObj.address=" + testObj.getAddress());
        return CommonResult.success("");
    }
}

5.使用

配置相关参数

[SFRequestConfig sharedConfig].timeoutInterval = 30;
[SFRequestConfig sharedConfig].allowInvalidCertificates = YES;
[SFRequestConfig sharedConfig].validatesDomainName = NO;
[SFRequestConfig sharedConfig].acceptableContentTypes = [NSSet setWithObjects:@"text/html",@"image/jpeg",@"image/png",@"text/plain",@"application/json",nil];
    
[SFResponseConfig sharedConfig].codeKey = @"code";
[SFResponseConfig sharedConfig].dataKey = @"data";
[SFResponseConfig sharedConfig].messageKey = @"msg";
[SFResponseConfig sharedConfig].successedCode = @0;
[SFResponseConfig sharedConfig].failedCode = @500;
[SFResponseConfig sharedConfig].defaultMessageValue = @"请求失败";

调用

[self testGet:@"value of args"];
[self testPostFormData:@"value of args"];
[self testPostFormUrlencoded:@"value of args"];
[self testPostJson:@"hello" address:@"world"];

- (void)testGet:(NSString *)argsA{
    [SFBaseRequest GET:@"http://192.168.0.121:9000/api/test/get" params:@{@"argsA":argsA} headers:@{} completionBlock:^(NSInteger code, id  _Nonnull data, NSString * _Nonnull message) {
   
    }];
}

- (void)testPostFormData:(NSString *)argsA{
    [SFBaseRequest POST:@"http://192.168.0.121:9000/api/test/postFormData" params:@{@"argsA":argsA} headers:@{} completionBlock:^(NSInteger code, id  _Nonnull data, NSString * _Nonnull message) {
   
    }];
}

- (void)testPostFormUrlencoded:(NSString *)argsA{
    [SFBaseRequest POST:@"http://192.168.0.121:9000/api/test/postXWwwFormUrlencoded" params:@{@"argsA":argsA} headers:@{} contentType:ApplicationXWwwFormUrlencoded completionBlock:^(NSInteger code, id  _Nonnull data, NSString * _Nonnull message) {
 
    }];
}

- (void)testPostJson:(NSString *)name address:(NSString *)address{
    [SFBaseRequest POST:@"http://192.168.0.121:9000/api/test/postJson" params:@{@"name":name,@"address":address} headers:@{} contentType:ApplicationJson completionBlock:^(NSInteger code, id  _Nonnull data, NSString * _Nonnull message) {

    }];
}

6.调试

==============================================================
请求地址:http://192.168.0.121:9000/api/test/postXWwwFormUrlencoded
请求参数:{"argsA":"value of args"}
请求响应:{"msg":"操作成功","data":"","code":200}
==============================================================
===========================================
请求地址:http://192.168.0.121:9000/api/test/get
请求参数:{"argsA":"value of args"}
请求响应:{"msg":"操作成功","data":"","code":200}
===========================================
================================================
请求地址:http://192.168.0.121:9000/api/test/postJson
请求参数:{"name":"hello","address":"world"}
请求响应:{"msg":"操作成功","data":"","code":200}
================================================
====================================================
请求地址:http://192.168.0.121:9000/api/test/postFormData
请求参数:{"argsA":"value of args"}
请求响应:{"msg":"操作成功","data":"","code":200}
====================================================
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,591评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,448评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,823评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,204评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,228评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,190评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,078评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,923评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,334评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,550评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,727评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,428评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,022评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,672评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,826评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,734评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,619评论 2 354

推荐阅读更多精彩内容