Automação para os mais pequenos. Notas. API RESTful

Este artigo é uma das notas curtas prometidas durante a série de artigos Automation For The Menorest .
Como a principal maneira de interagir com o sistema IPAM é a API RESTful, decidi falar sobre isso separadamente.



Elogio os arquitetos do mundo moderno - temos interfaces padronizadas. Sim, existem muitos deles - isso é um sinal de menos, mas eles são - isso é uma vantagem.

Essas interfaces ganharam o nome API - Application Programming Interface.

Uma dessas interfaces é a API RESTful, usada para trabalhar com o NetBox.



Se for muito simples, a API fornece ao cliente um conjunto de ferramentas por meio das quais ele pode gerenciar o servidor. E o cliente pode ser essencialmente qualquer coisa: um navegador da Web, um console de comando, um aplicativo desenvolvido pelo fabricante ou qualquer outro aplicativo que tenha acesso à API.

Por exemplo, no caso da NetBox, você pode adicionar um novo dispositivo a ele das seguintes maneiras: por meio de um navegador da Web, enviando uma solicitação para o console, use o Postman, acesse a biblioteca de solicitações em python, use o pynetbox SDK ou vá para o Swagger.

Assim, depois de escrever uma única interface, o fabricante se livra para sempre da necessidade de concordar com cada novo cliente como conectá-la (embora isso seja apenas um pouco astuto).

Conteúdo


  • REST, RESTful, API
  • Estrutura de mensagens HTTP
    • Linha de partida
    • Cabeçalhos
    • O corpo da mensagem HTTP

  • Métodos
    • HTTP GET
    • HTTP POST
    • HTTP PUT
    • PATCH HTTP
    • HTTP DELETE

  • Maneiras de trabalhar com a API RESTful
    • Enrolar
    • Carteiro
    • Solicitações Python +
    • Pynebtbox SDK
    • Swagger

  • Críticas ao REST e alternativas
  • Links úteis

REST, RESTful, API


Abaixo, darei uma descrição muito simplificada do que é REST.

Para começar, a API RESTful é exatamente a interface de interação baseada em REST, enquanto a própria REST ( REpresentational State Transfer ) é um conjunto de restrições usadas para criar serviços WEB.

É possível ler sobre exatamente quais limitações podem ser encontradas no capítulo 5 da dissertação de Roy Fielding, Estilos arquitetônicos e Design de arquiteturas de software baseadas em rede . Deixe-me dar apenas os três mais significativos (do meu ponto de vista) deles:

  1. A arquitetura REST usa o modelo de interação cliente-servidor.
  2. Cada solicitação REST contém todas as informações necessárias para sua execução. Ou seja, o servidor não deve se lembrar de nada sobre solicitações de clientes anteriores, que, como você sabe, são caracterizadas pela palavra Stateless - não armazenam informações de status.
  3. Interface unificada. A implementação do aplicativo é separada do serviço que ele fornece. Ou seja, o usuário sabe o que faz e como interagir com ele, mas como faz isso não importa. Quando você altera o aplicativo, a interface permanece a mesma e os clientes não precisam se ajustar.

Os serviços WEB que atendem a todos os princípios do REST são chamados de serviços WEB RESTful .

E a API que fornece serviços RESTful WEB é chamada API RESTful.

O REST não é um protocolo, mas o chamado estilo de arquitetura (um dos). Desenvolvido com HTTP por Roy Fielding, o REST pretendia usar o HTTP 1.1 como transporte.

Endereço de destino (ou em outras palavras - um objeto ou de outra maneira - um ponto final) - esse é o nosso URI usual.

O formato dos dados transmitidos é XML ou JSON .
Para esta série de artigos sobre o linkmeup, foi implantada uma implantação somente leitura (para vocês, queridos leitores) Instalação do NetBox : netbox.linkmeup.ru : 45127.

Os direitos de leitura não são necessários, mas se você quiser tentar ler com um token, poderá usar este: API de Token: 90a22967d0bc4bdcd8ca47ec490bbf0b0cb2d9c8 .
Vamos fazer um pedido de diversão:

curl -X GET -H "Authorization: TOKEN 90a22967d0bc4bdcd8ca47ec490bbf0b0cb2d9c8" \ -H "Accept: application/json; indent=4" \ http://netbox.linkmeup.ru:45127/api/dcim/devices/1/ 

