Skip to content

Instantly share code, notes, and snippets.

@javascripto
Last active August 9, 2018 12:13
Show Gist options
  • Select an option

  • Save javascripto/fffd11d22bffa7108f864cee1229afa1 to your computer and use it in GitHub Desktop.

Select an option

Save javascripto/fffd11d22bffa7108f864cee1229afa1 to your computer and use it in GitHub Desktop.
Anotações Laravel REST API - (parcial)

API's REST com Laravel

Estrutura padrão da URL:

Servidores rest não armazenam cookies - são stateless

  • REST - REpresentational State Transfer
  • Orientado a resources (recursos)
  • Stateless
  • HTTP status code
  • HTTP methods (GET, POST, PUT, DELETE)
  • HTTP headers
  • Negociação de content-type

HTTP Status Code

2xx - Tudo certo
3xx - Alteração de estado ( não usado no REST por ser stateless )
4xx - Erro no cliente
5xx - Erro no servidor

200 - Tudo ok
301 - Redirecionamento permanente
302 - Redirecionamento Temporário
404 - Página não encontrada
422 - Falha na validação
500 - Erro no servidor

HTTP Headers

Authorization: bearer <token>
Content-Type: application/x-www-form-utlenconded
Content-Type: application/json
Accept: application/json

Preparando o laravel

