IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope: Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  Web development | Open source  >

使用 PHP 在 CICS 上构建 REST 服务

developerWorks
前一页第 5 页,共 10 页后一页

文档选项

样例代码


对本教程的评价

帮助我们改进这些内容


综合学过的知识

到目前为止,您已经:

  • 了解如何通过 PHP 与 CICS COMMAREA 程序交互
  • 了解一些能够帮助构建 REST 式 Web 服务的 CA1S 约定和函数

我们将使用这些知识将书库程序公开为 REST 式 Web 服务。

REST 式 设计

资源

第一步是定义我们希望公开的资源。在这个场景中,我们使用惟一的资源类型 “书”,它有 4 个属性:

book {
  string: title,   // The title of the book
  string: author,	 // The author of the book
  boolean: onLoan, // Whether the book is on loan
  string: borrower // The person who borrowed the book
                   // if it is on loan, null otherwise.
}

方法

接下来,我们简单描述每个受支持方法的输入/输出数据:


表 6. 集合 URI (/ca1s/resources/book) 上的请求
HTTP 方法 在 book.php 中调用的处理程序方法 描述
GET book::onList() 返回用 JSON 编码的图书及其所有属性的列表。
POST book::onCreate() 向书库添加一本书。
这个请求必须提供用 JSON 编码的新书属性 titleauthor。这将返回一条消息,表明书已添加。
PUT book::onPutCollection() 不支持
DELETE book::onDeleteCollection() 不支持


表 7. 成员 URI (/ca1s/resources/book/10) 上的请求
HTTP 方法 在 book.php 中调用的处理程序方法 描述
GET book::onRetrieve() 返回使用 JSON 编码的书及其所有属性。如果要请求某个属性(例如,book/10/title),将仅返回该属性(用 JSON 编码)。
POST book::onPostMember() 不支持
PUT book::onUpdate() 将图书标记为已借出或已归还。这个请求必须提供一个包含 onLoanborrower 属性的 JSON 对象。不支持更新书的作者和标题。这将返回一条消息,表明书已添加。
DELETE book::onDelete() 从书库中将书永久删除。这将返回一条消息,表明书已删除。

任何错误都将导致服务返回一个相应的 HTTP 状态码,以及在 JSON 对象属性 errorMessage 中返回一条错误消息。例如,尝试获取 ID 为 909 的图书时,如果不存在这本书的话,将返回 HTTP 404 响应,其内容体为:

{"errorMessage":"Could not retrieve book 909 as it was not found."}
             





回页首


实现

最后,让我们根据以上的设计实现资源处理程序 book.php。resources/book.php 中的示例代码包包含了完整的脚本。

对所有事件通用的代码

脚本 book.php 包含的 book 类带有 LCRUD 处理程序方法,同时还包含除类定义之外的代码。在调用恰当的处理程序方法之前,将对所有请求执行这个代码。

在我们使用的场景中,在类定义之外导入表示 COMMAREA 和程序常量的 Java 类非常重要,因为它们是最常用的。我们还使用 PHP function header() 将响应内容类型设置为 text/json,因为所有数据都以 JSON 格式返回:


清单 7. 所有请求类型都使用的引导代码


// Load the Java classes
java_import('library.Library_Commarea');
java_import('library.Library_Constants');
// All responses will be JSON, so always set response content-type to text/json.
header('Content-Type: text/json');
             

我们还在一个全局函数中打包程序链接操作及其异常处理。这个函数对所有事件处理程序都是可用的。如果书库程序意外终止,脚本的执行将暂停,并且向客户端返回一条错误消息。


清单 8. 程序链接的包装器函数
/**
 * Invoke the library program with the supplied COMMAREA.
 * Notify the client if an error occurs.
 */
function runLibraryProgram($COMMAREA) {
  $program = new CICSProgram('LIBRARY');
  try {
    $program->link($COMMAREA);
  } catch (CICSException $e) {
    header('HTTP/1.1 500 Internal Server Error');
    $error['errorMessage'] = 
              'Exeption when linking with CICS program:' . $e->getMessage();
    echo json_encode($error);
    exit;
  }
}
             