Ou seja, com o utilitário curl , criamos um objeto GET em netbox.linkmeup.ru : 45127 / api / dcim / devices / 1 / com uma resposta no formato JSON e um recuo de 4 espaços.

Ou um pouco mais academicamente: GET retorna o objeto de dispositivos digitados, que é um parâmetro do objeto DCIM .

Você também pode atender a essa solicitação - basta copiá-la para o seu terminal.
O URL ao qual estamos nos referindo na solicitação é chamado de Terminal . De certa forma, este é o objeto final com o qual interagiremos.
Por exemplo, no caso de netbox, uma lista de todos os pontos de extremidade pode ser obtida aqui .
E preste atenção ao sinal / no final da URL - é necessário .
Aqui está o que obtemos em resposta:

 { "id": 1, "name": "mlg-host-0", "display_name": "mlg-host-0", "device_type": { "id": 4, "url": "http://netbox.linkmeup.ru/api/dcim/device-types/4/", "manufacturer": { "id": 4, "url": "http://netbox.linkmeup.ru/api/dcim/manufacturers/4/", "name": "Hypermacro", "slug": "hypermacro" }, "model": "Server", "slug": "server", "display_name": "Hypermacro Server" }, "device_role": { "id": 1, "url": "http://netbox.linkmeup.ru/api/dcim/device-roles/1/", "name": "Server", "slug": "server" }, "tenant": null, "platform": null, "serial": "", "asset_tag": null, "site": { "id": 6, "url": "http://netbox.linkmeup.ru/api/dcim/sites/6/", "name": "", "slug": "mlg" }, "rack": { "id": 1, "url": "http://netbox.linkmeup.ru/api/dcim/racks/1/", "name": "A", "display_name": "A" }, "position": 41, "face": { "value": "front", "label": "Front", "id": 0 }, "parent_device": null, "status": { "value": "active", "label": "Active", "id": 1 }, "primary_ip": null, "primary_ip4": null, "primary_ip6": null, "cluster": null, "virtual_chassis": null, "vc_position": null, "vc_priority": null, "comments": "", "local_context_data": null, "tags": [], "custom_fields": {}, "config_context": {}, "created": "2020-01-14", "last_updated": "2020-01-14T18:39:01.288081Z" } 

Este é JSON (como solicitamos), descrevendo o dispositivo com o ID 1: como é chamado, com qual função, qual modelo, onde está, etc.

Isso parecerá uma solicitação HTTP:

  GET /api/dcim/devices/1/ HTTP/1.1 Host: netbox.linkmeup.ru:45127 User-Agent: curl/7.54.0 Accept: application/json; indent=4 


Portanto, a resposta será:
  HTTP/1.1 200 OK Server: nginx/1.14.0 (Ubuntu) Date: Tue, 21 Jan 2020 15:14:22 GMT Content-Type: application/json Content-Length: 1638 Connection: keep-alive Data 

Despejar a transação .

Agora vamos descobrir o que fizemos.



Estrutura de mensagens HTTP


Uma mensagem HTTP consiste em três partes, somente a primeira delas é necessária.

  • Linha de partida
  • Cabeçalhos
  • Corpo da mensagem

Linha de partida


As linhas de início da solicitação e resposta HTTP parecem diferentes.

Solicitação HTTP


 METHOD URI HTTP_VERSION 

O método determina qual ação o cliente deseja executar: receber dados, criar um objeto, atualizá-lo, excluí-lo.
URI - o identificador do recurso em que o cliente acessa ou, em outras palavras, o caminho para o recurso / documento.
HTTP_VERSION é a versão HTTP, respectivamente. Hoje, para o REST, é sempre 1.1.
Um exemplo:

 GET /api/dcim/devices/1/ HTTP/1.1 


Resposta HTTP


 HTTP_VERSION STATUS_CODE REASON_PHRASE 

HTTP_VERSION - versão HTTP (1.1).
STATUS_CODE - três dígitos do código de status (200, 404, 502, etc.)
REASON_PHRASE - Explicação (OK, NÃO ENCONTRADO, MATE GATEWAY, etc.)

Um exemplo:

 HTTP/1.1 200 OK 

Cabeçalhos


Os cabeçalhos passam os parâmetros para esta transação HTTP.

Por exemplo, no exemplo acima na solicitação HTTP, eles foram:

  Host: netbox.linkmeup.ru:45127 User-Agent: curl/7.54.0 Accept: application/json; indent=4 