mkdir laravel_rest && cd laravel_rest
composer create-project --prefer-dist laravel/laravel 
echo "CREATE DATABASE curso_laravel_rest_2" | mysql -uroot -p`
  • Edite o .env informando o DB_DATABASE=curso_laravel_rest_2 e tambem usuario e senha do banco

  • php artisan serve

Nosso primeiro endpoint

  • php artisan make:model Product -mc
  • A flag -mc indica a criação da migration e do controler juntamente com a model.
  • A flag -mr indica a criação de um controler do tipo resource com método index, store, e outros que são padrões do laravel.
  • Na migration criada indicaremos os seguintes campos:
Schema::create('products', function (Blueprint $table) {
  $table->increments('id');
  $table->string('title');
  $table->text('body');
  $table->timestamps();
});
  • Abra o arquivo routes/api.php e crie a seguinte rota:
Route::prefix('v1')->group(function() {
  Route::get('/products', 'ProductController@index');
});
  • Abra o controller e crie o método index:
use App\Product;

class ProductController extends Controller
{
  public function index()
  {
    return Product::all();
  }
}

Inserindo registros

  • Abra o controler e crie um novo método com nome store
public function store(Request $request)
{
  return Product::create($request->all());
}
  • Abra as rotas da api e inclua uma rota post no grupo já criado anteriormente
  Route::post('/products', 'ProductController@store');
  • Baixe o app postman para testar
    • Envie uma requisição POST para http://localhost:8000/api/v1/products
    • Na aba Body do postman, escolha a opção x-www-form-urlencoded e crie os campos title e body criados na migration
    • Preencha os campos com valores e envie a requisição.
    • Vai acontecer um erro na requisição porque é necessário configurar o model primeiro.
  • Abra o model Product e informe os campos preenchiveis.
  protected $fillable = ['title', 'body'];
  • Envie a requisição post novamente e tudo ocorrerá bem.
  • Verifique no banco de dados se o registro foi inserido
  • echo "SELECT * FROM curso_laravel_rest_2.products" | mysql -uroot -p

Finalizando o CRUD

  • Crie uma nova rota para atualizar os registros: Route::put('/products/{product}', 'ProductController@update');
  • O parametro nomeado na rota put precisa ser o nome do model (product) para que seja feito um binding implicito durante a requisição.
  • Crie o método update no Controller para fazer a atualização do registro:
  public function update(Request $request, Product $product)
  {
    $product->update($request->all());
    return $product;
  }
  • O método update receberá como segundo parametro a injeção do model product.
  • Modifique os dados do Body da requisição e faça uma nova requisição put na url: http://localhost:8000/api/v1/products/1
  • Crie agora uma nova rota get para listar apenas um produto: Route::get('/products/{product}', 'ProductController@show');
  • Crie o método show:
  public function show(Product $product)
  {
    return $product;
  }
  • Faça a requisição na mesma url anterior do put porem dessa vez com o método get para obter o recurso desejado informando o seu id.
  • Agora para finalizar crie a rota para delete: Route::delete('/products/{product}', 'ProductController@destroy');
  • E tambem o método destroy:
  public function destroy(Product $product)
  {
    $product->delete();
    return $product;
  }
  • Utilize a mesma url de update porem utilizando o método http delete para remover o item.
  • Após remover o recurso, faça uma requisição get do método index do controller para ver que é retornado uma collection vazia.

Trabalhando com mais de um resource

  • Para facilitar as coisas, não é necessario declarar todas aquelas rotas como vimos anteriormente.
  • Se seguirmos este padrão utilizado pelo laravel e API's REST, podemos substituir todas aquelas rotas por apenas uma do tipo resource
Route::prefix('v1')->group(function() {
  Route::resource('products', 'ProductController@index');
});
  • Faça testes com os métodos post, get, put e delete com as mesmas URL's utilizadas anteriormente para testar as rotas que o tipo resource nos fornece.
  • Alem do metodo estatico de rotas do tipo Resource, tambem existe o método estatico Resources (no plural), onde podem ser declarados diversos recursos de uma vez inseridos em um array associativo de recursos e controllers.
Route::resources([
  'products' => 'ProductController',
  'users' => 'UserController'
]);
  • Um comando do artisan pode ser usado para cirar controllers já com métodos para recursos:
  • php artisan make:controller --resource UserController
  • Este controller gerado ainda vem com dois métodos extras, são eles o create e o edit que são para telas de formularios para criação e edição de itens. Em API's REST estes métodos nao são utilizados nos controllers.
  • O model e a migration para user já existem por padraõ em aplicações laravel. Agora que foram criadas as rotas e o controller para este recurso, você já pode testa-lo.
  • Faça testes com os diversos metodos http no postman para o recurso user, lembrando que os campos disponiveis para envio estão declarados na migration. (name, email, password)
  • Veja que ao fazer a inserção de um novo registro, as informações retornadas como json são apenas name e email. Isso acontece porque o campo password está declarado no model no array de nome $hidden

Trabalhando com validações

  • Crie uma Request para Product com o comando: php artisan make:request ProductRequest
  • Abra o request criado (app/Http/Requests/ProductRequest) e altere o método authorize para retornar true;
  • Defina as regras de validação no método rules:
return [
  'title' => 'required',
  'body' => 'required|min:10'
];
  • Agora abra o Controller de Product e troque o caminho do namespace de Request usado para o caminho do Request criado e dê um alias para ele como Request para que não precise Substituir o nome em outras partes do mesmo arquivo.
  • Antes: use Illuminate\Http\Request;. Depois: use App\Http\Requests\ProductRequest as Request
  • Agora faça uma requisição Post sem enviar nunhum dado no body e você receberá um erro informando a mensagem de campos requiridos
  • Caso você seja redirecionado em uma versão anterior do laravel, é ncessário enviar como cabeçalho da requisição, o campo seguinte: "X-Requested-With": "XMLHttpRequest" ou então o campo "Accept": "Application/json"
  • As mensagens são retornadas em inglês porem, é possivel traduzi-las para seu idioma com o laravel

Instalando o Laravel Passport

  • composer require laravel/passport
  • php artisan migrate. Para fazer as migrations de autenticação
  • php artisan passport:install. Copie os dados gerados para um arquivo declarado no .gitignore.
  • Abra a model User e declare: use Laravel\Passport\HasApiTokens; para usar o Trait
  • Tambem delcare dentro da classe: use HasApiTokens, Notifiable;
  • Abra o arquivo app/Providers/AuthServiceProvider.php; e declare: use Laravel\Passport\Passport;
  • No método boot() do AuthServiceProvider adicione uma linha no final: Passport::routes();
  • Agora abra o arquivo config/auth.php; e na propriedade 'guards' => 'api' => 'drive', altere o valor 'token' para 'passport'.
  • Já podemos testar a rota POST http://localhost:8000/oauth/token para receber um erro de acesso.

Vue Components do Laravel Passport e requisição de token

  • php artisan vendor:publish --tag=passport-components. Para publicar dentro de resources/assets/js/components alguns componentes vue.
  • Abra o arquivo resources/assets/js/app.js
  • Adicione as seguintes linhas para registrar os compoentes gerados no subdiretorio components/passport:
Vue.component('passport-clients', require('./components/passport/Clients.vue'));
Vue.component('authorized-clients', require('./components/passport/AuthorizedClients.vue'));
Vue.component('personal-access-tokens', require('./components/passport/PersonalAccessTokens.vue'));
  • Agora instale as dependencias de front-end com npm install.
  • Digite php artisan make:auth para publicar o scaffold de algumas novas views de authenticação em resources/views.
  • Na view layout/app.blade.php a tag script que faz referencia ao asset modificado anteriormente estará inserido.
  • Abra a view home.blade.php e insira a tag do componente <passport-clients></passport-clients> logo abaixo do texto You are logged in! para testar.
  • Insira tambem as tags dos outros dois componentes para que possamos ve-los ao fazer o primeiro login.
  • Agora o arquivo public/js/app.js precisa ser atualizado com o conteudo adicionado no resources/assets/js/app.js
  • Execute o comando npm run dev para atualizá-lo.
  • Vamos então acessar a rota raiz http://localhost:8000 e registrar um novo usuário.
  • Crie um usuario e faça login para ver os componentes gerados.
  • Vamos agora fazer testes no postman

Testando rota de autenticação no postman

  • Crie uma nova requisição POST para o endereço http://localhost:8000/oauth/token
  • No body da requisição vamos cadastrar alguns campos:
    • grant_type: password
    • client_id: 1 ou 2 dependendo de qual key você for utilizar
    • client_secret: coloque aquela chave gerada e guardada no arquivo ingorado pelo .gitignore
    • username: email do usuario recem cadastrado
    • password: senha do usuario cadastrado
    • scope: permissões que você quer ter acesso (deixe em branco)
  • Caso dê algume erro de cliente invalido, use o comando php artisan passport:client --password para gerar uma nova client_id e client_secret.
  • A requisição irá te retornar um access_token e um refresh_token. Copie o access_token para darmos continuidade.

Obtendo o access_token

  • Abra o arquivo routes/api.php e adicione o middleware de autenticação antes do prefix da nossa rota.
Route::middleware('auth:api')->prefix('v1')->group(function() {
  Route::resources([
    'products' => 'ProductController',
    'users' => 'UserController'
  ]);
});
  • Agora as nossas rotas estarão protegidas.
  • Tente fazer uma requisição em /api/v1/products e você receberá a mensaggem de erro Unauthenticated..
  • Para fazer acesso às rotas protegidas pelo middleware de authenticação, é necessário passar um novo Header de requisição.
  • Crie o cabeçalho Authorization e cole o valor copiado de access_token da rota /oauth/token mas antes dele coloque o nome Bearer e separe-o do token com um espaço.
  • Pronto, agora ja com o token no cabeçalho, faça novamente uma requisição em /api/v1/products.
  • Esta forma de autenticação é segura se for feita em aplicações que rodam no backend pois o client_secret utilizado para obter o access_token está protegido no servidor. Porem em aplicações front-end ou mobile não dá para guardar essa key, nesse tipo de aplicação é necessário utilizar um proceso de autenticação chamado de implicit grant.
  • Abra o arquivo app/Providers/AuthServiceProvider.php e declare no método boot() após Passport::routes(); a o seguinte: Passport::enableImplicitGrant();.
  • Ativado o impicit grant, vamos agora acessar pelo navegador no endereço: http://localhost:8000/oauth/authorize?client_id=3&response_type=code&scope=&redirect_uri=http://meusite.com/pagina-de-login passando alguns parametros pela url.
  • Acesse a rota /home depois de fazer login comum no laravel clique em Create new Client no componente vue gerado anteriormente.
  • Preencha os campos Name e Redirect URL com os valores passados na query na url do implicit grant: Meu site e http://meusite.com/pagina-de-login
  • O componente vue vai gerar um novo id e secret.
  • Agora acesse novamente a url alterando o id para o que foi informado pelo componente vue.
  • A página irá pedir permissão para acessar sua conta, semelhante ao login feito por meio do google, facebook, twitter, dropbox, github e outros.

Trabalhando com tokens

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment