No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Exponer un recurso en particular a través de HTTP GET

7/19
Recursos

Aportes 46

Preguntas 11

Ordenar por:

Los aportes, preguntas y respuestas son vitales para aprender en comunidad. Regístrate o inicia sesión para participar.

Les dejo el codigo del Router.

<?php

$matches=[];
if(preg_match('/\/([^\/]+)\/([^\/]+)/',$_SERVER["REQUEST_URI"],$matches))
{
    $_GET['resource_type']=$matches[1];    
    $_GET['resource_id']=$matches[2];
    error_log(print_r($matches,1));
    require 'server.php';
}else if(preg_match('/\/([^\/]+)\/?/',$_SERVER["REQUEST_URI"],$matches))
{
    $_GET['resource_type']=$matches[1];        
    error_log(print_r($matches,1));
    require 'server.php';
}else
{
    error_log('No matches');
    http_response_code(404);
}

?>

Para los que estén corriendo en localhost tienen que agregar mas opciones a la condicional
en esta parte :
preg_match(’//([^/]+)/([^/]+)/([^/]+)/([^/]+)/’

antes del ultimo slash -> /
agregar: /([^/]+)
por cada directorio adicional que tengan

Versión con python

from flask import Flask, jsonify, make_response
from flask_restful import Resource, Api, abort

app = Flask(__name__)
api = Api(app)

BOOKS = {
    '1': {
        'isbn': '744586',
        'title': 'Cien años de soledad',
        'description': 'Lorem insup lol.',
        'autor': 'Gabriel Garcia Marquez'
    },
    '2': {
        'isbn': '7894546',
        'title': 'De animales a dioses',
        'description': 'Lorem insup lol.',
        'autor': 'Yuval Noah Harari'
    }
}

def abort_if_book_doesnt_exits(book_id):
    if book_id not in BOOKS:
        abort(404, message='El libro con id {} no existe'.format(book_id))

class BookList(Resource):
    def get(self):
        return jsonify({'data': BOOKS})

class Book(Resource):
    def get(self, book_id):
            abort_if_book_doesnt_exits(book_id)
            return jsonify({'data': BOOKS[book_id]})

class Authors(Resource):
    pass

class Generes(Resource):
    pass

api.add_resource(BookList, '/books')
api.add_resource(Book, '/books/<book_id>')
api.add_resource(Authors, '/authors')
api.add_resource(Generes, '/generes')

if __name__ == '__main__':
    app.run(debug=True)

Archivo server.php

<?php

// Definimos los recursos disponibles
$allowedResourceType = [
    'books',
    'authors',
    'genres',
];

// Validamos que el recurso este disponible
$resourceType = $_GET['resource_type'];

if ( !in_array($resourceType, $allowedResourceType)) {
    die;
}

// Defino los recursos
$books = [
    1 => [
        'titulo' => 'Lo que el viento se llevo',
        'id_autor' => 2,
        'id_genero' => 2,
    ],
    2 => [
        'titulo' => 'La Iliada',
        'id_autor' => 1,
        'id_genero' => 1,
    ],
    3 => [
        'titulo' => 'La Odisea',
        'id_autor' => 1,
        'id_genero' => 1,
    ],
];

// Se indica al cliente que lo que recibirá es un json
header('Content-Type: application/json');

// Levantamos el id del recurso buscado
$resourceId = array_key_exists('resource_id', $_GET) ? $_GET['resource_id']:'';

// Generamos la respuesta asumiendo que el pedido es correcto
switch( strtoupper($_SERVER['REQUEST_METHOD']) ) {
    case 'GET':
         if ( empty( $resourceId ) ){
            echo json_encode( $books );
         }else{
            if( array_key_exists( $resourceId, $books) ){
                echo json_encode( $books[ $resourceId ] );
            }
        }
        break;
    case 'POST':
        break;
    case 'PUT':
        break;
    case 'DELETE':
        break;
}

Consulta:

$curl "http://localhost:8000?resource_type=books&resource_id=1"

Archivo router.php

<?php

$matches=[];
if(preg_match('/\/([^\/]+)\/([^\/]+)/',$_SERVER["REQUEST_URI"],$matches))
{
    $_GET['resource_type']=$matches[1];    
    $_GET['resource_id']=$matches[2];
    error_log(print_r($matches,1));
    require 'server.php';
}elseif(preg_match('/\/([^\/]+)\/?/',$_SERVER["REQUEST_URI"],$matches))
{
    $_GET['resource_type']=$matches[1];        
    error_log(print_r($matches,1));
    require 'server.php';
}else
{
    error_log('No matches');
    http_response_code(404);
}

?>

Consulta:

$ curl http://localhost:8000/books/1

para aquellos que no entendieron la linea:

$resourceId=array_key_exists('resource_id',$_GET)?$_GET['resource_id']:'';

es un operador ternario.
que es igual a escribir esto:

if(array_key_exists(‘resource_id’,$_GET))
{
$resourceId=’$_GET[‘resource_id];
}else
{
$resourceId=’’;
}

el micrófono es muy potente… me distrae como traga saliva al hablar XD

Este video es oro puro.
La conversion a url amigables esta hecha de manera sencilla y precisa.
@beco tiene un curso, aqui en platzi, de expresiones regulares muy bueno, para comprender a fondo las lineas del preg_match

Para escapar el caracter & (Ampersand) en el CMD de windows se utiliza el caracter ^. Por lo que la petición completa quedaría de la siguiente manera:

curl http://localhost:8000?resource_type=books^&resource_id=2 | jq

Del carajo esta clase.! Es como hacer una autopsia al código, así se aprende medicina.! Excelente.

Apuntes:
Basicamente la expresion regular evalua, si despues de la primera barra de la URL, existe cualquier conjunto de caracteres que permita una URL, y luego repite el mismo proceso en caso de que haya un parámetro despues de una segunda barra, en caso tal de que no haya una segunda barra, solamente capturará lo contenido despues de la primera barra (que seria el parametro y en este caso, el parametro de books.

NOTA: En el caso de usar IIS, debemos crear un archivo .htaccess para la reescritura de URL

La operación del if en la misma linea se usa con el operador mas conocido como operador ternario.

Ejemplo en c#:

int input = new Random().Next(-5, 5);
string classify;

**If Normal**
if (input >= 0)
{
    classify = "nonnegative";
}
else
{
    classify = "negative";
}

**Operador Ternario**
classify = (input >= 0) ? "nonnegative" : "negative";

Consulta de un recurso en particular:
$ curl "http://localhost:800?resource_type=books&resource_id=1"

Rest propone: http://dominio.com/book/1
$ curl http://localhost:8000/books/1

Les dejo el código del router con comentarios de lo que hace para que no se confundan ni les queden lagunas

<?php
    /* El router nos va a permitir configurar el servidor para
     * poder gestionar las URL de las peticiones. Es decir, podremos
     * pasar de una petición con parametros:
     *      https://example.com/?resource_type=books&resource_id=1
     * a una sin parámetros y más estética:
     *      https://example.com/book/1
     * para que sea más agradable al usuario final.
     */

    $matches = [];

    // Si la URI coincide con la expresión regular (String que empieza con
    // una diagonal, seguido del tipo de recurso, seguido de otra diagonal,
    // seguido del recurso en particular, y que termine con otra diagonal),
    // entonces se agregan el tipo de recurso y el recurso particular al
    // arreglo de matches.
    if(preg_match('/\/([^\/]+)\/([^\/]+)/', $_SERVER['REQUEST_URI'], $matches)){
        // Creamos una URL con parámetros cuayos valores se obtendrán de lo que
        // la funcion preg_match agregó al arreglo
        $_GET['resource_type'] = $matches[1];
        $_GET['resource_id'] = $matches[2];

        // Si algo falla el error se envia a un correo (supuestamente).
        // Ver documentacion de error_log() y leer la descripcioón del
        // parámetro message_type.
        error_log(print_r($matches, 1));

        // Despues de hacer este análisis, se ejecuta el código del archivo
        // server.php para continuar con la ejecución de la API
        require 'server.php';
    }
    // Si la URI coincide con la expresión regular (String que empieza con
    // una diagonal, seguido del tipo de recurso,y que termine con otra 
    // diagonal), entonces se agrega el tipo de recurso arreglo de matches.
    elseif(preg_match('/\/([^\/]+)\/?/', $_SERVER['REQUEST_URI'], $matches)){
        $_GET['resource_type'] = $matches[1];
        error_log(print_r($matches, 1));
        require 'server.php';
    } else{
        // Si nada coincidió, agregamos la cadena 'No matches' al error log
        // y devolvemos un error 404
        error_log('No matches');
        http_response_code(404); 
    }
?>

Si tienes problemas con “jp” y estas en Mac, instala.

brew install jq

Link de donde lo resolví

LEs dejo mi codigo en php, para validar ademas de libros autores, generos y libros

<?php
//definimos los recursos disponibles
$allowedResourceTypes = [
    'books',
    'authors',
    'genders'
];
//validamos que el recurso este disponible
$resourceType = $_GET['resource_type'];
if(!in_array($resourceType, $allowedResourceTypes)){
    die;
}
//definos los recursos
$books = [
        0 => [
            'title'=>'Lo que el viento se llevo',
            'id_author' => 1,
            'id_gender'=>1
        ],
        1 => [
            'title'=>'La iliada',
            'id_author' => 0,
            'id_gender'=>0,
        ],
        2 => [
            'title'=>'La riqueza de las naciones',
            'id_author' => 2,
            'id_gender'=>2,
        ],
        3 => [
            'title'=>'El diario de Ana Frank',
            'id_author' => 3,
            'id_gender'=>3,
        ],
        4 => [
            'title'=>'La odisea',
            'id_author' => 0,
            'id_gender'=>0,
        ],
        5 => [
            'title'=>'Teoría de los sentimientos morales',
            'id_autor' => 2,
            'id_gender'=>2,
        ]
    ];

    $genders = [
        0 => [
            'id_genders' => 0,
            'genero' => 'poeseia/epopeya',

        ],
        1 => [
            'id_genders' => 1,
            'genero' => 'novela',
            
        ],
        2 => [
            'id_genders' => 2,
            'genero' => 'politica/naturaleza humana',
            
        ],
        3 => [
            'id_genders' => 3,
            'genero' => 'autobiografia',
            
        ]

        ];

    $authors =[
        0 => [
            'id_authors' => 0,
            'athors' => 'Homero',
        ],
        1 => [
            'id_authors' => 1,
            'athors' => 'Margaret Mitchell',
        ],
        2 => [
            'id_authors' => 2,
            'athors' => 'Adam Smith',
        ],
        3=>[
            'id_authors' => 3,
            'athors' => 'Ana frank',
        ],
    ];

    header('Content-Type:application/json');
    //levantamos el id del recurso buscado
    $resourceId = array_key_exists('resource_id',$_GET) ? $_GET['resource_id'] : '' ;

    // Generamos la respuesta asumiendo que el pedido es correcto
    switch(strtoupper($_SERVER['REQUEST_METHOD'])){
        case 'GET':
            //Validar que tipo de recurso es
            switch($resourceType){
                case 'books':
                    if(empty($resourceId)){
                        echo json_encode($books);
                    }{
                        if(array_key_exists($resourceId, $books)){
                            echo json_encode($books[$resourceId]);
                        }
                    }
                break;
                case 'authors':
                    if(empty($resourceId)){
                        echo json_encode($authors);
                    }{
                        if(array_key_exists($resourceId, $authors)){
                            echo json_encode($authors[$resourceId]);
                        }
                    }
                break;
                case 'genders':
                    if(empty($resourceId)){
                        echo json_encode($genders);
                    }{
                        if(array_key_exists($resourceId, $genders)){
                            echo json_encode($genders[$resourceId]);
                        }
                    }
                break;

            }
            
            
            break;
        case 'POST':
            break;
        case 'PUT':
            break;
        case 'DELETE':
            break;
    }```

aprendi a embellecer la url en php nativo, ya que en frameworks no es necesario pq ya existe esa implementacion por medio de router tambien

Buenas, algun lugar donde pueda entender mas sobre lo que hizo aca , documentacion o o video?

if (preg_match('/\/([^\/]+)\/([^\/]+)/', $_SERVER["REQUEST_URI"], $matches)) {
    $_GET['resource_type'] = $matches[1];
    $_GET['resource_id'] = $matches[2];

    error_log( print_r($matches, 1) );
    require 'server.php'; 
}elseif(preg_match('/\/([^\/]+)\/?/', $_SERVER["REQUEST_URI"], $matches)){
    $_GET['resource_type'] = $matches[1];
    error_log( print_R($matches, 1));

    require('server.php');
}else{

    error_log('No matches');
    http_response( 404 );
}```
<h3>Exponer un recurso en particular a través de HTTP GET</h3>

De la siguiente forma hacemos una validación en una sola línea y también estamos obteniendo de la url el id del recurso que se quiere consultar

// Levantamos el ID del recurso buscado
$resourceId = array_key_exists('resource_id', $_GET) ? $_GET['resource_id'] : '';

Y en el caso de las respuestas, tenemos que configurar el switch para que nos de una respuesta cuando venga vacío la petición y cuando venga llena

switch (strtoupper($_SERVER['REQUEST_METHOD'])) {
    case 'GET':
        if (empty( $resourceId )) {
            echo json_encode( $books );
        } else {
            if (array_key_exists( $resourceId, $books )) {
                echo  json_encode($books[ $resourceId ]);
            }
        }
        break;

Y en la consola tendremos que escribir la petición así:

curl http ://localhost:8000/\?resource_type\=books\&resource_id\=1

Si queremos que la petición con la url sea más corta la configuramos así:

<?php

$matches=[];

if(preg_match('/\/([^\/]+)\/([^\/]+)/', $_SERVER["REQUEST_URI"], $matches)) {
    $_GET['resource_type'] = $matches[1];    
    $_GET['resource_id'] = $matches[2];
    
    error_log( print_r($matches, 1) );
    require 'create_api.php';
} else if( preg_match('/\/([^\/]+)\/?/', $_SERVER["REQUEST_URI"], $matches) ) {
    $_GET['resource_type'] = $matches[1];        
    error_log( print_r($matches, 1) );
    require 'create_api.php';
} else {
    error_log('No matches');
    http_response_code(404);
}

Y ahora podemos hacer nuestra petición de la siguiente manera:

curl http ://localhost:8000/books/1

Si tienen problemas con ejecutar en consola los pedidos usen postman. Su interface es muy amigable y fácil de entender, Platzi tiene curso de Postman

Excelente ejemplo para ir entendiendo los estándares del API RESTFUL

Con lo poco que llevo de curso ya me esta pareciondo muy top.

De verdad aprecio este curso porque te explicar cómo construir desde cero un api rest aunque lo explique con php. Te da una idea como construirlo en otro lenguaje desde cero.

Me gusta la forma de explicar del profesor cuando escribe el código, es muy clara!! Esta super! 💥💥💥

Una forma de hacerlo con python usando Flask

from flask import Flask
import json

app = Flask(__name__)

@app.route('/books/<id>', methods=['GET'])
def books(id):
  books_info = {
    "1":{
      "title":"De animales a dioses",
      "id_author":"1",
      "id_genres":"1",
    },
    "2":{
      "title":"Brujula para el mundo contemporaneo",
      "id_author":"2",
      "id_genres":"2",
    },
  }
  if id in  books_info.keys():
    books_json = json.dumps(books_info[id])
  else:
    books_json = "The book was not found"
  return books_json

@app.route('/')
def index():
  return """
  <h1>Hi, World!</h1>
  <h2>You should go to /book/#id_book </h2>
  """

if __name__ == '__main__':
  app.run(debug=True)

Espero entender más cuando pase al API REST con Laravel. /:

tambien se puede (y es lo que a nosotros los novatos nos funciona mucho) if (isset($_GET['resource_id])){ $id = $_GET['resource_id'] }else{ $id = "" }

Excelente clase ahora entiendo el porqué de esas urls poco amigables, que no sirven para SEO.

preg_match para url amigable

hubiese sido bueno que hubieras dejado el codigo del Route en los marcadores

Por otra parte con laravel ya configura la ruta de un CRUD para que la URL sea midominio.com/book/1 con esta linea en web.php

Route::resource('book','BooksController');

Por alguna razón, creo de sintaxis, a mi no me funcionaba. Me mostraba error en la variable $matches lo que hice fue escribirla así $_matches en todos los lugares que aparecía y funcionó.

It works!

Me encantaria que lo hagan en otro lenguaje

En Go:

package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
)

var AllowedResources = map[string]bool{
	"books":   true,
	"authors": true,
	"genres":  true,
}

type Book struct {
	Titulo    string `json:"titulo"`
	Id_Autor  int    `json:"id_autor"`
	Id_gereno int    `json:"id_gereno"`
}

var Books = []Book{
	{
		Titulo:    "Lo que el viento se llevo",
		Id_Autor:  2,
		Id_gereno: 2,
	},
	{
		Titulo:    "El señor de los anillos",
		Id_Autor:  1,
		Id_gereno: 1,
	},
	{
		Titulo:    "La Odisea",
		Id_Autor:  1,
		Id_gereno: 3,
	},
}

func main() {
	http.Handle("/", http.HandlerFunc(ExampleHandler))
	http.HandleFunc("/books", books)
	http.HandleFunc("/book/", book)

	http.ListenAndServe(":8080", nil)
}

func book(w http.ResponseWriter, r *http.Request) {
	idstr := r.URL.Path[len("/book/"):]
	id, err := strconv.Atoi(idstr)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		fmt.Fprint(w, "<h1>Bad Request</h1>")
		return
	}

	switch r.Method {
	case http.MethodGet:
		getBook(w, r, id)
	case http.MethodDelete:
		w.Write([]byte("DELETE"))
	}
}

func getBook(w http.ResponseWriter, r *http.Request, id int) {
	// Get a single book
	maxBooks := len(Books)

	if id >= maxBooks {
		w.WriteHeader(http.StatusNotFound)
		fmt.Fprint(w, "<h1>Not Found</h1>")
		return
	}

	book := Books[id]
	response, err := json.Marshal(book)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, "<h1>Internal Server Error</h1>")
		return
	}

	fmt.Fprint(w, string(response))
}

func books(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		getBooks(w, r)
	case "POST":
		postBooks(w, r)
	}
}

func getBooks(w http.ResponseWriter, r *http.Request) {
	respose, err := json.Marshal(Books)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprint(w, "<h1>Internal Server Error</h1>")
		return
	}

	fmt.Fprint(w, string(respose))
}

