 | 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."}
|
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);
}
}
|
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
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
O serviço de biblioteca também pode ser combinado com outros serviços usando
uma plataforma como WebSphere sMash.
|  |