• 日常搜索
  • 百度一下
  • Google
  • 在线工具
  • 搜转载

使用AWS SimpleDB和Node.js构建REST API

SimpleDB是Amazon Web服务( aws ) 提供的远程数据库。根据 sql 语言的使用(或不使用),数据存储的世界通常分为 SQL 和 NoSQL。NoSQL 数据存储通常基于更简单的键/值设置。SimpleDB 跨越了这条线——它是一个键/值存储,它也可以使用 SQL 的变体进行检索。大多数 SQL 语言都基于对数据的行和列进行布局的模式,但 SimpleDB 是一个无模式的数据库,可以提供非常灵活的数据存储。

在 SimpleDB 数据库模型中,您有项目、属性和值。 数据库中的每一行都是一个项目 ,可以通过唯一且可分配的项目名称来标识。每个项目最多可以有 256 对属性和值。SimpleDB的一个出乎意料的方面是,一个属性可以有超过一对的每个项目。我认为考虑 SimpleDB 的最佳方式是考虑电子表格,但不是每个列/行交叉点代表一个值,而是代表一个值数组。

使用AWS SimpleDB和Node.js构建REST API  第1张此图表表示存储在 SimpleDB 域中的两个项目。术语域 类似于其他数据库中的“表”。 

第一列是项目名称——这是唯一一个您只能有一个值的列,您可以将其视为唯一索引列。 

其他四列(宠物、汽车、家具和电话)表示当前在该域中的属性——您不仅限于此,因此每个项目都可以具有一组完全独特的属性。在这个数据中, 物品 personInventory1上的属性pets有三对;用 JSON 表示,它看起来像这样:

{ "Name" : "pets", "Value" : "dog" }, 
{ "Name" : "pets", "Value" : "cat" }, 
{ "Name" : "pets", "Value" : "fish" }

另一方面,项目personInventory2只有一对:

{ "Name" : "pets", "Value" : "cat" }

虽然您不必为每件商品提供相同的属性,但您确实需要提供至少一对。这意味着您不能拥有“空”项目。由于 1kb 的值限制和 256 对的限制,每个属性的值最大为 1kb,因此这意味着每个项目在功能上限制为 256kb。

SimpleDB 是分布式的,它有一些独特的特征,您在设计应用程序时需要理解和牢记这些特征。作为分布式数据库意味着一整组机器将响应您的请求,并且您的数据将在这些服务器中复制。这种分布对你的程序来说是完全透明的,但它确实引入了一致性问题的可能性——你的数据不能保证最初出现在所有服务器上。 

不要惊慌:这并不像听起来那么糟糕,原因有几个。使用SimpleDB,一致性并不好,但根据我的经验,它通常非常好并且可以快速到达所有节点。围绕这一点进行设计也不是那么难——通常你会尽量避免立即阅读你刚写的唱片。最后,SimpleDB 可以选择执行一致性读取,但它们速度较慢并且可能会消耗更多资源。如果您的应用程序每次都需要一致的读取,您可能需要重新考虑使用 SimpleDB 作为您的数据存储,但对于许多应用程序来说,这可以围绕甚至不考虑来设计。

从好的方面来说,分布式特性还为 SimpleDB 提供了一些与 node.js 环境完美结合的优势。由于您没有单个服务器响应您的请求,因此您无需担心服务饱和,您可以通过向 SimpleDB 发出许多并行请求来获得良好的性能。Node.js 可以轻松处理并行和异步请求。

与许多 AWS 服务不同,没有 Amazon 提供的用于管理 SimpleDB 的控制台。幸运的是,有一个很好的浏览器内管理控制台,形式为 Google chrome 插件SdbNavigator。在 SdbNavigator 中,您可以添加或删除域、插入、更新和删除项目、修改属性以及执行查询。

AWS 开发工具包

现在我们已经了解了 SimpleDB 服务,让我们开始编写我们的rest服务器。首先,我们需要安装 AWS 开发工具包。此 SDK 不仅处理 SimpleDB,还处理所有 AWS 服务,因此您可能已经将它包含在您的 package.json 文件中。要安装 SDK,请从命令行运行以下命令:

