Endereços e CEPs de maneira simplificada – Parte Final

Tempo de Leitura: 7 Minutos

Para finalizar, vamos construir um Web Service que responda a nossa consulta devolvendo um JSON contendo de maneira literal todas as partes da nossa estrutura de CEP. Você pode adaptar esta consulta para a sua necessidade ou acrescentar recursos como autenticação para uso da API, mais não vai ser o nosso caso, que vai ser algo bem simples que foi o intuito original dessa estrutura.

Para que nossa estrutura funcione vamos chamar nosso serviço da seguinte maneira: https://www.servidor.com/ws/cep/00000-000 ou https://www.servidor.com/ws/cep/00000000 e a resposta vai ser um JSON contendo os dados da nossa base de dados. Para funcionar, é necessário que nosso servidor web permita a reescrita de url, isso pode ser feito em praticamente qualquer servidor web, mais como a maioria usa o Apache, vamos ao arquivo .htaccess correspondente:

Options +FollowSymLinks

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?route=$1 [NC,L]

A primeira linha, Options define que se existir algum arquivo simbólico, o apache siga o caminho e execute o arquivo. Na Segunda linha ligamos o motor de reescrita, e nas 2 próximas linhas testamos as condições para a regra de reescrita ser válida, para isso, usamos a variavel {REQUEST_FILENAME} que é o endereço requisitado pelo browser para o site, se ao verificar que ela é um arquivo, não satisfaz a condição e o URL não é reescrito, na próxima linha fazemos a mesma coisa e verificamos se é um diretório, se for não reescrevemos. Ou seja, se o arquivo ou diretório existirem usamos os conteúdos deles respectivamente.

Na ultima linha RewriteRule ^(.*)$ index.php?route=$1 [NC,L] é onde fazemos o roteamento de tudo o que não for localizado para nosso arquivo a sintaxe é RewriteRule Pattern Substitution Flags, onde o pattern é a expressão regular que vai criar a variável, Substitution é o que vai ser usado, e flags. No nosso caso pegamos todo o conteúdo da linha inteira, e direcionamos como texto para a variável route que será tratada no nosso script, e vai nos fornecer tudo o que foi passado para realizar-mos a consulta ao CEP. As FLAGS indicam NC que é para ignorar se é maiusculas ou minusculas, L que é para finalizar a reescrita não usando este endereço para novas regras caso o arquivo ainda tenha mais conteúdo, de outras regras.

Feito isso, vamos escrever nosso arquivo index.php que vai tratar e devolver todo o nosso conteúdo, ou o erro em caso de falha, veja como escrevemos ele, e depois comentamos todos os passos usados para ele:

<?php
// SERVERID = Endereço do Host de BANCO DE DADOS MYSQL onde temos nossas tabelas armezenadas
// DBUSER = Nome de usuário // DBPASSW = Senha do usuário
// DBNAME = Nome da base de dados onde se encontram nossas tabelas

$db1 = @mysqli_connect("SERVERID","DBUSER","DBPASSW");
if(!$db1) {
   $_MSG['erro_no']='1004';
   $_MSG['erro']='Falha na Conexão ao Banco de Dados';
   __out($_MSG,503); //503 - Service Unavaiable
}
//2? passo - Seleciona o Banco de Dados
if(!($cn=mysqli_select_db($db1, "DBNAME"))) {
   $_MSG['erro_no']='1005';
   $_MSG['erro']='Falha na Seleção do Banco de Dados';
   __out($_MSG,503); // 503 - Service Unavaiable
}

Nesta primeira etapa, simplesmente fazemos a conexão ao banco de dados e deixamos disponível a variável $db1 que é nosso ponteiro para essa conexão, e é utilizada para as funções de acesso ao banco de dados. Nossa variável $_MSG[] é o array que será exportado no corpo da resposta com o conteúdo da requisição, ou a mensagem de erro/sucesso. A função __out() que é a responsável pela saída do conteúdo ao browser do usuário, mais a frente veremos como a implementamos.

if(strtoupper($_SERVER['REQUEST_SCHEME'])!='HTTPS'){
    $_MSG['erro_no']='1001';
    $_MSG['erro']="HTTPS é obrigatório";
    __out($_MSG,403); // 403 - Forbidden - Acesso proibido se não for pelo HTTPS
}

