Guias

Published on June 24th, 2015 | by Leo Schmidt

5

BSP + jQuery Mobile, ou a aventura de SAP-webdev – Parte III: Backend

Acho que não demorou tanto quanto a saga do Freeza, mas sim caros zumbis, chegamos ao final desse guia! Vou falar agora da parte principal do desenvolvimento: a aplicação BSP. Aqui vou mostrar como estruturar os recursos no backend e também como ele vai responder a tudo o que acontece no frontend.

…mas antes, um pouco mais de frontend

Lembra, lá primeira parte, quando eu disse que é necessário subir as bibliotecas de jQuery e jQuery Mobile e os plugins no repositório MIME do SAP? Então, se você fez o frontend fora da SE80, é bem provável que o código que você fez ainda não esteja referenciando as bibliotecas que estão no repositório. Os seus headers podem estar mais ou menos assim:

O endereço da aplicação BSP, na maioria das vezes, vai ser algo como http://[servidor]:[porta]/sap/bc/bsp/[namespace]/[aplicação]. Aproveitando essa estrutura, podemos referenciar de maneira indireta as bibliotecas que estão disponíveis globalmente. Por exemplo, se você tiver organizado as bibliotecas no repositório MIME mais ou menos com a mesma estrutura do CDN do jQuery, desse jeito:

mime_repository

Você consegue mudar as declarações acima por algo assim (os endereços sempre são case insensitive):

E agora eu posso finalmente explicar o porquê de usar o repositório MIME e fazer essas mudanças no header: é pra atender a política de segurança dos navegadores que se chama same-origin policy (política de mesma origem). Isso serve tanto para views que você vai usar no BSP como para as que você definiu nos métodos da classe de login.

Outro detalhe que dá pra perceber pela imagem e pelo código: uma prática comum em desenvolvimento web é fazer versões minified (minimizadas ou comprimidas) de todos os arquivos que compõem as páginas, com o mesmo nome mais a extensão .min.[extensão original], de modo que a aplicação trafegue uma quantidade menor de dados entre servidor e navegador. Em tempo de desenvolvimento ou de debug, referencia-se o arquivo original, e depois que tudo estiver pronto, comprimimos e mudamos as referências para apontar para os arquivos comprimidos. O Sublime Text, que eu indiquei no post anterior, tem alguns packages que fazem isso, como por exemplo o Minify (para a glória da obviedade!), que comprime JavaScript e CSS. Eu não achei ainda nenhum package bacana pra minimizar HTML no Sublime, então eu uso ferramentas on-line mesmo como o HTML Minifier.

Estruturando a aplicação

Eu pensei em colar aqui uma imagem com o famoso diagrama do MVC, mas eu não sabia qual escolher:

Tem até um com o Homer ali

Vamos tentar o segundo ali mesmo:

mvc

O fluxo representado por esse diagrama é basicamente o que a aplicação BSP deve fazer:

  1. Usuário envia uma request HTTP ao controller;
  2. Controller requisita dados do model;
  3. A resposta depende do conteúdo: se a request foi comum, pedindo por uma view, a aplicação a instancia e esta é exibida ao usuário; se a request foi feita via Ajax, os dados interpretados são devolvidos diretamente do controller para a view pela response, que então os interpreta e exibe o resultado ao usuário.

Dessa maneira, uma aplicação BSP mais básica que use jQuery / jQuery Mobile será composta de uma classe para o controller e uma classe para o model. A view não precisa necessariamente de uma classe própria, porque a ideia é que a manipulação da view seja feita através dos scripts inseridos na página, sem intervenção ABAP – mas se você tiver que montar HTML muito complexo ou de maneira muito frequente dentro dos métodos do controller (vou falar sobre isso mais pra baixo), talvez seja melhor ter uma classe que faça somente isso. Essa estrutura pode ser repetida quantas vezes forem necessárias; se a lógica de tratamento das requests/responses ou da seleção dos dados é suficientemente complicada para que haja separação em mais controllers ou models, esse é o caminho.

“Mas Leo, eu posso escrever tudo direto num controller só! Pra quê eu vou criar trocentas classes só pra UMA aplicação?” Porque eu espero que você vai ser uma pessoa boa e seguir a arquitetura MVC. Afinal, você não quer deixar um ninho de rato em forma de classe pra quem for dar manutenção na aplicação depois, certo? CERTO? Bom então.