npm install aws-sdk ——保存

要使用 SimpleDB,您还需要获取AWS 凭证,其中包括访问密钥和秘密密钥。SimpleDB 是一种即用即付服务,但 AWS 目前为 SimpleDB 提供了慷慨的免费津贴。 

警告语:与任何即用即付服务一样,请注意编写可能会产生大笔费用的代码,因此您需要密切关注您的使用情况并保持您的凭据私密并且安全。 

安装 AWS 开发工具包并获取凭证后,您需要在代码中设置 SimpleDB。在此示例中,我们将使用存储在您的主目录的 JSON 文件中的 AWS 凭证。首先,您需要包含 SDK 模块,创建一个 AWS 对象,最后设置您的 SimpleDB 接口。

var
  aws         = require('aws-sdk'),
  simpledb;
 
aws.config.loadFromPath(process.env['HOME'] + '/aws.credentials.json');
 
//We'll use the Northern Virginia datacenter, change the region / endpoint for other datacenters https://docs.aws.amazon.com/general/latest/gr/rande.html#sdb_region
simpledb = new aws.SimpleDB({
  region    : 'US-East',
  endpoint  : 'https://sdb.amazonaws.com'
});

请注意,我们正在使用特定的端点和区域。每个数据中心都是完全独立的,因此如果您在北弗吉尼亚州创建一个名为“ mysuperawesomedata ”的域,例如,它将不会复制到圣保罗数据中心,也不会出现在圣保罗数据中心中。

您创建的 SimpleDB 对象是您new aws.SimpleDB与 SimpleDB 交互的所有方法的基础。适用于 SimpleDB的AWS 开发工具包只有几个方法:

批量操作

  • 批量删除属性

  • batchPutAttributes

域管理和信息

物品/属性操作

  • 删除属性

  • 获取属性

  • putAttributes

查询

  • 选择

在本教程中,我们将只处理项目/属性操作和查询;虽然其他类别很有用,但许多应用程序对它们没有任何用处。

测试数据

使用 SdbNavigator,在工具中输入您的访问和安全密钥,选择“美国东部”,然后单击连接。

使用AWS SimpleDB和Node.js构建REST API  第2张成功连接后,让我们创建一个域进行测试。单击添加域。

使用AWS SimpleDB和Node.js构建REST API  第3张然后输入域名 'sdb-rest-tut' 并点击OK。

使用AWS SimpleDB和Node.js构建REST API  第4张现在您已经创建了一个域,让我们输入一些测试数据。单击添加属性并添加一个名为“颜色”的属性。作为惯例,我通常以复数形式命名属性以反映 SimpleDB 的多值特性。

使用AWS SimpleDB和Node.js构建REST API  第5张最后,我们将单击添加记录来创建我们的第一个 SimpleDB 项目。在 ItemName() 列中,输入您的唯一项目名称。SdbNavigator 的一个怪癖是,默认情况下,它只接受每个项目的单个值,但这掩盖了一个属性可以包含多个值的事实。要输入多个值,请单击属性列右边缘的S。


使用AWS SimpleDB和Node.js构建REST API  第6张在新框中,选择数组 以输入多个值。在“值”列中,输入“red”,然后单击“ Add value ”并输入“blue”。

使用AWS SimpleDB和Node.js构建REST API  第7张

最后,单击更新 以保存对该行的更改。

使用AWS SimpleDB和Node.js构建REST API  第8张使用AWS SimpleDB和Node.js构建REST API  第8张使用AWS SimpleDB和Node.js构建REST API  第10张现在我们已经输入了一些测试数据,让我们从 Node.js 发出第一个 SimpleDB 请求。我们将只获取域中的所有内容,此时,它只是一行。

var
  aws         = require('aws-sdk'),
  simpledb;
 
aws.config.loadFromPath(process.env['HOME'] + '/aws.credentials.json');
 
simpledb = new aws.SimpleDB({
  region        : 'US-East',
  endpoint  : 'https://sdb.amazonaws.com'
});
 