if(strtoupper($_SERVER['REQUEST_METHOD'])!='GET'){
    $_MSG['erro_no']='1001'; 
    $_MSG['erro']="GET DEVE SER USADO"; 
    __out($_MSG,405); // 405 - Not Allowed - Acesso proibido se não for pelo METODO GET
}
$route = $_GET['route'];
$route = array_filter(explode('/',$route));
$cep=$route[0];

Nesta segunda parte do código pegamos o REQUEST_SCHEME e validamos se ele é HTTPS ou seja, só aceitamos requisições feitas com uso do protocolo SSL, na sequencia, verificamos o verbo HTTP (GET, POST, PUSH, PUT, ETC) se não for GET retornamos o estado de 405 método não permitido. Por fim, tratamos nossa variável route, que foi passada pelo apache com o conteúdo da requisição. Supondo-se que nosso script esteja dentro do diretório /ws/cep/index.php que também é o local onde teremos nosso .htaccess a requisição no browser deverá ser:
https://www.nossoservidor.com.br/ws/cep/01001-000 (o CEP 01001-000 pertence a Praça da Sé em São Paulo)

o conteúdo da variavel route neste exemplo será um array de 1 elemento, contendo uma string de 9 posições que equivale a nosso CEP, e ignoramos para este exemplo o resto, porém, poderiamos ter outros dados sendo passados dessa mesma maneira, e todos estariam neste mesmo array (em outro momento, vou falar mais sobre roteamento feito em PHP como usado nos frameworks comerciais), por hora, pegamos o conteúdo desse array e o copiamos para a variavel cep, para facilitar a didática da proxima parte do script.

$cep=filter_var($cep,FILTER_SANITIZE_STRING);
$re = '/^\d{5}-\d{3}$/m'; // Expressão Regular (REGEX) que determina a estrutura do cep, 00000-000 a 99999-999
if(!preg_match($re, $cep)){
    $_MSG['erro_no']='2001';
    $_MSG['erro']='CEP DEVE SER FORNECIDO COM - Ex: 01001-001';
    __out($_MSG,400); // 400 - Bad Request - Erro na Requisição
}

Nesta parte, passamos um filtro, caso a requisição tenha algum caractere de escape ou alguma tentativa de code injection ou sql injection será eliminada neste ponto, para garantir uma maior segurança do script, lembre-se que essa não deve ser a única maneira de garantir o uso, e abaixo falaremos mais alguns detalhes para colocar o script em um servidor de produção.

Por fim, vamos a consulta ao banco, que é a parte mais importante deste script:

if(!$rs = mysqli_query($db1, "SELECT l.logradouro_cep as cep, l.logradouro_tipo as tipo, l.logradouro_nome as logradouro, b.bairro_nome as bairro, c.cidade_nome as cidade, u.uf_sigla as uf, p.pais_nome as pais
   from logradouros as l, bairros as b, cidades as c, ufs as u, paises as p
   where l.bairro_id=b.bairro_id and b.cidade_id=c.cidade_id and c.uf_id=u.uf_id and u.pais_id=p.pais_id
   and l.logradouro_cep='$cep' limit 1")){
   //se houver erro
   $_MSG['erro_no']='1003';
   $_MSG['erro']='FALHA NA CHAMADA OU SERVIÇO INDISPONÍVEL';
   __out($_MSG,400); // 400 - Bad Request - Erro na chamada
}
// Caso tenha retornado um resultado, do banco de dados, montamos o JSON da resposta
if(mysqli_affected_rows($db1)==1){
   $rr=mysqli_fetch_assoc($rs);
   $_MSG['CEP']=$rr['cep'];
   $_MSG['TIPO']=$rr['tipo'];
   $_MSG['LOGRADOURO']=$rr['logradouro'];
   $_MSG['BAIRRO']=$rr['bairro'];
   $_MSG['CIDADE']=$rr['cidade'];
   $_MSG['UF']=$rr['uf'];
   $_MSG['PAIS']=$rr['pais'];
   __out($_MSG,200);  //200 - ok - resultado OK
} else {
// se não houve uma resposta completa, verificamos na tabela de CEP GERAL se existe o cep
   if(!$rs=mysqli_query($db1,"SELECT cep_id as cep, bairro, cidade, uf_sigla as uf, p.pais_nome as pais from ceps, ufs as u, paises as p where ceps.uf_id=u.uf_id and u.pais_id=p.pais_id and cep_id='$cep' limit 1")){
        $_MSG['erro_no']='1003';
        $_MSG['erro']='FALHA NA CHAMADA OU SERVIÇO INDISPONÍVEL';
        __out($_MSG,400); // 400 - Bad Request - Erro na chamada
    }
    if(mysqli_affected_rows($db1)==1){
        $rr=mysqli_fetch_assoc($rs);
        $_MSG['CEP']=$rr['cep'];
        $_MSG['TIPO']=' ';
        $_MSG['LOGRADOURO']=' ';
        $_MSG['BAIRRO']=' ';
        $_MSG['CIDADE']=$rr['cidade'];
        $_MSG['UF']=$rr['uf'];
        $_MSG['PAIS']=$rr['pais'];
        __out($_MSG,200); //200 - OK - Resultado
    } else {
      $_MSG['CEP']=$cep;
      $_MSG['TIPO']=' ';
      $_MSG['LOGRADOURO']=' ';
      $_MSG['BAIRRO']=' ';
      $_MSG['CIDADE']=' ';
      $_MSG['UF']=' ';
      $_MSG['PAIS']=' ';
      $_MSG['erro_no']='2000';
      $_MSG['erro']="CEP NÃO ENCONTRADO NO BANCO DE DADOS";
      __out($_MSG,206); //206 - OK, Conteúdo Parcial - Responde, porém sem dados
  }
}

Para finalizar, criamos a função __out(JSON, STATUS); que dá a saída e a resposta HTTP para o browser:

function __out($_msg, $excode=200){
   header("content-type: application/json;charset=UTF-8");
   header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}");
   header('Access-Control-Allow-Credentials: true');
   header('Access-Control-Max-Age: 86400');
   http_response_code($excode);
   $_msg = array_map("utf8_encode", $_msg);
   echo json_encode($_msg);
   exit($excode);
}
?>

Para esta função, primeiro mandamos uma sequencia de HEADERS HTTP onde, o content-type informa que é um JSON codificado em UTF-8, que o controle de acesso foi liberado para o HOST/IP de origem da requisição (aceito), na sequencia, o retorno de CREDENTIALS como TRUE ou seja, as credenciais ou atenticação foram permitidas, por fim, a vida útil dessa requisição em caso de CACHE ou CDN ou algum outro cache intermediário como 86400 segundos, ou seja, 1 dia, caso exista uma nova chamada a esse mesmo ponto, nosso load-balancer ou cache ou proxy armazena a resposta por 1 dia, e usa esse dado, não gerando uma nova requisição a API. O Ultimo header é gerado por http_response_code($excode); e determina se o STATUS da requisição HTTP é passado ou é 200 de OK.

Depois dos HEADERS definidos, codificamos todas as strings da ARRAY em UTF-8 pelo map, ecoamos o resultado da função JSON_ENCODE() para a saída e fechamos retornando um status 200, de OK, ou caso tenha sido passado outro estado, retornamos este estado.

Veja nesta tela do POSTMAN, uma resposta com o CEP do exemplo acima, e a resposta, abaixo, uma resposta de uma chamada sem o HTTPS, com a mensagem de erro:

Com este artigo, finalizamos a criação de um web service de consulta, e com essa base podemos implementar outros recursos como autenticação, contagem de uso por usuário, por ip, poderíamos registrar um limite de uso por um determinado tempo, além de outras checagens ou validações, a implementação de um método PUT para inserir novos CEPS, enfim, poderia expandir essa mesma API com recursos como devolver os DDDs válidos para aquela faixa de cep, os códigos e outros dados de pais, ou de estado, e mesmo criar um mecanismo que salve um histórico de consultas e o estado, para futuramente atualizar a base de CEP com base no aprendizado, entre muitos outros recursos.

No próximo artigo, vamos falar sobre o uso de autenticação básica via banco de dados. Se gostou do artigo, compartilhe e ajude-nos a crescer cada vez mais. Se tiver alguma sugestão, deixe seu comentário.

CONHEÇA NOSSA API PÚBLICA DE CONSULTA DE CEP

 

ADQUIRA O BANCO DE DADOS DE CEP NACIONAL (DNE)
PARA USO NOS SEUS SISTEMAS E APLICATIVOS