Eles indicam que

  1. Nos voltamos para o host netbox.linkmeup.ru:45127 ,
  2. A solicitação foi gerada em curl ,
  3. E aceitamos dados no formato JSON com recuo de 4 .

E aqui estão os cabeçalhos da resposta HTTP:

  Server: nginx/1.14.0 (Ubuntu) Date: Tue, 21 Jan 2020 15:14:22 GMT Content-Type: application/json Content-Length: 1638 Connection: keep-alive 

Eles indicam que

  1. Tipo de servidor: nginx no Ubuntu ,
  2. Tempo de resposta
  3. Formato dos dados de resposta: JSON
  4. Comprimento dos dados de resposta: 1638 bytes
  5. A conexão não precisa ser fechada - ainda haverá dados.

Os cabeçalhos, como você já notou, se parecem com pares nome: valor, separados por um sinal ":".

Uma lista completa de possíveis cabeçalhos .

O corpo da mensagem HTTP


O corpo é usado para transferir os dados reais.

Na resposta HTTP, isso pode ser uma página HTML ou, no nosso caso, um objeto JSON.

Deve haver pelo menos uma linha em branco entre os cabeçalhos e o corpo.

Ao usar o método GET em uma solicitação HTTP, geralmente não há corpo porque não há nada a transmitir. Mas o corpo está na resposta HTTP.

Mas, por exemplo, com o POST, o corpo já estará na solicitação. Vamos falar sobre os métodos e conversar agora.



Métodos


Como você já entendeu, o HTTP usa métodos para trabalhar com serviços WEB. O mesmo vale para a API RESTful.

Os cenários possíveis da vida real são descritos pelo termo CRUD - Criar, Ler, Atualizar, Excluir .
Aqui está uma lista dos métodos HTTP mais populares que implementam CRUD:

  • HTTP GET
  • HTTP POST
  • HTTP PUT
  • HTTP DELETE
  • PATCH HTTP

Os métodos também são chamados de verbos , porque indicam qual ação é executada.

Lista completa de métodos .

Vejamos cada um deles usando o exemplo NetBox.

HTTP GET


Este é um método para obter informações.

Por exemplo, retiramos a lista de dispositivos:

 curl -H "Accept: application/json; indent=4" \ http://netbox.linkmeup.ru:45127/api/dcim/devices/ 

O método GET é seguro porque não altera os dados, mas apenas pede.

É idempotente do ponto de vista que a mesma consulta sempre retorne o mesmo resultado (até que os dados em si sejam alterados).

No GET, o servidor retorna uma mensagem com o código HTTP e o corpo da resposta ( código de resposta e corpo da resposta ).

Ou seja, se tudo estiver OK, o código de resposta será 200 (OK).
Se o URL não for encontrado, 404 (NÃO ENCONTRADO).
Se algo estiver errado com o servidor ou os componentes, ele pode ser 500 (ERRO DE SERVIDOR) ou 502 (BAD GATEWAY).
Todos os códigos de resposta possíveis .

O corpo é retornado no formato JSON ou XML.

Despejar a transação .

Vamos dar mais alguns exemplos. Agora, solicitaremos informações sobre um dispositivo específico por seu nome.

 curl -X GET -H "Accept: application/json; indent=4" \ "http://netbox.linkmeup.ru:45127/api/dcim/devices/?name=mlg-leaf-0" 

Aqui, para definir as condições de pesquisa no URI, também especifiquei o atributo do objeto (o parâmetro name e seu valor mlg-leaf-0 ). Como você pode ver, antes da condição e após a barra, existe um "?" , e o nome e o valor são separados por um sinal "=" .

É assim que a solicitação se parece.

  GET /api/dcim/devices/?name=mlg-leaf-0 HTTP/1.1 Host: netbox.linkmeup.ru:45127 User-Agent: curl/7.54.0 Accept: application/json; indent=4 

Despejar a transação .

Se você precisar especificar algumas condições, a consulta terá a seguinte aparência:

 curl -X GET -H "Accept: application/json; indent=4" \ "http://netbox.linkmeup.ru:45127/api/dcim/devices/?role=leaf&site=mlg" 

Aqui solicitamos todos os dispositivos em folha localizados no site da mlg .
Ou seja, as duas condições são separadas uma da outra pelo sinal "&" .

Despejar a transação .

Do curioso e agradável - se através de "&" você define duas condições com o mesmo nome, então entre elas não haverá realmente um "AND" lógico, mas um "OR" lógico.