A estrutura do BSP, usando a arquitetura multipage no frontend, ficaria mais ou menos assim:

Por partes, vamos.

Características da aplicação

bsp_application_properties

Nada muito complexo por aqui:

  • Em Initial BSP (“BSP página inicial”) se indica o nome do controller principal da aplicação;
  • Normalmente não é necessário indicar uma Application Class (“Classe de aplicação”), mas se for necessário segregar a lógica da aplicação da aplicação em si, podemos indicar uma classe aqui, que pode ser acessada através do atributo APPLICATION do controller;
  • Não é necessário indicar nada em Theme (“Tema”), porque carrega-se os temas diretamente pelo markup com as tags <link>;
  • A questão do estado da aplicação é aberta. É possível desenhar a aplicação de maneira que não se dependa de dados guardados no model, caso onde a opção Stateful (“Com status”) pode ficar desmarcada. Porém, se a aplicação trata uma quantidade muito grande de dados ou tem um fluxo lógico muito complexo, é melhor manter estados e essa opção precisaria ficar marcada;
  • Supports Portal Integration (“Suporta integração Portal”) depende da sua aplicação. A integração com o Portal geralmente tem a ver com navegação e gerenciamento de sessões, parecido com o que acontece com aplicações Webdynpro ABAP. Ative conforme necessário;
  • XSRF Protection (“Proteção XSRF”) é a proteção do framework BSP para prevenir cross-site request forgery. Esta SAP note explica o que é e como usar essa opção.

Objetos MIME

Aqui é onde se deve importar o código específico da sua view: o HTML descomprimido, os scripts comprimidos e descomprimidos, os stylesheets comprimidos e descomprimidos, e quaisquer outros objetos estáticos específicos para a sua aplicação: logotipos, vídeos, áudios, PDFs, etc. No HTML é possível referenciar diretamente os objetos que você importar usando somente o nome dos mesmos. O exemplo do cabeçalho que eu usei acima, então, ficaria:

View

bsp_view

Aqui, também, nada extraordinário:

  • Observe, em primeiro lugar, que a view contém o código HTML comprimido, que ficará na aba Layout – portanto o nome index.min.htm. A página com o código original descomprimido fica como objeto MIME do BSP, para futura referência/manutenção;
  • É importante deixar o atributo Compression (“Comp.páginas”) em branco (com o valor “None” ou “nenhuma”), porque os outros tipos de compressão podem afetar a sintaxe do HTML que já esteja minimizado;
  • O atributo W/O Script Code (“sem cód.script“) deve ficar desmarcado, pois a página contém scripts;
  • O Page Type (“Tipo de página”) é View (“Visão”), e você pode indicar qualquer controller ali (contanto que a classe seja descendente da CL_BSP_CONTROLLER – você pode inclusive indicar ela própria);
  • Esta página não será uma página de erro (falei sobre elas na primeira parte da série), portanto o atributo Is Error Page fica desmarcado. Normalmente não se atribui aqui uma página de erro específica, pois as mensagens podem ser exibidas com outras técnicas (que eu expliquei na segunda parte da série), porém em Assigned Error Page (“Pág.erro atribuída”) você pode indicar uma outra página dentro da aplicação que servirá como página de erro;
  • Por fim, mantém-se desmarcado o atributo Delta Handling (“Tratmto.delta”), porque não queremos que o framework do BSP se intrometa na maneira como carregamos as páginas da aplicação.

Controller

bsp_controller

Assim como nas outras páginas de configuração, nada muito estranho:

  • O nome do controller pode ser como você quiser, e não precisa necessariamente terminar com .do. Para o controller principal é comum usar main;
  • Em Controller Class (“Classe controlador”) indica-se a classe criada para servir de controller da aplicação. Esta classe deve ser descendente da CL_BSP_CONTROLLER;
  • A opção Start BSP (“BSP início”) funciona em conjunto com a opção XSRF Protection da aplicação, portanto veja a nota que eu mencionei mais acima para entender como usá-la;
  • As opções de página de erro funcionam aqui da mesma maneira que funcionam na view;
  • Com relação ao estado, a escolha aqui é a mesma que foi feita para a característica da aplicação em si, com a diferença que o estado do controller pode ser mantido (stateful) independentemente do estado da aplicação;
  • O armazenamento em cache pode ter um tempo maior do que zero, mas isso funciona melhor em aplicações stateless. Em aplicações stateful as opções de Caching podem ficar em zero mesmo;
  • Compression (“Compressão”) e HTTPS são opcionais. A primeira comprime a página antes de enviá-la ao navegador, e a segunda trafega-a usando SSL;
  • Assim como na view, deixe desmarcada a opção Delta Handling (“Tratmto.delta”).

