AWS Lambda笔记-创建API网关-6

本章使用CloudFormation来实现API网关的创建和自动部署。主要包括:

  1. 设置API网关写CloudWatch权限
  2. 创建API
  3. 创建资源
  4. 创建方法
  5. 配置Lambda权限
  6. 部署API服务

工程目录结构

image.png

准备3个java文件

代码同 Serverless-HelloWorld-3可以直接copy即可。

2. 准备Gradle的2个文件

代码同AWS Lambda教程-自动部署-5 可以直接copy即可。

cloudformation.template 文件说明

为了让大家先感受API网关,先将代码直接贴出来,大家先deploy下,将以下内容直接copy到cloudformation.template 文件,保存后在工程目录运行 ./gradlew deploy。然后在运行以下命令,查看serverlessbook的所有资源信息,其中重点关注AWS::ApiGateway::RestApi资源,其中"PhysicalResourceId": "aj4k22d99h" 是新部署的API的ID(这边需要替换成你的ID哦),我们可以尝试访问测试下:https://aj4k22d99h.execute-api.us-east-2.amazonaws.com/production/test?value=hello+world,这是你可以在浏览器里看到 hello world。

aws cloudformation describe-stack-resources --region us-east-2 --stack-name serverlessbook
cloudformation.template,往下看有模版关系图哦。
{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Parameters": {
        "DeploymentBucket": {
            "Type": "String",
            "Description": "S3 bucket name where built artifacts are deployed"
        },
        "ProjectVersion": {
            "Type": "String",
            "Description": "Project Version"
        },
        "DeploymentTime": {
            "Type": "String",
            "Description": "It is a timestamp value which shows the deployment time. Used to rotate sources."
        }
    },
    "Resources": {
        "DeploymentLambdaRole": {
            "Type": "AWS::IAM::Role",
            "Properties": {
                "AssumeRolePolicyDocument": {
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Effect": "Allow",
                            "Principal": {
                                "Service": [
                                    "lambda.amazonaws.com"
                                ]
                            },
                            "Action": [
                                "sts:AssumeRole"
                            ]
                        }
                    ]
                },
                "Path": "/",
                "ManagedPolicyArns": [
                    "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
                ],
                "Policies": [
                    {
                        "PolicyName": "LambdaExecutionPolicy",
                        "PolicyDocument": {
                            "Version": "2012-10-17",
                            "Statement": [
                                {
                                    "Effect": "Allow",
                                    "Action": [
                                        "lambda:PublishVersion",
                                        "apigateway:POST"
                                    ],
                                    "Resource": [
                                        "*"
                                    ]
                                }
                            ]
                        }
                    }
                ]
            }
        },
        "DeploymentLambda": {
            "Type": "AWS::Lambda::Function",
            "Properties": {
                "Role": {
                    "Fn::GetAtt": [
                        "DeploymentLambdaRole",
                        "Arn"
                    ]
                },
                "Handler": "serverless.handler",
                "Runtime": "nodejs12.x",
                "Code": {
                    "S3Bucket": {
                        "Fn::Sub": "serverless-arch-${AWS::Region}"
                    },
                    "S3Key": "serverless.zip"
                }
            }
        },
        "ApiGatewayCloudwatchRole": {
            "Type": "AWS::IAM::Role",
            "Properties": {
                "AssumeRolePolicyDocument": {
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Effect": "Allow",
                            "Principal": {
                                "Service": [
                                    "apigateway.amazonaws.com"
                                ]
                            },
                            "Action": "sts:AssumeRole"
                        }
                    ]
                },
                "Path": "/",
                "ManagedPolicyArns": [
                    "arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs"
                ]
            }
        },
        "ApiGatewayAccount": {
            "Type": "AWS::ApiGateway::Account",
            "Properties": {
                "CloudWatchRoleArn": {
                    "Fn::GetAtt": [
                        "ApiGatewayCloudwatchRole",
                        "Arn"
                    ]
                }
            }
        },
        "RestApi": {
            "Type": "AWS::ApiGateway::RestApi",
            "Properties": {
                "Name": {
                    "Ref": "AWS::StackName"
                }
            }
        },
        "LambdaExecutionRole": {
            "Type": "AWS::IAM::Role",
            "Properties": {
                "Path": "/",
                "AssumeRolePolicyDocument": {
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Effect": "Allow",
                            "Principal": {
                                "Service": [
                                    "lambda.amazonaws.com"
                                ]
                            },
                            "Action": [
                                "sts:AssumeRole"
                            ]
                        }
                    ]
                },
                "ManagedPolicyArns": [
                    "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
                ]
            }
        },
        "LambdaCustomPolicy": {
            "Type": "AWS::IAM::Policy",
            "Properties": {
                "PolicyName": "LambdaCustomPolicy",
                "PolicyDocument": {
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Effect": "Allow",
                            "Action": [
                                "s3:ListBuckets"
                            ],
                            "Resource": "*"
                        }
                    ]
                },
                "Roles": [
                    {
                        "Ref": "LambdaExecutionRole"
                    }
                ]
            }
        },
        "TestLambda": {
            "Type": "AWS::Lambda::Function",
            "Properties": {
                "Handler": "com.serverlessbook.lambda.test.Handler",
                "Runtime": "java8",
                "Timeout": "300",
                "MemorySize": "1024",
                "Description": "Test lambda",
                "Role": {
                    "Fn::GetAtt": [
                        "LambdaExecutionRole",
                        "Arn"
                    ]
                },
                "Code": {
                    "S3Bucket": {
                        "Ref": "DeploymentBucket"
                    },
                    "S3Key": {
                        "Fn::Sub": "artifacts/lambda-test/${ProjectVersion}/${DeploymentTime}.jar"
                    }
                }
            }
        },
        "TestResource": {
            "Type": "AWS::ApiGateway::Resource",
            "Properties": {
                "PathPart": "test",
                "RestApiId": {
                    "Ref": "RestApi"
                },
                "ParentId": {
                    "Fn::GetAtt": [
                        "RestApi",
                        "RootResourceId"
                    ]
                }
            }
        },
        "TestGetMethod": {
            "Type": "AWS::ApiGateway::Method",
            "Properties": {
                "HttpMethod": "GET",
                "RestApiId": {
                    "Ref": "RestApi"
                },
                "ResourceId": {
                    "Ref": "TestResource"
                },
                "AuthorizationType": "NONE",
                "RequestParameters": {
                    "method.request.querystring.value": "True",
                    "method.request.header.Accept": "True"
                },
                "MethodResponses": [
                    {
                        "StatusCode": "200"
                    }
                ],
                "Integration": {
                    "Type": "AWS",
                    "Uri": {
                        "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${TestLambda.Arn}/invocations"
                    },
                    "IntegrationHttpMethod": "POST",
                    "RequestParameters": {
                        "integration.request.querystring.value": "method.request.querystring.value",
                        "integration.request.header.Accept": "method.request.header.Accept"
                    },
                    "RequestTemplates": {
                        "application/json": "{\"value\":\"$input.params('value')\"}"
                    },
                    "PassthroughBehavior": "NEVER",
                    "IntegrationResponses": [
                        {
                            "SelectionPattern": ".*",
                            "StatusCode": "200"
                        }
                    ]
                }
            }
        },
        "TestLambdaPermission": {
            "Type": "AWS::Lambda::Permission",
            "Properties": {
                "Action": "lambda:InvokeFunction",
                "FunctionName": {
                    "Ref": "TestLambda"
                },
                "Principal": "apigateway.amazonaws.com",
                "SourceArn": {
                    "Fn::Sub": "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${RestApi}/*"
                }
            }
        },
        "ApiDeployment": {
            "DependsOn": [
                "TestGetMethod"
            ],
            "Type": "Custom::ApiDeployment",
            "Properties": {
                "ServiceToken": {
                    "Fn::GetAtt": [
                        "DeploymentLambda",
                        "Arn"
                    ]
                },
                "RestApiId": {
                    "Ref": "RestApi"
                },
                "StageName": "production",
                "DeploymentTime": {
                    "Ref": "DeploymentTime"
                }
            }
        }
    }
}
创建API网关组件关系图

看完图,我们接下来一块一块的解释,每个配置的内容。

1. 设置API网关写CloudWatch权限

"Resources": {
    "ApiGatewayCloudwatchRole": {
      // AWS::IAM::Role** 为您的 AWS 账户创建新角色。(角色:可在账户中创建的具有特定权限的 IAM 身份。
     //IAM 角色与 IAM 用户有一些相似之处。角色和用户均为 AWS 身份
     //这些身份具有确定其在 AWS 中可执行和不可执行的操作的权限策略。)
      "Type": "AWS::IAM::Role",
      "Properties": {
         //与此角色关联的信任策略。信任策略定义哪些实体可以代入该角色。只能将一个信任策略与一个角色关联。
        "AssumeRolePolicyDocument": { 
          //策略元素指定用于处理策略的语言语法规则。目前有两种版本2012-10-17 和 2008-10-17,
         //建议使用最新版本,否则有些变量无法识别例如: ${aws:username} 在 2008-10-17版本中将无法识别。
          "Version": "2012-10-17", 
          "Statement": [ //元素为策略的主要元素。
            {
              //用户申明产生的结果是“Allow”还是“Deny”,默认“Deny”。
              //如果允许访问资源必须设置为“Allow”。
              "Effect": "Allow", 
              //指定允许或拒绝访问资源的委托人,可以指定的委托人
              //(AWS 账户和 根用户,IAM 用户,IAM 角色,代入角色的会话,AWS 服务)
             // 本实例用的是AWS服务委托。格式:long_service-name.amazonaws.com.cn,
            //(apigateway.amazonaws.com,lambda.amazonaws.com)与Principal对应的是 NotPrincipal。
              "Principal": { 
                "Service": [
                  "apigateway.amazonaws.com"
                ]
              },
              //元素描述将允许或拒绝的特定操作。通过将服务命名空间用作操作前缀
              //(iam、ec2、sqs、sns、s3 等)并后跟允许或拒绝的操作名称来指定值。
              //例如,"Action": "iam:ChangePassword",或 "Action": 
              //["iam:ListAccessKeys","iam:ChangePassword"] 或通配符 "Action": "s3:*" 
              //sts:AssumeRole:角色的临时访问凭证
              "Action": "sts:AssumeRole" 
            }
          ]
        },
        "Path": "/",  //角色的路径
        //附加到用户的 IAM 托管策略的 Amazon 资源名称 (ARN) 的列表。例如:
        //arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs 
        //策略允许当前IAM角色想CloudWatch写入日志。
        "ManagedPolicyArns": [ 
          "arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs"
        ]
      }
    },
    //ApiGatewayAccount 资源指定API Gateway用来将API日志写入CloudWatch Logs 的IAM角色。
    //如果从未在 AWS 账户中创建某个 API Gateway 资源,则必须添加另一个 API Gateway 资源的依赖项,
    //例如 AWS::ApiGateway::RestApi 或 AWS::ApiGateway::ApiKey 资源。如果您的 AWS 账户中已创建
    //API Gateway 资源,则无需依赖关系(即使该资源已删除)。
    "ApiGatewayAccount": { 
      "Type": "AWS::ApiGateway::Account",
      "Properties": {
        "CloudWatchRoleArn": { //账户中 CloudWatch Logs 写权限的 IAM 角色的 Amazon 资源名称 (ARN)
          //内部函数返回模板中的资源的属性值。Fn::GetAtt: [ logicalNameOfResource, attributeName ] 
          //logicalNameOfResource 包含所需属性的资源的逻辑名称 (也称为逻辑 ID)。
          //attributeName:需要获得其值的资源特定属性名称。
          "Fn::GetAtt": [
            "ApiGatewayCloudwatchRole",
            "Arn"
          ]
        }
      }
    }
  }