Ou seja, essa consulta realmente retornará dois objetos: mlg-leaf-0 e mlg-spine-0

 curl -X GET -H "Accept: application/json; indent=4" \ "http://netbox.linkmeup.ru:45127/api/dcim/devices/?name=mlg-leaf-0&name=mlg-spine-0" 

Despejar a transação .

Vamos tentar acessar um URL inexistente.

 curl -X GET -H "Accept: application/json; indent=4" \ "http://netbox.linkmeup.ru:45127/api/dcim/IDGAF/" 

Despejar a transação .



HTTP POST


POST é usado para criar um novo objeto em uma coleção de objetos. Ou em uma linguagem mais complexa: para criar um novo recurso subordinado.

Esclarecimentos da arthuriantech :
Incluindo, mas não limitado a. O método POST foi projetado para transferir dados para o servidor para processamento adicional - é usado para todas as ações que não precisam ser padronizadas no HTTP. Antes da RFC 5789, era a única maneira legal de fazer alterações parciais.
roy.gbiv.com/untangled/2009/it-is-okay-to-use-post
tools.ietf.org/html/rfc7231#section-4.3.3


Ou seja, se houver um conjunto de dispositivos, o POST permitirá criar um novo objeto de dispositivo dentro dos dispositivos.

Selecione o mesmo ponto de extremidade e use o POST para criar um novo dispositivo.

 curl -X POST "http://netbox.linkmeup.ru:45127/api/dcim/devices/" \ -H "accept: application/json"\ -H "Content-Type: application/json" \ -H "Authorization: TOKEN a9aae70d65c928a554f9a038b9d4703a1583594f" \ -d "{ \"name\": \"just a simple russian girl\", \"device_type\": 1, \"device_role\": 1, \"site\": 3, \"rack\": 3, \"position\": 5, \"face\": \"front\"}" 

Um cabeçalho de autorização já aparece aqui, contendo um token que autoriza a solicitação de gravação e, após a diretiva -d, existe JSON com os parâmetros do dispositivo criado:

 { "name": "just a simple russian girl", "device_type": 1, "device_role": 1, "site": 3, "rack": 3, "position": 5, "face": "front"} 

Sua solicitação não funcionará, porque o token não é mais válido - não tente gravar no NetBox.
A resposta vem com uma resposta HTTP com o código 201 (CREATED) e JSON no corpo da mensagem, em que o servidor retorna todos os parâmetros sobre o dispositivo criado.

  HTTP/1.1 201 Created Server: nginx/1.14.0 (Ubuntu) Date: Sat, 18 Jan 2020 11:00:22 GMT Content-Type: application/json Content-Length: 1123 Connection: keep-alive JSON 

Despejar a transação .

Agora, com uma nova solicitação com o método GET, você pode vê-lo na saída:

 curl -X GET -H "Accept: application/json; indent=4" \ "http://netbox.linkmeup.ru:45127/api/dcim/devices/?q=russian" 

"Q" no NetBox permite encontrar todos os objetos que contêm em seu nome uma linha que vai além.
O POST, obviamente, não é seguro nem idempotente - provavelmente altera os dados, e uma solicitação executada duas vezes levará à criação do segundo mesmo objeto ou a um erro.



HTTP PUT


Este é um método para modificar um objeto existente. O terminal para PUT parece diferente do POST - agora ele contém um objeto específico.

PUT pode retornar os códigos 201 ou 200.

Um ponto importante com esse método: você deve passar todos os atributos necessários, pois PUT substitui o objeto antigo.

Portanto, se, por exemplo, apenas tentar adicionar o atributo asset_tag ao nosso novo dispositivo, receberemos um erro:

 curl -X PUT "http://netbox.linkmeup.ru:45127/api/dcim/devices/18/" \ -H "accept: application/json" \ -H "Content-Type: application/json" \ -H "Authorization: TOKEN a9aae70d65c928a554f9a038b9d4703a1583594f" \ -d "{ \"asset_tag\": \"12345678\"}" 

 {"device_type":["This field is required."],"device_role":["This field is required."],"site":["This field is required."]} 