Classe do controller

Essa classe é responsável por receber, tratar e responder as requests. Como já falei algumas vezes aqui, essa classe precisa ser descendente da CL_BSP_CONTROLLER.

A ideia é aproveitar o mínimo dessa herança apenas para fazer com que o controller responda as requests, tanto as geradas pelo usuário quanto as feitas via Ajax. Para isso, é necessário:

  1. Redefinir e implementar o método DO_INIT. A implementação desse método tem uma função muito simples, que é inicializar o(s) model(s) e guardar a referência da instância. Para isso, basta chamar o método CREATE_MODEL:

    (FINALMENTE chegamos no ABAP, hein?) O parâmetro model_id serve para encontrar a instância de model correta no atributo M_MODELS através do método GET_MODELS. Se você vai trabalhar com apenas um model (como é o caso desse exemplo), é mais prático guardar a instância do model diretamente num atributo novo da classe, como esse me->model que eu usei no exemplo acima, que no caso referencia a classe Z_MINHA_CLASSE_DO_MODEL.
  2. Redefinir e implementar o método DO_REQUEST. Essa é a implementação principal. O fluxo é basicamente como nesse exemplo aqui:

    “Ué Leo, mas de onde veio esse código 422 pra erro? Não é pra usar o 500?” Não, porque 500 é um código genérico pra dizer que aconteceu algum erro no servidor, e o framework do BSP já usa esse código pra informar dumps, o que é adequado. Os erros que acontecem já dentro dos event handlers na maioria das vezes são provocados por entradas mal-formadas, por culpa do usuário ou do processamento ABAP, e não necessariamente por causa do servidor. Por isso o erro devolvido aqui fica melhor na categoria 4xx (client error).

    “Ah… mas então porque não usar logo o 400 – Bad request mesmo?” Porque o problema não é com a request em si, mas sim com os dados contidos (pra falar mais empolado, não é sintático, é semântico). Por exemplo: se o código do material não existe, isso não é necessariamente um problema com a request, mas sim com o próprio código que pode estar errado. Eis um post explicando um pouco sobre isso.

  3. Criar event handlers específicos para cada action Ajax. Não precisam ser event handlers reais no contexto de ABAP Objects, mas sim apenas métodos que respondem a cada action Ajax definida no frontend, como eu mostrei nesse último exemplo. O fluxo de cada método vai ser simples: interpreta-se os parâmetros da request, transformando os dados conforme necessário, chama-se o método do model que tratará os dados e formata-se a resposta a ser devolvida ao DO_REQUEST. No exemplo abaixo, temos um event handler que vai ao model buscar uma lista de materiais a partir do centro e do depósito:

    O resultado desse método é um objeto JSON contendo um array de objetos representando os registros da MAKT que correspondem à busca feita pelo usuário, ou um outro objeto JSON com o tipo e texto da mensagem que será exibida em popup.

    Existe um certo dilema sobre o tipo de conteúdo que deve ser usado na resposta de uma request Ajax. É melhor retornar JSON e montar a exibição pelo script, ou devolver HTML montado e somente integrá-lo na página, também pelo script? Essa decisão cabe ao desenvolvedor, e pode sem problemas resultar em implementações diferentes para cada método. No projeto que eu estou usando de exemplo, há um pouco de ambas as técnicas: alguns métodos devolvem HTML montado, outros devolvem JSON. A regra que eu uso é a seguinte: se for simples montar o HTML pelo script, o método retorna JSON; caso contrário, o método retorna o HTML já montado.

Classe do model

Aqui é onde acontece a “mágica”, se por mágica entendermos o núcleo do trabalho ABAP – validação complexa das entradas, operações com o banco de dados, chamadas de funções, etc. Esta classe precisa ser descendente da CL_BSP_MODEL, porém, para este tipo de uso, não necessita da redefinição de nenhum dos métodos herdados.

