miércoles, 26 de octubre de 2016

Integración Java+Azure utilizando REST

Cuando escuchamos de Azure, inmediatamente se nos viene a la mente el SDK de esta interesante plataforma de Microsoft, sin embargo algunos desarrolladores preferimos invocar a los servicios de Azure directamente desde  su API REST, sin utilizar bibliotecas específicas. En este artículo encontrarás un ejemplo completo de cómo comunicarnos con Azure Blob Storage.


A continuación, se muestra la clase java que se encarga de realizar la comunicación:
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package util;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.TimeZone;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;

/**
 *
 * @author Rolando
 */
public class Util {

    private static Base64 base64 = new Base64();

    public static void signRequestSK(HttpURLConnection request, String account, String key) throws Exception {
        SimpleDateFormat fmt = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss");
        fmt.setTimeZone(TimeZone.getTimeZone("GMT"));
        String date = fmt.format(Calendar.getInstance().getTime()) + " GMT";

        StringBuilder sb = new StringBuilder();
        sb.append("GET\n"); // method
        sb.append("\n"); // content encoding
        sb.append("\n"); // content language
        sb.append("\n"); // content length
        sb.append("\n"); // md5 (optional)
        sb.append("\n"); // content type
        sb.append("\n"); // legacy date
        sb.append("\n"); // if-modified-since
        sb.append("\n"); // if-match
        sb.append("\n"); // if-none-match
        sb.append("\n"); // if-unmodified-since
        sb.append("\n"); // range
        sb.append("x-ms-date:" + date + "\n"); // headers
        sb.append("x-ms-version:2015-12-11\n");
        sb.append("/" + account + request.getURL().getPath() + "\ncomp:list");
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(new SecretKeySpec(base64.decode(key), "HmacSHA256"));
        String authKey = new String(base64.encode(mac.doFinal(sb.toString().getBytes("UTF-8"))));
        String auth = "SharedKey " + account + ":" + authKey;
        request.setRequestProperty("x-ms-date", date);
        request.setRequestProperty("x-ms-version", "2015-12-11");
        request.setRequestProperty("Authorization", auth);
        request.setRequestProperty("Accept-Charset", "UTF-8");
        request.setRequestProperty("Accept", "application/atom+xml,application/xml");
        request.setRequestMethod("GET");
    }

    public static void main(String args[]) throws Exception {
        //To use fiddler
        System.setProperty("http.proxyHost", "127.0.0.1");
        System.setProperty("https.proxyHost", "127.0.0.1");
        System.setProperty("http.proxyPort", "8888");
        System.setProperty("https.proxyPort", "8888");
        //--
        //Inputs
        String account = "<Nombre_Cuenta>";
        String key = "<Access_Key>";
        //--
        HttpURLConnection connection = (HttpURLConnection) (new URL("http://" + account + ".blob.core.windows.net/?comp=list")).openConnection();
        signRequestSK(connection, account, key);
        connection.connect();
        StringBuffer text = new StringBuffer();
        InputStreamReader in = new InputStreamReader((InputStream) connection.getContent());
        BufferedReader buff = new BufferedReader(in);
        String line;
        do {
            line = buff.readLine();
            text.append(line + "\n");
        } while (line != null);
        System.out.println(text);
    }
}
Entonces, si utilizamos Fiddler, podremos ver que este sería el Request:
GET http://"<Nombre_Cuenta>".blob.core.windows.net/?comp=list HTTP/1.1
x-ms-date: Wed, 26 Oct 2016 18:35:49 GMT
x-ms-version: 2015-12-11
Authorization: SharedKey "<Nombre_Cuenta>":psyT2FlwpU3BU7/kKqtfi+ZBTuUpEX8bnsZ45O/1rUk=
Accept-Charset: UTF-8
Accept: application/atom+xml,application/xml
User-Agent: Java/1.8.0_92
Host: "<Nombre_Cuenta>".blob.core.windows.net
Connection: keep-alive
Y el response quedaría de la siguiente manera:
<?xml version="1.0" encoding="UTF-8" ?>
<EnumerationResults ServiceEndpoint="http://"<Nombre_Cuenta>".blob.core.windows.net/">
    <Containers>
        <Container>
            <Name>sas</Name>
            <Properties>
                <Last-Modified>Tue, 25 Oct 2016 19:19:46 GMT</Last-Modified>
                <Etag>"0x8D3FD0BE1F7E8D0"</Etag>
                <LeaseStatus>unlocked</LeaseStatus>
                <LeaseState>available</LeaseState>
            </Properties>
        </Container>
    </Containers>
    <NextMarker />
</EnumerationResults>

viernes, 21 de octubre de 2016

Servicios Web REST y PHP

En este artículo les mostraré cómo implementar una aplicación CRUD con PHP y servicios Web RESTFul. Los requisitos son los siguientes:
  • PHP 4.0 o superior
  • MySQL 4.0 o superior
  • Integración PHP+MySQL realizada correctamente.

Y la estructura de archivos es como se muestra a continuación:

rest_api
|-- dao.php
|-- index.php
|-- model.php
|-- service.php


Como primer paso, crear una base de datos y crear la tabla test con la estructura que se muestra a continuación.