simpledb.select({
  SelectExpression  : 'select * from `sdb-rest-tut` limit 100'
}, function(err,resp) {
  if (err) {
    console.error(err);
  } else {
    console.log(JSON.stringify(resp,null,' '));
  }
});

响应将记录到控制台。这是响应,注释用于解释:

{
 "ResponseMetadata": {
  "RequestId": "...",             //Every request made to SimpleDB has a request ID
  "BoxUsage": "0.0000228616"      //This is how your account is charged, as of time of writing US-East region is 14 US cents per hour, so this request costs 0.00032 cents + the transfer cost (if you are currently outside of your free tier)
 },
 "Items": [                       //For a Select, your response will be in the "Items" object property
  {
   "Name": "myfirstitem",         //this is the itemName()
   "Attributes": [                //these are the attribute pairs
    {
     "Name": "colors",            //attribute name
     "Value": "red"               //value - note that every Value is a string, regardless of the contents
    },
    {
     "Name": "colors",            //Since the attribute name is repeated, we can see that `colors` has more than one value
     "Value": "blue"
    }
   ]
  }
 ]
}

一个 REST 服务器

由于我们将构建一个在 SimpleDB 中存储数据的 REST 服务器,因此了解 REST 服务器的功能很重要。REST代表 RE呈现性状态传输。REST 服务器实际上只是一个使用 HTTP 标准机制作为数据接口的服务器。通常,REST 用于服务器到服务器的通信,但您可以通过javascript库(例如jqueryangular )将 REST 服务器与客户端一起使用。但是,通常最终用户不会直接与 REST 服务器交互。

有趣的是,AWS SDK 实际上使用 REST 协议与 SimpleDB 进行交互,因此为另一个 REST 服务器创建一个 REST 服务器可能看起来很奇怪。您不希望直接使用 SimpleDB REST api,因为您需要对您的请求进行身份验证,这会危及您的 AWS 账户的安全性。此外,通过编写服务器,您将能够在数据存储中添加抽象层和验证层,这将使整个应用程序的其余部分更容易处理。

在本教程中,我们将构建基本的 CRUD+L 函数,即创建、读取、更新、删除和列表。如果您考虑一下,您可以将大多数应用程序分解为 CRUD+L。使用 REST,您将使用有限数量的路径和几个 HTTP 方法或动词来创建直观的 API。大多数开发人员都熟悉一些 HTTP 动词,即 GET 和post,因为它们最常用于 Web 应用程序,但还有 其他几个。


OperationHTTP Verb
CreatePOST
ReadGET
UpdatePUT
DeleteDELETE
ListGET

请注意,Read 和 List 都使用相同的动词;我们将使用稍微不同的路径来区分两者。我们使用 POST 来表示 Create,因为创建不被认为是幂等的。幂等意味着多个相同的调用将对用户和您的数据产生相同的结果,因此更新(又名 PUT)将被视为幂等。

作为我们的示例,我们将构建一个个人库存服务器——一个用于保存您拥有的任何东西的数据库。以下是路径的外观:

OperationHTTP VerbPath
CreatePOST/inventory
ReadGET/inventory/1234
UpdatePUT/inventory/1234
DeleteDELETE/inventory/1234
ListGET/inventory

1234是人员标识符 (ID) 的占位符 - 请注意,“创建”和“列表”没有 ID。在创建的情况下,将生成 ID,使用列表,我们将获取所有名称,因此我们不需要特定的 ID。 

构建服务器

首先,让我们安装Express,一个 Node.js HTTP 服务器框架:

npm install express ---保存

Express 管理着设置服务器的大部分细节,但它不包含任何处理 HTTP 请求正文的工具,因此我们需要安装另一个模块body-parser,以使我们能够读取请求正文。

npm install body-parser --save

Body-parser 有几个不同的选项来解析 HTTP 请求的正文。我们将使用该json()方法以提高可读性,但切换到另一种方法只是换出bodyParser对象上的方法。我们只需要bodyParsercreate 和 update 方法上的方法,所以我们可以将它包含在那些特定的路由中。

创造

由于每个 SimpleDB 都itemName需要是唯一的,我们可以itemName为每个新创建的项目自动生成一个新的。我们将使用cuid模块,这是一种生成唯一标识符的轻量级方法。

npm install cuid --save

SimpleDB 期望属性采用属性名称/值对格式:

[
    { "Name" : "attribute1", "Value" : "value1" },
    { "Name" : "attribute1", "Value" : "value2" },
    { "Name" : "attribute2", "Value" : "value3" },
    { "Name" : "attribute3", "Value" : "value4" }
]

您的服务器当然可以直接接受这种格式的值并将其直接传递给 SimpleDB,但这与数据通常的结构方式是违反直觉的,而且这是一个难以处理的概念。我们将使用更直观的数据结构,对象/值数组:

{
    "attribute1"    : ["value1","value2"],
    "attribute2"    : ["value3","value4"]
}

这是一个基本的基于 Express 的服务器,带有 create 操作:

var
  aws         = require('aws-sdk'),
  bodyParser  = require('body-parser'),
  cuid        = require('cuid'),
  express     = require('express'),
   
  sdbdomain   = 'sdb-rest-tut',
   
  app         = express(),
  simpledb;
 
aws.config.loadFromPath(process.env['HOME'] + '/aws.credentials.json');
simpledb = new aws.SimpleDB({
  region        : 'US-East',
  endpoint  : 'https://sdb.amazonaws.com'
});
 
//create
app.post(
  '/inventory', 
  bodyParser.json(),
  function(req,res,next) {
    var
      sdbAttributes   = [],
      newItemName     = cuid();
     
    //start with: 
    /*
      { attributeN     : ['value1','value2',..'valueN'] }
    */
    Object.keys(req.body).forEach(function(anAttributeName) {
      req.body[anAttributeName].forEach(function(aValue) {
        sdbAttributes.push({
          Name  : anAttributeName,
          Value : aValue
        });
      });
    });
    //end up with:
    /*
      [ 
        { Name : 'attributeN', Value : 'value1' },
        { Name : 'attributeN', Value : 'value2' },
        ...
        { Name : 'attributeN', Value : 'valueN' },
      ]
    */
 
    simpledb.putAttributes({
      DomainName    : sdbDomain,
      ItemName      : newItemName,
      Attributes    : sdbAttributes
    }, function(err,awsResp) {
      if (err) { 
        next(err);  //server error to user
      } else {
        res.send({
          itemName  : newItemName 
        });
      }
    });
  }
);
 
 
app.listen(3000, function () {
  console.log('SimpleDB-powered REST server started.');
});

让我们启动您的服务器并尝试一下。与 REST 服务器交互的一种好方法是使用curl 工具。此工具允许您直接从命令行使用任何动词发出 HTTP 请求。要尝试使用我们的 REST 服务器创建项目,我们需要激活一些额外的选项:

curl -H "Content-Type: application/json" -X POST -d '{"pets" : ["dog","cat"], "cars" : ["saab"]}' 
http://localhost:3000/inventory
选项目的
-H在 HTTP 标题中添加一行
-X定义将使用哪个动词
-dHTTP 请求正文中要发送的数据

运行命令后,您将看到带有新创建的 itemName 或 ID 的 JSON 响应。如果切换到 SdbNavigator,则在查询所有项目时应该会看到新数据。

现在让我们构建一个从 SimpleDB 读取项目的基本函数。为此,我们不需要执行查询,因为我们将从请求的路径中获取 itemName 或 ID。getAttributes我们可以使用该 itemName 或 ID执行请求。

如果我们在这里停下来,我们将拥有一种实用但不太友好的数据形式。让我们将名称/值数组转换为我们用来接受数据的相同形式(属性:值数组)。为此,我们需要遍历每个名称/值对,并将其添加到每个唯一名称的新数组中。 

最后,让我们添加 itemName 并返回结果。 