Mas se você adicionar os campos ausentes, tudo funcionará:

 curl -X PUT "http://netbox.linkmeup.ru:45127/api/dcim/devices/18/" \ -H "accept: application/json" \ -H "Content-Type: application/json" \ -H "Authorization: TOKEN a9aae70d65c928a554f9a038b9d4703a1583594f" \ -d "{ \"name\": \"just a simple russian girl\", \"device_type\": 1, \"device_role\": 1, \"site\": 3, \"rack\": 3, \"position\": 5, \"face\": \"front\", \"asset_tag\": \"12345678\"}" 

Despejar a transação .
Preste atenção ao URL aqui - agora ele inclui o ID do dispositivo que queremos alterar ( 18 ).



PATCH HTTP


Este método é usado para modificar parcialmente o recurso.
Wat? Você pergunta, mas e o PUT?

PUT é um método que existia originalmente no padrão, envolvendo a substituição completa de um objeto mutável. Assim, no método PUT, como escrevi acima, você precisará especificar até mesmo os atributos do objeto que não são alterados.

E PATCH foi adicionado posteriormente e permite especificar apenas os atributos que realmente mudam.

Por exemplo:

 curl -X PATCH "http://netbox.linkmeup.ru:45127/api/dcim/devices/18/" \ -H "accept: application/json" \ -H "Content-Type: application/json" \ -H "Authorization: TOKEN a9aae70d65c928a554f9a038b9d4703a1583594f" \ -d "{ \"serial\": \"BREAKINGBAD\"}" 

Aqui, o ID do dispositivo também é especificado no URL, mas há apenas um atributo serial para alterar.

Despejar a transação .



HTTP DELETE


Obviamente, exclui o objeto.

Um exemplo

 curl -X DELETE "http://netbox.linkmeup.ru:45127/api/dcim/devices/21/" \ -H "accept: application/json" \ -H "Content-Type: application/json" \ -H "Authorization: TOKEN a9aae70d65c928a554f9a038b9d4703a1583594f" 