说明:CloudWatch:AWS提供的日志存储服务,将应用程序的日志集中进行存储。API网关服务也需要一个IAM角色来调用CloudWatch服务。该IAM角色允许apigateway.amazonaws.com服务访问CloudWatch日志,并且作为AWS::ApiGateway::Account资源配置到API网关上。

2. 创建API

API网关有多个部分组成:API、阶段、资源、方法,例如该URL
https://aj4k34d22h.execute-api.us-east-2.amazonaws.com/production/test?value=hello+world
aj4k34d99h:API的ID
production:阶段名词(软件交付的阶段)
test:资源名
us-east-2:region

"RestApi": {
      "Type": "AWS::ApiGateway::RestApi", //资源创建一个 REST API
      "Properties": {
        "Name": {
        //返回使用 aws cloudformation create-stack 命令指定的堆栈的名称
        //如我们在build.gradle中定义的stackName "serverlessbook"。
       //注意我们在拼接API网关的时候使用的是“PhysicalResourceId”而非这里的“Name”
          "Ref": "AWS::StackName" 
        }
      }
    }
3. 创建资源
"TestResource": {
      "Type": "AWS::ApiGateway::Resource", //在 API 中创建资源。
      "Properties": {
        "PathPart": "test", //资源的路径名称。就是最后的/test
        "RestApiId": { //创建的RestApi资源的 ID,就是上一步创建的RestApi
          "Ref": "RestApi"
        },
        //如果需要创建子资源,则为父资源的 ID。对于没有父资源的资源,指定 RestApi 根资源 ID。
        "ParentId": {  
          "Fn::GetAtt": [  //例如 { "Fn::GetAtt": ["MyRestApi", "RootResourceId"] }
            "RestApi",
            "RootResourceId"
          ]
        }
      }
    }