Crear el archivo model.php, en donde se especificará una "clase entidad" que corresponderá con la tabla test de nuestra base de datos [Lo sé, no es java =(]. El hecho de no utilizar el concepto de encapsulación es debido a la versión de PHP que se utilizó para este post (4.0).
<?php

class Test {

    var $id;
    var $nombres;
    var $apellidos;

}
También tendremos una clase PHP exclusivamente para el acceso a base de datos. En el archivo dao.php, se implementarán todas las operaciones sobre base de datos.
<?php

class DAO {

    var $server;
    var $db;
    var $dblog;
    var $dbpass;

    function DAO() {
        $this->server = 'localhost';
        $this->db = 'test';
        $this->dblog = 'root';
        $this->dbpass = '123456';
    }

    function connect() {
        $this->cnn = new MySQL(array('host' => $this->server, 'user' => $this->dblog, 'password' => $this->dbpass, 'database' => $this->db));
    }

    function insert($test) {
        $this->connect();
        $sql = "INSERT INTO test(nombres, apellidos) VALUES ('" . $test->nombres . "','" . $test->apellidos . "'" . ")";
        $rs = $this->cnn->query($sql);
    }

    function findAll() {
        $this->connect();
        $sql = "SELECT * FROM test";
        $result = $this->cnn->query($sql);
        $rs = $result->fetchall(MYSQL_ASSOC);
        return $rs;
    }

    function find($id) {
        $this->connect();
        $sql = "SELECT * FROM test WHERE id=" . $id;
        $result = $this->cnn->query($sql);
        $rs = $result->fetchRow(MYSQL_ASSOC);
        return $rs;
    }

    function update($test) {
        $this->connect();
        $sql = "UPDATE test SET nombres ='" . $test->nombres . "', apellidos='" . $test->apellidos . "' WHERE id='" . $test->id . "'";
        $this->cnn->query($sql);
    }

    function delete($id) {
        $this->connect();
        $sql = "DELETE FROM test WHERE id='" . $id . "'";
        $this->cnn->query($sql);
    }

}
Las funciones de la clase DAO serán invocadas a través de una capa de servicio, la cual se encargará de procesar las peticiones y determinar qué tipo de servicio REST es el consultado. El archivo tendrá el nombre service.php.
<?php

require_once("dao.php");
require_once("model.php");

$method = $_SERVER['REQUEST_METHOD'];

$dao = new DAO();
$json_service = new Services_JSON();

//call DAO based on HTTP method
switch ($method) {
    case 'GET':
        $result = $dao->findAll();
        $json_response = $json_service->encode($result);
        echo $json_response;
        break;
    case 'POST':
        $test = new Test();
        $test->nombres = $_POST['nombres'];
        $test->apellidos = $_POST['apellidos'];
        $dao->insert($test);
        break;
    case 'PUT':
        parse_str(file_get_contents("php://input"), $post_vars);
        $test = new Test();
        $test->id = $post_vars['id'];
        $test->nombres = $post_vars['nombres'];
        $test->apellidos = $post_vars['apellidos'];
        $dao->update($test);
        break;
    case 'DELETE':
        parse_str(file_get_contents("php://input"), $post_vars);
        $id = $post_vars['id'];
        $dao->delete($id);
        break;
}
Finalmente, escribimos la parte de código correspondiente al lado del cliente en el archivo index.php. A través de una URL, accedemos a los servicios publicados y utilizamos sus funcionalidades dependiendo de la manera en cómo los invoquemos. (GET/POST/PUT/DELETE).
<?php

$url = "http://localhost/res_api/service.php";

if (!function_exists('http_build_query')) {

    function http_build_query($data, $prefix = null, $sep = '', $key = '') {
        $ret = array();
        foreach ((array) $data as $k => $v) {
            $k = urlencode($k);
            if (is_int($k) && $prefix != null) {
                $k = $prefix . $k;
            }

            if (!empty($key)) {
                $k = $key . "[" . $k . "]";
            }

            if (is_array($v) || is_object($v)) {
                array_push($ret, http_build_query($v, "", $sep, $k));
            } else {
                array_push($ret, $k . "=" . urlencode($v));
            }
        }

        if (empty($sep)) {
            $sep = ini_get("arg_separator.output");
        }

        return implode($sep, $ret);
    }

}

/* GET Client */
$client = curl_init($url);
curl_setopt($client, CURLOPT_RETURNTRANSFER, 1);
$curl_response = curl_exec($client);
echo $curl_response;

/* POST Client */
$curl_post_data = array(
    'nombres' => 'Roger Manuel',
    'apellidos' => 'Rodriguez Alfaro'
);
$client = curl_init($url);
curl_setopt($client, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($client, CURLOPT_POST, 1);
curl_setopt($client, CURLOPT_POSTFIELDS, $curl_post_data);
$curl_response = curl_exec($client);
echo $curl_response;

/* PUT Client */
$client = curl_init($url);
curl_setopt($client, CURLOPT_RETURNTRANSFER, true);
curl_setopt($client, CURLOPT_CUSTOMREQUEST, "PUT");
$curl_put_data = array(
    'id' => '14',
    'nombres' => 'Enma',
    'apellidos' => 'Cruz Ticle'
);
curl_setopt($client, CURLOPT_POSTFIELDS, http_build_query($curl_put_data));
$curl_response = curl_exec($client);
if ($response === false) {
    $info = curl_getinfo($client);
    curl_close($client);
    die('error occured during curl exec. Additioanl info: ' . var_export($info));
}
curl_close($client);
echo $curl_response;


/* DELETE Client */
$client = curl_init($url);
curl_setopt($client, CURLOPT_RETURNTRANSFER, true);
curl_setopt($client, CURLOPT_CUSTOMREQUEST, "DELETE");
$curl_post_data = array(
    'id' => '15'
);
curl_setopt($client, CURLOPT_POSTFIELDS, http_build_query($curl_post_data));
$curl_response = curl_exec($client);
curl_close($client);
echo $curl_response;
Evidentemente, estaría restando la parte de la vista, pero eso ya será tema de otro post. Recuerden que es mi primer post de PHP, por lo que les pido comprensión si algunos conceptos o separaciones de capas no las hago correctamente, la idea es aportar no? :D