服务器端脚本

服务器端脚本采用 JavaScript,作为 Google Apps 脚本运行。它们可以访问 Apps Script API 以及应用制作工具的服务器端对象和 API。请注意,您必须先启用 Google Advanced Services,然后才能使用它们。

服务器端脚本的基础知识

当您需要执行某些耗时的计算(以避免屏蔽界面)时,或者当您需要访问由于大型或安全问题而不希望发送给客户端的信息时,您应该使用服务器端脚本。

调用服务器脚本

如需调用服务器脚本,请使用 google.script.run。如需了解详情,请参阅客户端脚本文档

参数和返回值

值在 google.script.runwithSuccessHandler(function) 方法中返回。合法参数和返回值采用 NumberBooleanString 等 JavaScript 原语,以及由原语、对象和数组组成的 JavaScript 对象和数组。

举例来说,如果您尝试传递 Date 对象、FunctionRecord 或应用制作工具微件,请求将失败。如需传递更复杂的对象,一种常见策略是使用 JSON.stringifyJSON.parse

隐藏服务器脚本

需要注意的是,您在服务器脚本中定义的任何函数都对应用的所有用户开放,即使您没有在界面中公开它也是如此。如果您要编写只能从其他服务器脚本调用的效用函数,则必须在名称后附加下划线,举例来说,下列方法将会公开,允许任何客户端获取传入用户的工资!

function calculateUserSalary(user) {
      // Gets the salary of the user...
      return salary;
    }
    

其实,它应该类似于如下所示:

function calculateUserSalary_(user) {
      // Gets the salary of the user...
      return salary;
    }
    

安全

虽然隐藏服务器脚本非常有用,但有时您的服务器脚本可能需要向客户端公开敏感数据。这种情况下,保护脚本非常重要。虽然模型和视图具有权限,但就服务器脚本而言,您必须实现自己的权限。下面还是使用上面所举的例子,假设您希望用户能够调用它,但只希望在用户本人请求时或者经理请求时返回用户的工资。

function calculateUserSalary(user) {
      var currentUser = Session.getActiveUser().getEmail();
      if (currentUser !== user && !isManager(currentUser, user) {
        throw new Error(currentUser +
            " does not have access to salary for " + user);
      }

      // Gets the salary of the user...
      return salary;
    }
    

处理数据

服务器脚本允许您以通用方式与任何应用制作工具模型进行交互。它们允许您创建新记录、查询或编辑关系。

创建记录

// Assume a model called "Fruits" with a string field called "Name".
    var newRecord = app.models.Fruits.newRecord();
    newRecord.Name = "Kiwi";  // properties/fields can be read and written.
    app.saveRecords([newRecord]);  // The changes won't be committed to the
                                   // database unless you remember to save.
    

查询记录

var query = app.models.Person.newQuery();
    query.filters.Name._equals = "John";

    var records = query.run();
    console.log("Found " + records.length + " Johns.");

    for (var i in records) {
        var person = records[i];
        console.log("Found person named John " + person.FamilyName);
    }
    

处理关系

以下示例演示如何创建和查询相关记录。假设我们有一个“Address”模型,与“Country”模型是多对一的关系。

var address1 = app.models.Address.newRecord();
    address1.zip = 94040;
    var address2 = app.models.Address.newRecord();
    address2.zip = 94041;
    app.saveRecords([address1, address2]); // Save created records.

    var query = app.models.Country.newQuery();
    query.filters.Name._equals = countryName;
    var country = query.run()[0];

    country.Address.push(address1, address2);
    app.saveRecords([country]); // Save updated relation.
    

您还可以按照类似于以下代码段中的相关字段进行过滤:

function addressesForCountry(countryName) {
      var query = app.models.Address.newQuery();
      query.filters.Country.Name._equals = countryName;

      var records = query.run();
      return records;
    };
    

如需了解详情,请参阅关系过滤部分。

控制 CloudSQL 事务

以下示例显示如何使用服务器脚本来控制 Cloud SQL 事务。使用此 API 可以:

  • 启动一个事务并锁定相关记录
  • 提交对数据库的最新更改
  • 回滚一个事务
function submitOrder(orderKey) {

      // Sets a rollback point and starts a new transaction.
      app.transaction.cloudSql.start();

       // Locks all records that are read in the transaction until the end of the transaction.
      app.transaction.cloudSql.setLockOnRead(app.transaction.cloudSql.lockOnRead.UPDATE);

      var orderQuery = app.models.Order.newQuery();
      orderQuery.filters._key._equals = orderKey;
      var order = orderQuery.run()[0];

      // Read related order items
      var orderItems = order.OrderItems;

      var products = [];
      for (var i = 0; i < orderItems.length; i++) {
        // Read related product
        var product  = orderItem.Product;

        if (product.InStockQuantity < orderItem.Quantity) {
          // Insufficient stock to complete an order; transaction is rolled back.
          app.transaction.cloudSql.rollback();
          throw new app.ManagedError("Not enough " + product.Name + " in stock. " +
                                     product.InStockQuantity + " Order: " +
                                     orderItem.Quantity + " can't be completed.");
        }

        // Reduce inStock quantity
        product.InStockQuantity = product.InStockQuantity - orderItem.Quantity;
        products.push(product);
      }

      order.Status = "Submitted";
      app.saveRecords(products);
      app.saveRecords([order]);

      // Order is successfully processed; database changes committed.
      app.transaction.cloudSql.commit();

      return "Order submitted: " + orderKey;
    }
    

问题排查

应用制作工具会在您键入时公开语法错误,并以红色突出显示它们。它还可以检测常见问题(例如未使用的变量)并以警告的形式报告这些问题。

有时,您的脚本没有按预期运行,这种情况下,应用制作工具提供 console 对象,可用于记录任何相关信息。例如:

function getRecordsForCurrentUser() {
      var currentUser = Session.getActiveUser().getEmail();
      var query = app.models.Person.newQuery();
      query.filters.Email._equals = currentUser;

      var records = query.run();
      console.log("Found " + records.length + " records for " + currentUser);
      return records;
    };
    

在“预览”实例中,在控制台输出窗格中点击 Server logs。日志会在 Google Cloud Platform Console 中打开。

如需查看应用部署的日志,请执行以下操作:

  1. 在应用制作工具中,点击 Settings Deployments。
  2. 点击要查看的部署。
  3. 点击 View logs。日志会在 Google Cloud Platform Console 中打开。