4. 创建方法
"TestGetMethod": {
      //创建 API Gateway 方法,这些方法定义客户端必须在其请求中发送的参数和正文.
      "Type": "AWS::ApiGateway::Method", 
      "Properties": {
        "HttpMethod": "GET",//客户端用于调用此方法的 HTTP 方法.
        "RestApiId": {  //方法需要指定 RestAPI
          "Ref": "RestApi"
        },
        "ResourceId": {  //方法需要指定 资源
          "Ref": "TestResource"
        },
        "AuthorizationType": "NONE", //方法的授权类型。NONE 是表示不需要权限。
        "RequestParameters": { //API Gateway 接受的请求参数。参数指定为键/值对,源作为键,布尔值作为值
          "method.request.querystring.value": "True", //指定参数的value值。
          "method.request.header.Accept": "True" //Accept头信息。
        },
        "MethodResponses": [  //可发送到调用方法的客户端的响应。这边定义了一个状态码为200
          {
            "StatusCode": "200"
          }
        ],
        "Integration": { //该方法在收到请求时调用的后端系统(用于指定有关方法调用的目标后端的信息)
          "Type": "AWS", //方法运行的后端的类型,例如 HTTP 或 MOCK 或 AWS。
          //集成的统一资源标识符 (URI) Type 属性指定AWS,请采用以下形式指定 AWS 服务:
          //arn:aws:apigateway:region:subdomain.service|service:path|action/service_api。
          //例如,Lambda 函数 URI 遵循以下形式:arn:aws:apigateway:region:lambda:path/path。
          //路径通常采用以下形式:/2015-03-31/functions/LambdaFunctionARN/invocations。
          "Uri": { 
            "Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${TestLambda.Arn}/invocations"
          },
          "IntegrationHttpMethod": "POST",  //集成的 HTTP 方法类型。
          //API Gateway 在方法的后端处理完请求后提供的响应。
          //API Gateway 将拦截来自后端的响应,以便您能够控制 API Gateway 呈现后端响应的方式。
          "RequestParameters": { 
            "integration.request.querystring.value": "method.request.querystring.value",
            "integration.request.header.Accept": "method.request.header.Accept"
          },
          //对请求负载应用的 Apache Velocity 模板的映射。
          //API Gateway 使用的模板基于客户端发送的 Content-Type 标头的值。
          //内容类型值为密钥,模板为值(指定为字符串),
          //例如以下代码段:"application/json": "{\n \"statusCode\": 200\n}"
          //将参数在传入后台时进行格式化。value=hello+world 转化为 {"value":"hello+world"}
          "RequestTemplates": { 
            "application/json": "{\"value\":\"$input.params('value')\"}"
          },
          //指示 API Gateway 将请求传递到目标后端的时间。
          //此行为取决于请求的 Content-Type 标头以及您是否为其定义了映射模板。这里为“NEVER”。
          "PassthroughBehavior": "NEVER",  
          //API Gateway 在方法的后端处理完请求后提供的响应。
          //API Gateway 将拦截来自后端的响应,以便您能够控制 API Gateway 呈现后端响应的方式。
          "IntegrationResponses": [ 
            {
              //一个正则表达式,用于指定来自后端的哪些错误字符串或状态代码将映射到集成响应。
              "SelectionPattern": ".*",  
               //API Gateway 用于将集成响应映射到 MethodResponse 状态代码的状态代码。
               //这里将所有的返回结果都对应状态码200.
              "StatusCode": "200"
            }
          ]
        }
      }
    }
