Avançar para a área de conteúdo

ir para o conteúdo principal

developerWorks Brasil  >  Software livre  >

Construir um Serviço RESTful no CICS com PHP

developerWorks
Ir para a página anteriorPágina 5 de 10 Ir para a próxima página

Opções de documento

Código de amostra


Classificar este tutorial

Ajude-nos a melhorar este conteúdo


Juntando Tudo

Até o momento você:

  • Estabeleceu como interagir com programas CICS COMMAREA a partir do PHP
  • Aprendeu algumas das convenções e funções do CA1S que ajudam construir serviços da Web RESTful

Agora usaremos este conhecimento para expor o programa de biblioteca como um serviço da Web RESTful.

Design do RESTful

O Recurso

A primeira etapa é definir os recursos aos quais pretendemos fornecer acesso. Neste cenário, temos um único tipo de recurso, book, que possui quatro propriedades:

book {
  string: title,   // O título do book
  string: author,	 // O autor do book
  boolean: onLoan, // Se o book está emprestado
  string: borrower // A pessoa que emprestou o book
                   // se não estiver emprestado, será nulo.
}

Os Métodos

A seguir descrevemos brevemente os dados de entrada/saída pretendidos e esperados de cada método suportado:


Tabela 6. Pedidos no URI de coleta (/ca1s/resources/book)
Método de HTTP Método do manipulador invocado em book.php Descrição
GET book::onList() Retorna a lista completa de books e todas as suas propriedades, codificadas em JSON.
POST book::onCreate() Inclui um book na biblioteca.
O pedido deve fornecer as propriedades título e author do novo book, codificadas em JSON. Retorna uma mensagem indicando que o book foi incluído.
PUT book::onPutCollection() Não-suportado
DELETE book::onDeleteCollection() Não-suportado


Tabela 7. Pedidos em um URI do membro (/ca1s/resources/book/10)
Método de HTTP Método do manipulador invocado em book.php Descrição
GET book::onRetrieve() Retorna um único book e todas as suas propriedades, codificados em JSON. Se uma propriedade individual for solicitada (por exemplo, book/10/title), retorna apenas essa propriedade, codificada em JSON.
POST book::onPostMember() Não-suportado
PUT book::onUpdate() Marca um book como emprestado ou devolvido. O pedido deve fornecer um objeto JSON contendo as propriedades onLoan e borrower . A atualização do autor ou título não é suportada. Retorna uma mensagem indicando que o book foi incluído.
DELETE book::onDelete() Remove permanentemente um da biblioteca. Retorna uma mensagem indicando que o book foi excluído.

Os casos de erros resultarão no serviço retornando o código de status de HTTP apropriado e uma mensagem de erro em uma propriedade do objeto JSON denominada errorMessage. Por exemplo, tentar recuperar um book com o ID 909 quando não houver este book retornará uma resposta de HTTP 404 com o seguinte corpo:

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



Voltar para parte superior


Implementação

Por último, implementaremos o manipulador de recursos book.php com base no design acima. O script completo está incluído no pacote do código de amostra em resources/book.php.

Código Comum a Todos os Eventos

O script book.php contém um book de classe com métodos do manipulador LCRUD, mas também inclui código fora da definição de classe. Este código será executado para todos os pedidos, antes da chamada do método do manipulador apropriado.

Em nosso cenário, é útil importar as classes Java que representam a COMMAREA e as constantes do programa fora da definição de classe, porque, provavelmente, elas serão usadas independentemente do evento. Também configuramos o tipo de conteúdo de resposta como usando a função PHP header(), porque sabemos que todos os pedidos retornarão dados em formato JSON:


Listagem 7. Código de introdução usado por todos os tipos de pedidos


// Carregar as classes Java
java_import('library.Library_Commarea');
java_import('library.Library_Constants');
// Todas as respostas serão JSON, portanto, sempre configure o tipo de conteúdo de 
resposta como text/json.
header('Content-Type: text/json');
             

Também agrupamos a operação de link do programa e sua manipulação de exceção em uma função global. Esta função estará disponível para todos os manipuladores de eventos. Caso o programa de biblioteca seja finalizado anormalmente, a execução do script será paralisada e será retornada uma mensagem de erro para o cliente.


Listagem 8. Função de wrapper para o link do programa
/**
 * Invoque o programa de biblioteca com a COMMAREA fornecida.
 * Notifique o cliente se ocorrer um erro.
*/
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;
  }
}
             

Por último, o construtor da classe também será invocado antes de qualquer manipulador; portanto, podemos usá-lo para configurar propriedades que, provavelmente, serão usadas por todos os métodos.


Listagem 9. O construtor do manipulador de recursos do book
  /**
   * O construtor é invocado antes dos manipuladores de eventos.
   */
  function __construct() {
    $this->COMMAREA = new Library_Commarea();
    $this->constants = new Library_Constants();
  }
             

A seguir, vamos observar a implementação de cada um dos manipuladores de eventos.

onList

Os onList() implementa uma lógica semelhante ao script library.php que foi usado para aprender como chamar programas COMMAREA a partir do PHP: ele invoca o programa de biblioteca com um comando LIST, em seguida, itera sobre a coleta de books. No entanto, em vez de apenas imprimir o título e autor, esta versão constrói um array associativo que representa os books, em seguida, serializa-o para JSON. Ela também verifica o código de retorno de chamada da biblioteca e envia uma mensagem de erro para o cliente, se algo estiver errado.