//Read
app.get('/inventory/:itemID', function(req,res,next) {
  simpledb.getAttributes({
    DomainName    : sdbDomain,
    ItemName      : req.params.itemID   //this gets the value from :itemID in the path
  }, function(err,awsResp) {
    var
      attributes = {};
       
    if (err) { 
      next(err);  //server error to users
    } else {
      awsResp.Attributes.forEach(function(aPair) {
        // if this is the first time we are seeing the aPair.Name, let's add it to the response object, attributes as an array
        if (!attributes[aPair.Name]) { 
          attributes[aPair.Name] = [];
        }
        //push the value into the correct array
        attributes[aPair.Name].push(aPair.Value);
      });
      res.send({
        itemName    : req.params.itemID,
        inventory   : attributes
      });
    }
  });
});

为了测试这一点,我们需要再次使用 curl。尝试用[cuid]本教程前面创建项目的示例返回的 itemName 或 ID 替换。

curl -D- http://localhost:3000/inventory/[cuid]

请注意,我们正在使用该 选项。这将转储 HTTP 头,以便我们可以看到响应代码。 -D-

REST 的另一个方面是有意义地使用您的响应代码。在当前示例中,如果您向 curl 提供不存在的 ID,则上述服务器将崩溃,因为您正在尝试forEach使用不存在的数组。我们需要考虑这一点,并返回一个有意义的 HTTP 响应代码,表明该项目未找到。 

为了 pr事件错误,我们应该测试变量的存在awsResp.Attributes。如果不存在,我们将状态码设置为 404 并结束 http 请求。如果它存在,那么我们可以为响应提供属性。 

app.get('/inventory/:itemID', function(req,res,next) {
  simpledb.getAttributes({
    DomainName    : sdbDomain,
    ItemName      : req.params.itemID
  }, function(err,awsResp) {
    var
      attributes = {};
       
    if (err) { 
      next(err);
    } else {
      if (!awsResp.Attributes) {
        //set the status response to 404 because we didn't find any attributes then end it
        res.status(404).end(); 
      } else {
        awsResp.Attributes.forEach(function(aPair) {
          if (!attributes[aPair.Name]) { 
            attributes[aPair.Name] = [];
          }
           
          attributes[aPair.Name].push(aPair.Value);
        });
        res.send({
          itemName    : req.params.itemID,
          inventory   : attributes
        });
      }
    }
  });
});

尝试使用新代码和不存在的 ID,您会看到服务器返回 404。 

既然我们知道如何使用status 来更改值,我们还应该更新我们如何响应 POST/create。虽然 200 响应在技术上是正确的,因为它意味着“OK”,但更深入的响应代码是 201,它表示“created”。要进行此更改,我们将在发送之前将其添加到状态方法中。

res
 .status(201)
 .send({
   itemName  : newItemName
 });

更新

对于任何系统来说,更新通常是最困难的操作,这个 REST 服务器也不例外。 

SimpleDB 的性质也使此操作更具挑战性。在 REST 服务器的情况下,更新是您替换整个存储数据的地方;另一方面,SimpleDB 表示 itemName 下的单个属性/值对。 

为了允许更新表示单个数据而不是名称/值对的集合,我们需要为我们的代码定义一个模式(即使 SimpleDB 不需要)。如果现在不清楚,请不要担心 - 继续阅读,我将说明要求。

与许多其他数据库系统相比,我们的模式将非常简单:只是一个定义的属性数组。在我们的示例中,我们关注四个字段:pets、cars、failure和phone:

schema      = ['pets','cars','furniture','phones'],

使用 SimpleDB,您不能存储空的属性/值对,SimpleDB 也没有任何单个项目的概念,因此我们假设如果 SimpleDB 不返回值,则它不存在。同样,如果我们尝试使用空属性/值对更新 SimpleDB 项,它将忽略该数据。以这些数据为例:

{
  "itemName": "cil89uvnm00011ma2fykmy79c",
  "inventory": {
    "cars": [],
    "pets": [
      "cat",
      "dog"
    ]
  }
}

逻辑上,我们知道cars,作为一个空数组,应该没有值, pets应该有两个值,但是phones和furniture呢?你对那些做什么?以下是我们如何将此更新请求转换为使用 SimpleDB:

  • 将具有pet值的属性放到cat.

  • 将具有pet值的属性放到dog.

  • 删除 的属性 cars。

  • 删除 的属性 phones。

  • 删除 的属性 furniture。