Se estivermos trabalhando com uma aplicação stateful, os resultados das operações da aplicação ficarão armazenados em atributos dessa classe, e será necessário controlar em alguns pontos específicos os valores desses atributos. Por exemplo, uma tabela que represente o resultado da busca de um relatório pode precisar ser limpa toda vez que se chama a página do relatório, da mesma maneira que se faz com variáveis globais em um grupo de funções. Para isso é necessário criar uma chamada Ajax na entrada de cada página, e também métodos correspondentes no controller e no model para fazer a limpeza dos atributos.

É bom lembrar também que, numa aplicação stateful, as requests Ajax que resultam em um estado intermediário (ou seja, actions do tipo set*que apenas gravam dados nos atributos do modeltambém estão alterando dados no servidor e portanto devem ser iniciadas com o método POST.

Esta classe deve esperar parâmetros de entrada em seus métodos com dados já convertidos para os tipos ABAP, o que deve ser feito anteriormente pelo controller. Da mesma forma, os dados gerados pelo processamento do model são apenas retornados para o controller, que será responsável por formatá-los adequadamente e incluí-los na resposta, conforme o exemplo de event handler que está acima – portanto essa classe não deve ser responsável por montar HTML ou JSON. As mensagens, por exemplo, podem ser chamadas com o uso de MESSAGE ... INTOe retornadas em uma string, retornando também o valor de SY-MSGTY.


Assim chegamos ao fim desse guia maluco. Espero ter ajudado vocês a terem uma ideia de como usar tecnologias de frontend, e de como integrar isso com o BSP, e qual é o papel de cada um numa aplicação. Não é a coisa mais nova em folha em termos de desenvolvimento web pra SAP, mas pra quem gosta de interface isso dá um sabor diferente de WebDynpro ABAP e de SAP GUI. Espero que eu consiga mais pra frente fazer um guia espelho desse, usando Gateway e UI5, mas por enquanto é isso.

Dúvidas, reclamações ou sugestões, favor entrar em contato com o departamento de comentários abaixo ou via e-mail. Até mais!

Tags: , ,


About the Author

ABAP profissional desde dez/2008. Meu negócio é desenvolver ABAP, mas sem pensar só em ABAP: gosto de fazer o SAP conversar com outras linguagens e sistemas, sempre de olho no Basis.



5 Responses to BSP + jQuery Mobile, ou a aventura de SAP-webdev – Parte III: Backend

  1. Muito interessante! Quando você fizer esse guia na SAPUI5 + Gateway estarei na primeira fila para pegar meu exemplar 🙂

    Confesso que meu critério para escolha de qual diagrama MVC escolher foi o mesmo que o seu:
    http://abap101.com/2012/04/01/falsa-programacao-orientada-objetos/

    Abraços!

  2. Ficou animal! (só acabei de ler hoje haha).

    Tendo trabalho com você com bsps tempos atrás, é muito legal ver quantas melhorias você fez da nossa singela arquitetura de primeira viagem, principalmente de performance (com a minificazizacação) e uso de JSONs 🙂

    Parabéns e abs mano!

  3. Max says:

    Opa…blz cara… ficou muito bom o post!! só tenho um problema na tela de login: ao trocar as referencias css e javascript para o endereço local, é requirida a autenticação para o css e javascript?! se eu ignorar esta autenticação, ele dá “401 Unauthorized” no cabeçalho de cada include.. se eu me autenticar, ele ignora a tela de login!
    Você poderia me ajudar?! vlw!

    • Leo Schmidt says:

      Fala Max, valeu!

      Agora que vc falou, eu acabei testando aqui e tem esse problema mesmo. Realmente, não dá pra usar o mesmo caminho dos recursos na tela de login e na aplicação em si.

      A solução, que no caso é meio gambiarrenta, é subir as bibliotecas de jQuery e jQuery Mobile TAMBÉM em outro diretório no repositório. Um diretório público que a tela de login consegue enxergar é esse aqui:

      [host:porta]/sap/public/bc/ur

      Nesse diretório ficam as bibliotecas de Unified Rendering, que são responsáveis pelo layout da tela de login standard. Pode criar um diretório novo ali e subir tudo de jQuery que a tela de login vai conseguir exibir sem problema.

      Vou colocar essa informação nos posts. Valeu de novo!

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to Top ↑