最后,在调用任何处理程序之前调用类构造器,因此我们可以使用它设置所有方法都可以使用的属性。


清单 9. 图书资源处理程序构造器
  /**
   * The constructor is invoked before event handlers.
   */
  function __construct() {
    $this->COMMAREA = new Library_Commarea();
    $this->constants = new Library_Constants();
  }
             

接下来,我们看看每个事件处理程序的实现。

onList

onList() 处理程序实现类似于 library.php 脚本(我们使用它了解如何从 PHP 调用 COMMAREA)的逻辑:它使用 LIST 命令调用书库程序,然后遍历图书集合。然而,这个版本不仅输出图书的标题和作者,而且还构建一个表示图书的关联数组,然后将其序列化为 JSON。它还检查调用书库返回的代码,如果出现问题,将向客户端发送一条错误消息。


清单 10. onList 处理程序
  /**
   * Respond with a JSON-encoded list of books.
   */
  function onList() {
    // Attempt to get book list
    $this->COMMAREA->setLibRequestType('LIST');
    runLibraryProgram($this->COMMAREA);

    // Process return code
    switch ($this->COMMAREA->getLibReturnCode()) {
      case $this->constants->getLibraryOk():
        // Return the list of books
        $books = array();
        for ($i = 0; $i<$this->COMMAREA->getLibItemCount(); $i++) {
          $CICSbook = $this->COMMAREA->getLibBookItem($i);
          $bookId = $CICSbook->getBookItemRef();
          $books[$bookId]['title'] = trim($CICSbook->getBookTitle());
          $books[$bookId]['author'] = trim($CICSbook->getBookAuthor());
          $books[$bookId]['onLoan'] = $CICSbook->isBookOnloan();
          $books[$bookId]['borrower'] = $books[$bookId]['onLoan'] ?
          trim($CICSbook->getBookBorrower()) : null;
        }
        echo json_encode($books);
        break;
      default:
        // Notify client of internal error
        header('HTTP/1.1 500 Internal Server Error');
        $error['errorMessage'] = 'Unexpected return code when listing books: ' 
                                        . $this->COMMAREA->getLibReturnCode();
        echo json_encode($error);
    }
  }

onCreate

onCreate() 处理程序首先从请求获取输入数据并验证该数据,然后采用类似于 onList() 的模式。使用 PHP 函数 header()(见 参考资料)在请求成功后设置恰当的 HTTP 响应代码。


清单 11. onCreate 处理程序
  /**
   * Add book to library based on JSON data in the request body.
   */
  function onCreate() {
    // Check input data for new book
    $book = json_decode(zget('/request/input/transcoded'), true);
    if (!is_array($book) || !isset($book['title'], $book['author'])) {
      header('HTTP/1.1 400 Bad Request');
      $error['errorMessage'] = 'Bad book data: ' . zget('/request/input/transcoded') 
                                        . '. Please specify an author and a title.';
      echo json_encode($error);
      return;
    }

    // Attempt to create book
    $this->COMMAREA->setLibRequestType('ADD');
    $this->COMMAREA->getLibBookItem(0)->setBookTitle($book['title']);
    $this->COMMAREA->getLibBookItem(0)->setBookAuthor($book['author']);
    runLibraryProgram($this->COMMAREA);

    // Process return code
    switch ($this->COMMAREA->getLibReturnCode()) {
      case $this->constants->getLibraryOk():
        header('HTTP/1.1 201 Created');
        $bookId = $this->COMMAREA->getLibBookItem(0)-> getBookItemRef();
        $status['statusMessage'] = "Successfully created book $bookId.";
        echo json_encode($status);
        break;
      case $this->constants->getLibraryFull():
        header('HTTP/1.1 400 Bad Request');
        $error['errorMessage'] = 'Could not create book : Library is full.';
        echo json_encode($error);
        break;
      default:
        header('HTTP/1.1 500 Internal Server Error');
        $error['errorMessage'] = 'Unexpected return code when creating book ' 
                                       . $this->COMMAREA->getLibReturnCode();
        echo json_encode($error);
        break;
    }
  }