如果没有某种形式的模式至少定义了属性,我们将不知道这一点phones并且furniture需要被删除。幸运的是,我们可以将此更新操作合并为两个 SimpleDB 请求,而不是五个:一个用于放置属性,一个用于删除属性。现在是从 post/create 函数中提取代码的好时机,该函数将值 对象的属性/数组转换为属性/值对数组。

function attributeObjectToAttributeValuePairs(attrObj, replace) {
   var
     sdbAttributes   = [];
 
    Object.keys(attrObj).forEach(function(anAttributeName) {
      attrObj[anAttributeName].forEach(function(aValue) {
        sdbAttributes.push({
          Name    : anAttributeName,
          Value   : aValue,
          Replace : replace           //if true, then SimpleDB will overwrite rather than append more values to an attribute
        });
      });
    });
 
   return sdbAttributes; 
}

我们还将对 create 函数进行重要更改。我们将为所有项目添加一个新的属性/值。此属性不会添加到架构中,并且实际上是只读的。 

我们将添加一个名为的属性created 并将其值设置为1。使用 SimpleDB,在添加属性和值之前检查项目是否存在的能力有限。在每个putAttributes请求中,您都可以检查单个属性的值和存在——在我们的例子中,我们将使用created 并检查值 1。虽然这看起来是一个奇怪的解决方法,但它提供了一个非常重要的安全性来防止从能够创建具有任意 ID 的新项目的更新操作。

newAttributes.push({
  Name    : 'created',
  Value   : '1'
});

由于我们将执行几个异步 HTTP 请求,让我们安装async 模块以简化这些回调的处理。

npm install async ——保存

请记住,由于 SimpleDB 是分布式的,因此没有理由按顺序放置我们的属性然后删除。我们将使用该函数async.parallel来运行这两个操作并在两者都完成时获得回调。来自 AWS 的响应形成putAttributes并且deleteAttributes不提供重要信息,因此如果没有错误,我们将仅发送带有状态代码 200 的空响应。

app.put(
  '/inventory/:itemID', 
  bodyParser.json(),
  function(req,res,next) {
    var
      updateValues  = {},
      deleteValues  = [];
     
    schema.forEach(function(anAttribute) {
      if ((!req.body[anAttribute]) || (req.body[anAttribute].length === 0)) {
        deleteValues.push({ Name : anAttribute});
      } else {
        updateValues[anAttribute] = req.body[anAttribute];
      }
    });
     
    async.parallel([
        function(cb) {
          //update anything that is present
          simpledb.putAttributes({
              DomainName    : sdbDomain,
              ItemName      : req.params.itemID,
              Attributes    : attributeObjectToAttributeValuePairs(updateValues,true),
              Expected      : {
                Name          : 'created',
                Value         : '1',
                Exists        : true
              }
            },
            cb
          );
        },
        function(cb) {
          //delete any attributes that not present
          simpledb.deleteAttributes({
              DomainName    : sdbDomain,
              ItemName      : req.params.itemID,
              Attributes    : deleteValues
            },
            cb
          );
        }
      ],
      function(err) {
        if (err) {
          next(err);
        } else {
          res.status(200).end();
        }
      }
    );
  }
);

为了试一试,让我们更新以前创建的条目。这一次,我们将让库存只包含一条“狗”,移除所有其他物品。同样,使用 cURL 运行命令,将 [cuid] 替换为您的项目 ID 之一。

curl -H "Content-Type: application/json" -X PUT -d '{"pets" : ["dog"] }' http://localhost:3000/inventory/[cuid]

删除

SimpleDB 没有删除项目的概念,但它可以删除属性,如上所述。要删除一个项目,我们需要删除所有属性,“项目”将不再存在。 

由于我们已经在我们的模式中定义了一个属性列表,我们将使用deleteAttributes调用来删除所有这些属性以及created属性。按照我们的计划,此操作将与 Update 位于同一路径,但使用动词 delete。 