5. 配置Lambda权限
"TestLambdaPermission": {
     //资源授予 AWS 服务或其他账户使用函数的权限。要向另一个账户授予权限,请将账户ID指定为Principal。
    //对于 AWS服务,委托人是服务定义的域样式标识符例如 s3.amazonaws.com 或 apigateway.amazonaws.com。
    //对于AWS服务,您还可以将关联资源的ARN指定为SourceArn。  
    "Type": "AWS::Lambda::Permission",
      "Properties": {
        "Action": "lambda:InvokeFunction",  //委托人可以对函数执行的操作。这边的权限是调用函数。
        "FunctionName": {  //Lambda 函数的名称、版本或别名。这边是指定内部TestLambda。
          "Ref": "TestLambda"
        },
        "Principal": "apigateway.amazonaws.com",
        //对于AWS服务,为调用该函数的AWS资源的ARN。例如,Amazon S3 存储桶或 Amazon Lambda 函数。
        "SourceArn": {  
          //将输入字符串中的变量替换为您指定的值。
          "Fn::Sub": "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${RestApi}/*" 
        }
      }
    }
6. 部署API服务

创建了一个IAM角色,并赋予了lambda:PublishVersion,lambda:PublishVersion(从函数的当前代码和配置创建一个版本。使用版本创建不更改的功能代码和配置快照。)权限。

"DeploymentLambdaRole": {
      "Type": "AWS::IAM::Role",
      "Properties": {
        "AssumeRolePolicyDocument": {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Principal": {
                "Service": [
                  "lambda.amazonaws.com"
                ]
              },
              "Action": [
                "sts:AssumeRole"
              ]
            }
          ]
        },
        "Path": "/",
        "ManagedPolicyArns": [
          //Lambda功能的托管策略之一, 管理弹性网络接口以将您的函数连接到VPC的权限。
          "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
        ],
         //添加或更新嵌入在指定 IAM 角色中的内联策略文档。有关附加策略的信息,
        //附加策略已经附加到用户、组或角色的托管策略。
        "Policies": [ 
          {
            "PolicyName": "LambdaExecutionPolicy",  //策略文档。
            "PolicyDocument": {  //标识策略的友好名称。
              "Version": "2012-10-17",
              "Statement": [
                {
                  "Effect": "Allow",
                  "Action": [
                    "lambda:PublishVersion",
                    "apigateway:POST"
                  ],
                  "Resource": [
                    "*"
                  ]
                }
              ]
            }
          }
        ]
      }
    },
    "DeploymentLambda": {
      //创建 Lambda 函数。要创建函数,需要部署程序包和执行角色。部署程序包中包含您的函数代码。
      //执行角色授予该函数使用 AWS 服务的权限。
      "Type": "AWS::Lambda::Function", 
      "Properties": {
        "Role": {
          "Fn::GetAtt": [
            "DeploymentLambdaRole",
            "Arn"
          ]
        },
        "Handler": "serverless.handler",
        "Runtime": "nodejs12.x",
        "Code": {
          "S3Bucket": {
            "Fn::Sub": "serverless-arch-${AWS::Region}"
          },
          "S3Key": "serverless.zip"
        }
      }
    }

创建完上面Lambda函数后,这是创建自定义资源的标准语法,其中ServiceToken时必须,且是Lambada函数的Arn名称,其他参数将在事件发生时传给Lambda函数进行处理。这里用于部署创建一个新的API部署任务,然后返回结果。

"ApiDeployment": {
      "DependsOn": [
        "TestGetMethod"
      ],
      //将 Lambda 函数与自定义资源关联,则在创建、更新或删除自定义资源时就会调用该函数。 
      //AWS CloudFormation 将通过调用 Lambda API 来调用此函数以及将所有请求数据
      //(如请求类型和资源属性)传递到此函数。
      "Type": "Custom::ApiDeployment",  
      "Properties": {
        "ServiceToken": {
          "Fn::GetAtt": [
            "DeploymentLambda",
            "Arn"
          ]
        },
        "RestApiId": {
          "Ref": "RestApi"
        },
        "StageName": "production",
        "DeploymentTime": {
          "Ref": "DeploymentTime"
        }
      }
    }

到此完成API网关的部署,但是这个程序是自动生产URL无法记忆不是很友好,后面将解决URL变成我们自己定义的URL。

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

推荐阅读更多精彩内容