onRetrieve

onRetrieve() 类似于 onList(),但它访问的是一本书,而不是完整的列表。另外,对于子路径的请求(比如 book/10/title),它还包含访问图书的某个属性的逻辑。请求的属性由 zget('/event/pathInfo') 决定。


清单 12. onRetrieve 处理程序
  /**
   * Respond with a JSON representation of an individual book, or a specific attribute
   * of a book.
   */
  function onRetrieve() {
    $bookId = zget('/request/params/bookId');
    $this->COMMAREA->setLibRequestType('QUERY');
    $this->COMMAREA->getLibBookItem(0)->setBookItemRef($bookId);
    runLibraryProgram($this->COMMAREA);

    // Process return code
    switch ($this->COMMAREA->getLibReturnCode()) {
      case $this->constants->getLibraryOk():
        $book['title'] = trim($this->COMMAREA->getLibBookItem(0)->getBookTitle());
        $book['author'] = trim($this->COMMAREA->getLibBookItem(0)->getBookAuthor());
        $book['onLoan'] = $this->COMMAREA->getLibBookItem(0)->isBookOnloan();
        $book['borrower'] = $book['onLoan'] ?
                trim($this->COMMAREA->getLibBookItem(0)->getBookBorrower()) : null;
        break;
      case $this->constants->getLibraryNotfound():
        header('HTTP/1.1 404 Not Found');
        $error['errorMessage'] = "Could not retrieve book $bookId as it was not found.";
        echo json_encode($error);
        return;
      default:
        header('HTTP/1.1 500 Internal Server Error');
        $error['errorMessage'] = "Unexpected return code deleting book $bookId: " 
                                           . $this->COMMAREA->getLibReturnCode();
        echo json_encode($error);
        return;
    }

    // Send back appropriate info about the book
    $requestedInfo = zget('/event/pathInfo');
    switch($requestedInfo) {
      case null:
        // return the whole book
        echo json_encode($book);
        break;
      case '/title':
      case '/author':
      case '/onLoan':
      case '/borrower':
        // return just the relevant info
        $requestedInfo = substr($requestedInfo, 1);
        echo json_encode(array($requestedInfo => $book[$requestedInfo]));
        break;
      default:
        header('HTTP/1.1 404 Not Found');
        $error['errorMessage'] = "Could not retrieve book detail $requestedInfo 
                          about book $bookId: don't know what $requestedInfo is.";
        echo json_encode($error);
    }
  }
               

onUpdate

onUpdate() 处理程序检查输入数据的正确性,它可能包含 onLoanborrower 属性。然后通过调用恰当的 CICS 程序将图书标记为 “已借出” 或 “已归还”。和前面的处理程序一样,将对程序的返回代码进行检查,并恰当地处理错误。与 OnRetrieve() 不同,这个处理程序不支持对子路径(例如,book/10/onLoan)的单个属性执行操作;如果您想进一步体验,可以放心添加这个函数!