app.delete(  '/inventory/:itemID',   function(req,res,next) 
{    var      attributesToDelete;         attributesToDelete = schema.map(function(anAttribute)
{      return { Name : anAttribute };    });         
attributesToDelete.push({ Name : 'created' });         
simpledb.deleteAttributes({        
DomainName    : sdbDomain,        
ItemName      : req.params.itemID,        
Attributes    : attributesToDelete      },      
function(err) {        
if (err) {          
next(err);        
} else {          
res.status(200).end();        }      }    );  });

列表

完善我们的 REST 动词是列表。为了实现列表操作,我们将使用 select 命令和类似 SQL 的查询语言。我们的列表功能将是准系统,但将作为以后更复杂检索的良好基础。我们将做一个非常简单的查询:

select * from `sdb-rest-tut` limit 100

当我们遇到 get/read 操作时,SimpleDB 的响应并不是很有用,因为它专注于属性/值对。为了避免重蹈覆辙,我们将 get/read 操作的部分重构为一个单独的函数并在这里使用它。当我们这样做时,我们还将过滤掉created 属性(因为它将显示在 get 操作中)。

function attributeValuePairsToAttributeObject(pairs) {
  var
    attributes = {};
   
   
  pairs
    .filter(function(aPair) {
      return aPair.Name !== 'created';
    })
    .forEach(function(aPair) {
    if (!attributes[aPair.Name]) { 
      attributes[aPair.Name] = [];
    }
     
    attributes[aPair.Name].push(aPair.Value);
  });
   
  return attributes;
}

通过选择操作,SimpleDB 返回Items 数组中的值。每个项目都由一个对象表示,该对象包含 itemName(简单地说Name)和属性/值对。 

为了简化这个响应,让我们在一个对象中返回所有内容。首先,我们将属性/值对转换为属性/值数组,就像我们在读取/获取操作中所做的那样,然后我们可以添加 itemName 作为属性 ID。 

app.get(
  '/inventory',
  function(req,res,next) {
    simpledb.select({
      SelectExpression  : 'select * from `sdb-rest-tut` limit 100'
    },
    function(err,awsResp) {
      var
        items = [];
      if (err) {
        next(err);
      } else {
        items = awsResp.Items.map(function(anAwsItem) {
          var
            anItem;
           
          anItem = attributeValuePairsToAttributeObject(anAwsItem.Attributes);
           
          anItem.id = anAwsItem.Name;
           
          return anItem;
        });
        res.send(items);
      }
    });
  }
);

要查看我们的结果,我们可以使用 curl:

curl -D- -X GET http://localhost:3000/inventory

验证

验证本身就是一个主题,但是通过我们已经编写的代码,我们可以开始一个简单的验证系统。 

现在,我们要确保的是用户不能提交任何东西,除了模式中的内容。回顾为 update/put 编写的代码,forEach对模式进行 ing 将防止添加任何未经授权的属性,因此我们实际上只需要应用类似于我们的 create/post 操作的东西。在这种情况下,我们将过滤属性/值对,消除任何非模式属性。

newAttributes = newAttributes.filter(function(anAttribute) {
  return schema.indexOf(anAttribute.Name) !== -1;
});

在您的生产代码中,您可能需要一个更强大的验证系统。我建议集成像ajv这样的 JSON 模式验证器,并构建一个中间件,该中间件位于bodyParser创建和更新操作的路由函数之间。

下一步

使用本文中概述的代码,您拥有存储、读取和修改数据所需的所有操作,但这只是您旅程的开始。在大多数情况下,您需要开始考虑以下主题:

  • 验证

  • 分页

  • 复杂的列表/查询操作

  • 其他输出格式(xml、csv等)

SimpleDB 支持的 REST 服务器的这一基础允许您添加中间件和其他逻辑来为您的应用程序构建主干。


文章目录
  • AWS 开发工具包
  • 测试数据
  • 一个 REST 服务器
  • 构建服务器
    • 创造
    • 更新
    • 删除
    • 列表
    • 验证
  • 下一步