Listagem 10. O manipulador onList
  /**
   * Responder com uma lista de books codificada por JSON.
   */
  function onList() {
    // Tentativa de obter a lista de books
    $this->COMMAREA->setLibRequestType('LIST');
    runLibraryProgram($this->COMMAREA);

    // Processar código de retorno
    switch ($this->COMMAREA->getLibReturnCode()) {
      case $this->constants->getLibraryOk():
        // Retornar a lista de 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:
        // Notificar o cliente sobre erro interno
        header('HTTP/1.1 500 Internal Server Error');
        $error['errorMessage'] = 'Unexpected return code when listing books: '
                                        . $this->COMMAREA->getLibReturnCode();
        echo json_encode($error);
    }
  }

onCreate

O manipulador onCreate() inclui uma etapa inicial para recuperar e verificar os dados de entrada do pedido, depois, segue um padrão semelhante a onList(). A função PHP header() (consulte Recursos) é usada para configurar o código de resposta de HTTP apropriado, dependendo do êxito do pedido.


Listagem 11. O manipulador onCreate
  /**
   * Incluir book na biblioteca com base em dados de JSON no corpo do pedido.
   */
  function onCreate() {
    // Verificar novo book nos dados de entrada
    $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;
    }

    // Tentativa de criar book
    $this->COMMAREA->setLibRequestType('ADD');
    $this->COMMAREA->getLibBookItem(0)->setBookTitle($book['title']);
    $this->COMMAREA->getLibBookItem(0)->setBookAuthor($book['author']);
    runLibraryProgram($this->COMMAREA);

    // Processar código de retorno
    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() se assemelha a onList(), mas acessa um único book em vez da lista completa. Além disso, inclui lógica para acessar uma propriedade individual de um book para pedidos em subcaminhos, como book/10/title. A propriedade solicitada é determinada com zget('/event/pathInfo').


Listagem 12. O manipulador onRetrieve
  /**
   * Responder com uma representação de JSON de um book individual, 
   * ou um atributo específico de um book.
   */
  function onRetrieve() {
    $bookId = zget('/request/params/bookId');
    $this->COMMAREA->setLibRequestType('QUERY');
    $this->COMMAREA->getLibBookItem(0)->setBookItemRef($bookId);
    runLibraryProgram($this->COMMAREA);

    // Processar código de retorno
    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;
    }

    // Retornar informações apropriadas sobre o book
    $requestedInfo = zget('/event/pathInfo');
    switch($requestedInfo) {
      case null:
        // devolver o book inteiro
        echo json_encode($book);
        break;
      case '/title':
      case '/author':
      case '/onLoan':
      case '/borrower':
        // retornar apenas informações relevantes
        $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

O manipulador onUpdate() verifica a exatidão dos dados de entrada, que devem conter propriedades onLoan e borrower. Ele marca então os books como "emprestado" ou "devolvido", invocando o programa CICS conforme apropriado. Assim como os manipuladores anteriores, o código de retorno do programa é verificado e os casos de erros são processados conforme apropriado. Diferente de OnRetrieve(), este manipulador não suporta operações em subcaminhos de propriedade individual (por exemplo, book/10/onLoan); sinta-se à vontade para incluir esta função, se desejar fazer mais experiências!


Listagem 13. O manipulador onUpdate
  /**
   * Atualize o status de um book para marcá-lo como emprestado ou devolvido.
   */
  function onUpdate() {
    // Verificar dados de entrada
    $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;
    }

    // Tentativa de atualizar o status do book
    $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);

    // Processar código de retorno
    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

Por último, o manipulador onDelete() invoca o programa CICS para executar uma operação 'DELETE' no ID do book fornecido no URI do pedido.


Listagem 14. O manipulador onDelete
  /**
   * Exclua um book da biblioteca.
   */
  function onDelete() {
    // Tentativa de Excluir book
    $bookId = zget('/request/params/bookId');
    $this->COMMAREA->setLibRequestType('DELETE');
    $this->COMMAREA->getLibBookItem(0)->setBookItemRef($bookId);
    runLibraryProgram($this->COMMAREA);

    // Processar código de retorno
    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);
    }
  }
             



Voltar para parte superior


Consumindo o Serviço da Web

Agora que o aplicativo de biblioteca é exposto através de uma interface consistente e se comunica usando estruturas de dados JSON claramente definidas, ele pode ser facilmente acessado por uma grande variedade de clientes. A maneira mais simples de testar o serviço é com um cliente leve REST, como o complemento Poster para Firefox (consulte Recursos).


Figura 5. Testando seu serviço com um cliente REST simples
O cliente REST é mostrado em ação usando o complemento do Poster para Firefox.

No código de amostra, incluímos uma página HTML e Javascript de exemplo que emite pedidos Ajax para o serviço. Ela pode ser testada copiando library/scripts/ajaxLibrary.php para seu sistema CICS em /ca1s/work/scripts/ajaxLibrary.php (e, se ainda não transferiu, transfira resources/book.php para /ca1s/work/resources/book.php), em seguida, acesse-o em seu navegador.


Figura 6. Um front end Ajax para seu serviço
Uma captura de tela do aplicativo Example Library mostrando uma visualização em colunas de vários books de amostra.

O serviço de biblioteca também pode ser combinado com outros serviços usando uma plataforma como WebSphere sMash.



Voltar para parte superior



Ir para a página anteriorPágina 5 de 10 Ir para a próxima página