清单 13. onUpdate 处理程序
  /**
   * Update the status of a book to mark it as borrowed or returned.
   */
  function onUpdate() {
    // Check input data
    $bookId = zget('/request/params/bookId');
    $updateData = json_decode(zget('/request/input/transcoded'), true);
    if (!isset($updateData['onLoan'])) {
      header('HTTP/1.1 400 Bad Request');
      $error['errorMessage'] = 'Bad book update data: ' .
      zget('/request/input/transcoded') . '. Please specify onLoan.';
      echo json_encode($error);
      return;
    }
    if ($updateData['onLoan'] && empty($updateData['borrower'])) {
      header('HTTP/1.1 400 Bad Request');
      $error['errorMessage'] = 'Bad book update data: ' .
      zget('/request/input/transcoded') . '. Please specify a borrower.';
      echo json_encode($error);
      return;
    }

    // Attempt to update book status
    $this->COMMAREA->setLibRequestType($updateData['onLoan'] ? 'BORROW' : 'RETURN');
    $this->COMMAREA->getLibBookItem(0)->setBookItemRef($bookId);
    $this->COMMAREA->getLibBookItem(0)->setBookLoanStatus($updateData['onLoan']);
    if (isset($updateData['borrower'])) {
      $this->COMMAREA->getLibBookItem(0)->setBookBorrower($updateData['borrower']);
    }
    runLibraryProgram($this->COMMAREA);

    // Process return code
    switch ($this->COMMAREA->getLibReturnCode()) {
      case $this->constants->getLibraryOk():
        $status['statusMessage'] =  "Successfully updated status of book $bookId.";
        echo json_encode($status);
        break;
      case $this->constants->getLibraryNotfound():
        header('HTTP/1.1 404 Not Found');
        $error['errorMessage'] = "Could not update book $bookId as it was not found.";
        echo json_encode($error);
        break;
      default:
        header('HTTP/1.1 500 Internal Server Error');
        $error['errorMessage'] = 
        "Unexpected return code when updating status of book $bookId: " 
                                 . $this->COMMAREA->getLibReturnCode();
        echo json_encode($error);
    }
  }
             

onDelete

最后一个处理程序是 onDelete(),它通过调用 CICS 程序对请求 URI 中的图书 ID 执行 “DELETE” 操作。


清单 14. onDelete 处理程序
  /**
   * Delete a book from the library.
   */
  function onDelete() {
    // Attempt to Delete book
    $bookId = zget('/request/params/bookId');
    $this->COMMAREA->setLibRequestType('DELETE');
    $this->COMMAREA->getLibBookItem(0)->setBookItemRef($bookId);
    runLibraryProgram($this->COMMAREA);

    // Process return code
    switch ($this->COMMAREA->getLibReturnCode()) {
      case $this->constants->getLibraryOk():
        $status['statusMessage'] =  "Successfully deleted book $bookId.";
        echo json_encode($status);
        break;
      case $this->constants->getLibraryNotfound():
        header('HTTP/1.1 404 Not Found');
        $error['errorMessage'] = "Could not delete book $bookId as it was not found.";
        echo json_encode($error);
        break;
      default:
        header('HTTP/1.1 500 Internal Server Error');
        $error['errorMessage'] = "Unexpected return code deleting book $bookId: " 
                                           . $this->COMMAREA->getLibReturnCode();
        echo json_encode($error);
    }
  }
             





回页首


使用 Web 服务

现在,这个书库应用程序已经通过一致的接口公开,并且使用明确定义的 JSON 数据结构进行通信,因此,各种客户端都可以轻松访问它。测试该服务的最简单方法是使用轻量级的 REST 客户端,比如 Poster add-on for Firefox(见 参考资料)。


图 5. 使用简单的客户端测试服务
使用 Poster add-on for Firefox 的 REST 客户端

在示例代码中,我们包含了一个向服务发出 Ajax 请求的 HTML 和 JavaScript 页面。如果您要试用它,可以将 library/scripts/ajaxLibrary.php 复制到您的 CICS 系统的 /ca1s/work/scripts/ajaxLibrary.php 目录下(将 resources/book.php 转移到 /ca1s/work/resources/book.php,如果还没有转移的话),然后在浏览器中访问它。


图 6. 服务的 Ajax 前端
示例书库应用程序的屏幕快照,它显示了几本样例书籍的列表视图

可以通过某个平台(比如 WebSphere sMash)将这个书库服务与其他服务进行混搭。





回页首



前一页第 5 页,共 10 页后一页
    关于 IBM 隐私条约 联系 IBM 使用条款