O método DELETE é idempotente do ponto de vista de que uma consulta repetida não altera mais nada na lista de recursos (mas retornará o código 404 (NOT FOUND).

 curl -X DELETE "http://netbox.linkmeup.ru:45127/api/dcim/devices/21/" \ -H "accept: application/json" \ -H "Content-Type: application/json" \ -H "Authorization: TOKEN a9aae70d65c928a554f9a038b9d4703a1583594f" 

 {"detail":"Not found."} 



Maneiras de trabalhar com a API RESTful


Naturalmente, o Curl é muito conveniente para os valentes guerreiros da CLI, mas existem ferramentas melhores.

Carteiro


O Postman permite formar consultas na interface gráfica, selecionando métodos, cabeçalhos, corpo e exibe o resultado em um formato legível por humanos.

Além disso, as consultas e os URIs podem ser salvos e retornados a eles posteriormente.

Faça o download do Postman no site oficial .

Para que possamos fazer um GET:


Aqui, o token é indicado no GET apenas como exemplo.

E então POST:



O Postman deve ser usado apenas com a API RESTful.
Por exemplo, não tente enviar o NETCONF XML através dele, como fiz no início da minha carreira em automação.
Um dos bônus interessantes da API especificada é que você pode importar todos os pontos de extremidade e seus métodos para o Postman como uma coleção.

Para fazer isso, na janela Importar (Arquivo-> Importar), selecione Importar do link e cole na janela netbox.linkmeup.ru URL: 45127 / api / docs /? Format = openapi.



Além disso, tudo o que você pode encontrar nas coleções.






Solicitações Python +


Mas mesmo através do Postman, você provavelmente não estará gerenciando seus sistemas de produção. Certamente, você terá aplicativos externos que desejam interagir com eles sem a sua participação.

Por exemplo, um sistema de geração de configuração deseja pegar uma lista de interfaces IP da NetBox.
O Python possui uma maravilhosa biblioteca de solicitações que implementa o trabalho por meio de HTTP.
Um exemplo de solicitação de uma lista de todos os dispositivos:

 import requests HEADERS = {'Content-Type': 'application/json', 'Accept': 'application/json'} NB_URL = "http://netbox.linkmeup.ru:45127" request_url = f"{NB_URL}/api/dcim/devices/" devices = requests.get(request_url, headers = HEADERS) print(devices.json()) 

Adicione um novo dispositivo novamente:

 import requests API_TOKEN = "a9aae70d65c928a554f9a038b9d4703a1583594f" HEADERS = {'Authorization': f'Token {API_TOKEN}', 'Content-Type': 'application/json', 'Accept': 'application/json'} NB_URL = "http://netbox.linkmeup.ru:45127" request_url = f"{NB_URL}/api/dcim/devices/" device_parameters = { "name": "just a simple REQUESTS girl", "device_type": 1, "device_role": 1, "site": 3, } new_device = requests.post(request_url, headers = HEADERS, json=device_parameters) print(new_device.json()) 



Python + NetBox SDK


No caso do NetBox, também existe um Python SDK - Pynetbox , que representa todos os pontos de extremidade do NetBox como um objeto e seus atributos, fazendo todo o trabalho sujo para gerar URIs e analisar a resposta, embora não de graça, é claro.

Por exemplo, vamos fazer o mesmo que acima, usando o pynetbox.
Lista de todos os dispositivos:

 import pynetbox NB_URL = "http://netbox.linkmeup.ru:45127" nb = pynetbox.api(NB_URL) devices = nb.dcim.devices.all() print(devices) 

Adicionar novo dispositivo:

 import pynetbox API_TOKEN = "a9aae70d65c928a554f9a038b9d4703a1583594f" NB_URL = "http://netbox.linkmeup.ru:45127" nb = pynetbox.api(NB_URL, token = API_TOKEN) device_parameters = { "name": "just a simple PYNETBOX girl", "device_type": 1, "device_role": 1, "site": 3, } new_device = nb.dcim.devices.create(**device_parameters) print(new_device) 

Documentação do Pynetbox



Swagger


O que mais vale a pena graças à década passada são as especificações da API. Se você seguir esse caminho , será direcionado para a documentação da API Swagger UI - Netbox.



Esta página lista todos os pontos de extremidade, métodos de trabalho com eles, possíveis parâmetros e atributos e indica quais deles são necessários. Além disso, as respostas esperadas são descritas.



Na mesma página, você pode executar consultas interativas clicando em Experimente .
Por alguma razão, o swagger usa o nome do servidor sem porta como o URL base, portanto, a função Experimentar não funciona nos meus exemplos do Swagger. Mas você pode experimentá-lo em sua própria instalação.
Quando você clica em Executar Swagger, a interface do usuário gera uma sequência de curvatura que pode ser usada para fazer uma solicitação semelhante na linha de comando.

Na interface do usuário do Swagger, você pode até criar um objeto:



Para fazer isso, basta ser um usuário autorizado com os direitos necessários.

O que vemos nesta página é a interface do usuário do Swagger, uma documentação gerada com base na especificação da API.

Com as tendências na arquitetura de microsserviço, está se tornando cada vez mais importante ter uma API padronizada para interação entre componentes, cujos pontos de extremidade e métodos são fáceis de determinar para a pessoa e o aplicativo sem vasculhar o código-fonte ou a documentação em PDF.

Portanto, os desenvolvedores hoje estão cada vez mais seguindo o paradigma da API First quando pensam pela primeira vez na API e somente então na implementação.
A API é especificada primeiro neste design e, em seguida, a documentação, o aplicativo cliente, a parte do servidor são gerados a partir dele e são necessários testes.

O Swagger é uma linguagem de estrutura e especificação (que agora foi renomeada para OpenAPI 2.0), permitindo implementar esta tarefa.
Eu não vou me aprofundar nisso.

Para mais detalhes aqui:




Críticas ao REST e alternativas


Existe um sim. Nem tudo nesse mundo do ano 2000 já é tão otimista.

Não sendo um especialista, não pretendo divulgar substancialmente a questão, mas darei um link para um artigo incontestável sobre Habré .

Uma interface alternativa para a interação dos componentes do sistema hoje é o gRPC. Ele também profetizou um grande futuro no campo de novas abordagens para trabalhar com equipamentos de rede. Mas falaremos sobre ele em algum momento no futuro, quando chegar a sua vez.

Você também pode dar uma olhada no GraphQL promissor, mas, novamente, não precisamos trabalhar com ele por enquanto, então ele permanece para estudo independente.



É importante
O token a9aae70d65c928a554f9a038b9d4703a1583594f foi usado apenas para fins de demonstração e não funciona mais.

A indicação direta de tokens no código do programa é inaceitável e eu fiz aqui apenas no interesse de simplificar os exemplos.



Links úteis



Obrigado


  • Andrei Panfilov pela revisão e edição
  • Alexander Fatin para revisão e edição

Source: https://habr.com/ru/post/pt485618/


All Articles