func postBooks(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("POST"))
}

Mi código en Python con FastAPI

Les dejo el enlace de postman por si desean consumir los recursos de una forma gráfica.

https://www.postman.com/downloads/

y enlace del curso de postman de platzi

https://platzi.com/clases/postman/

no sé sino son curso actualizados, pero nunca me salen de forma local. 😦
Les juro que hago paso por paso y no que queda.

yo recuerdo que también se tenía que modificar algunas configuraciones en apache para poder tener url amigables

ESTE CURSO ES TEORICO Y PRACTICO

clase de implementación de URLs amigables!, ni me pregunten cómo lo hubiera hecho yo (shame on me), hubiera buscado cosas como: verificación de mod_rewrite de Apache, inclusión de .htaccess, par de líneas en este archivo, luego otras para que coincidiera con los parámetros correspondientes con los recursos a consultar … qué va!, pero qué bueno que estamos aquí aprendiendo y actualizándonos! 🤓😊😁

Pagina donde se enseña todo sobre expresiones regulares, esta en ingles:

https://www.regular-expressions.info/tutorial.html

¡Excelente!, ver el uso de las expresiones regulares.

gracias 😃

TIP; para no estar cerrando y abriendo la consola cada vez que empiezan el server, pueden usar ctrl (o command en MAC) + C. El server se cerrará y luego lo pueden iniciar de nuevo.

Excelente

Vale, lo que hace el router es hacer match con una cadena tipo /algo/otracosa y asigna los valores a la variable $_GET, y por último delega toda la información al server.php