2019年2月1日 星期五

RESTful API設計:全部程式

RESTful API設計:全部程式

*route:
routes\api.php
app\Http\Middleware\AuthUserMiddleware.php
app\Http\Kernel.php

*controller:
app\Http\Controllers\api\ArticleController.php
app\Http\Controllers\Auth\RegisterController.php
app\Http\Controllers\Auth\LoginController.php
app\Exceptions\Handler.php

*model:
app\Article.php
app\User.php

*migration:
database\migrations\2019_01_21_052638_create_articles_table.php
database\migrations\2014_10_12_000000_create_users_table.php
database\migrations\2019_01_25_122630_adds_api_token_to_users_table.php

*seeder:
database\seeds\ArticlesTableSeeder.php
database\seeds\UsersTableSeeder.php
database\seeds\DatabaseSeeder.php

=========================
routes\api.php

<?php

use Illuminate\Http\Request;

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/

Route::middleware('auth:api')->get('/user', function (Request $request) {
    return $request->user();
});

//Article
// Route::group(['middleware'=>'auth:api'],function(){
Route::group(['middleware'=>'user.auth'],function(){
    Route::get('articles','api\ArticleController@index');
    Route::get('articles/{article}','api\ArticleController@show');
    Route::post('articles','api\ArticleController@store');
    Route::put('articles/{article}','api\ArticleController@update');
    Route::delete('articles/{article}','api\ArticleController@delete');
});

//user
Route::post('register', 'Auth\RegisterController@register');
Route::post('login', 'Auth\LoginController@login');
Route::post('logout', 'Auth\LoginController@logout');

=========================
app\Http\Middleware\AuthUserMiddleware.php

<?php

namespace App\Http\Middleware;

use Closure;
use App\Article; //model

class AuthUserMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        //預設不允許存取
        $is_allow_access=false;

        //取得會員編號
        // $user_id=session()->get('user_id');
        session_start();
        $user_id=$_SESSION['user_id'];

        //session有會員編號,允許存取
        if(!is_null($user_id)){
            $is_allow_access=true;
        }

        //不允許存取,回傳"未登入"
        if(!$is_allow_access){
            return response()->json(['status' => '未登入']);
        }

        return $next($request);
    }
}

=========================
app\Http\Kernel.php

<?php

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
    /**
     * The application's global HTTP middleware stack.
     *
     * These middleware are run during every request to your application.
     *
     * @var array
     */
    protected $middleware = [
        \App\Http\Middleware\CheckForMaintenanceMode::class,
        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
        \App\Http\Middleware\TrimStrings::class,
        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
        \App\Http\Middleware\TrustProxies::class,
    ];

    /**
     * The application's route middleware groups.
     *
     * @var array
     */
    protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            // \Illuminate\Session\Middleware\AuthenticateSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],

        'api' => [
            'throttle:60,1',
            'bindings',
        ],
    ];

    /**
     * The application's route middleware.
     *
     * These middleware may be assigned to groups or used individually.
     *
     * @var array
     */
    protected $routeMiddleware = [
        'auth' => \App\Http\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
        'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
        'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
        'user.auth' => \App\Http\Middleware\AuthUserMiddleware::class,
    ];

    /**
     * The priority-sorted list of middleware.
     *
     * This forces non-global middleware to always be in the given order.
     *
     * @var array
     */
    protected $middlewarePriority = [
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\Authenticate::class,
        \Illuminate\Session\Middleware\AuthenticateSession::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
        \Illuminate\Auth\Middleware\Authorize::class,
    ];
}

=========================
app\Http\Controllers\api\ArticleController.php

<?php

namespace App\Http\Controllers\api;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Article;

class ArticleController extends Controller
{
    //查全部資料
    public function index()
    {
        // return Article::all();
        return response()->json(['status' => 'success', 'posts' => Article::all()]);
    }

    //查單筆資料
    public function show(Article $article)
    {
        // return $article;

        return response()->json(['status' => 'success', 'post' => $article]);

        //異常處理要寫在 app\Exceptions\Handler.php
        // return response()->json(['status' => 'error', 'message' => 'Id Not Found']);
    }

    //新增資料
    public function store(Request $request)
    {
        $article=Article::create($request->all());

        // return response()->json($article,201);
        return response()->json(['status' => 'success', 'posts' => $article]);
    }

    //修改資料
    public function update(Request $request, Article $article)
    {
        $article->update($request->all());

        // return response()->json($article,200);
        return response()->json(['status' => 'success', 'posts' => $article]);

        //異常處理要寫在 app\Exceptions\Handler.php
        // return response()->json(['status' => 'error', 'message' => 'Id Not Found']);
    }

    //刪除資料
    public function delete(Article $article)
    {
        $article->delete();

        // return response()->json(null,204);
        return response()->json(['status' => 'success']);

        //異常處理要寫在 app\Exceptions\Handler.php
        // return response()->json(['status' => 'error', 'message' => 'Id Not Found']);
    }
}

=========================
app\Http\Controllers\Auth\RegisterController.php

<?php

namespace App\Http\Controllers\Auth;

use App\User;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Foundation\Auth\RegistersUsers;

use Illuminate\Http\Request; //自加
use Illuminate\Auth\Events\Registered; //自加

class RegisterController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Register Controller
    |--------------------------------------------------------------------------
    |
    | This controller handles the registration of new users as well as their
    | validation and creation. By default this controller uses a trait to
    | provide this functionality without requiring any additional code.
    |
    */

    use RegistersUsers;

    /**
     * Where to redirect users after registration.
     *
     * @var string
     */
    protected $redirectTo = '/home';

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest');
    }

    /**
     * Get a validator for an incoming registration request.
     *
     * @param  array  $data
     * @return \Illuminate\Contracts\Validation\Validator
     */
    protected function validator(array $data)
    {
        return Validator::make($data, [
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
            'password' => ['required', 'string', 'min:6', 'confirmed'],
        ]);
    }

    /**
     * Create a new user instance after a valid registration.
     *
     * @param  array  $data
     * @return \App\User
     */
    protected function create(array $data)
    {
        return User::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => Hash::make($data['password']),
        ]);
    }

    //自加 register
    public function register(Request $request)
    {
        // Here the request is validated. The validator method is located
        // inside the RegisterController, and makes sure the name, email
        // password and password_confirmation fields are required.
   
        $this->validator($request->all())->validate();
   
        // A Registered event is created and will trigger any relevant
        // observers, such as sending a confirmation email or any
        // code that needs to be run as soon as the user is created.
   
        event(new Registered($user = $this->create($request->all())));
   
        // After the user is created, he's logged in.
   
        $this->guard()->login($user);
   
        // And finally this is the hook that we want. If there is no
        // registered() method or it returns null, redirect him to
        // some other URL. In our case, we just need to implement
        // that method to return the correct response.
   
        return $this->registered($request, $user)?: redirect($this->redirectPath());
    }

    //自加 registered
    protected function registered(Request $request, $user)
    {
        $user->generateToken();
   
        return response()->json(['data' => $user->toArray()], 201);
    }

}

=========================
app\Http\Controllers\Auth\LoginController.php

<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;

use Illuminate\Http\Request; //自加
use Illuminate\Support\Facades\Auth; //自加

class LoginController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Login Controller
    |--------------------------------------------------------------------------
    |
    | This controller handles authenticating users for the application and
    | redirecting them to your home screen. The controller uses a trait
    | to conveniently provide its functionality to your applications.
    |
    */

    use AuthenticatesUsers;
    //vendor\laravel\framework\src\Illuminate\Foundation\Auth\AuthenticatesUsers.php

    /**
     * Where to redirect users after login.
     *
     * @var string
     */
    protected $redirectTo = '/home';

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest')->except('logout');
    }

    //自加
    public function login(Request $request)
    {
        //vendor\laravel\framework\src\Illuminate\Foundation\Auth\AuthenticatesUsers.php

        $this->validateLogin($request);

        if ($this->attemptLogin($request)) {
            $user = $this->guard()->user();
            $user->generateToken();

            // return $user->generateToken();

            // $userx=session()->put('user_id',$user->id); //驗證登入
            session_start();
            $_SESSION['user_id']=$user->id;

            return response()->json([
                'data' => $user->toArray(),
                // 'data' => $_SESSION['user_id'],
            ]);
        }

        return $this->sendFailedLoginResponse($request);
    }

    //自加
    public function logout(Request $request)
    {
        $user = Auth::guard('api')->user();

        if ($user) {
            $user->api_token = null;
            $user->save();
        }

        session_start();
        $_SESSION['user_id']=null;

        return response()->json(['data' => 'User logged out.'], 200);
    }
}

=========================
app\Exceptions\Handler.php

<?php

namespace App\Exceptions;

use Exception;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;

use Illuminate\Database\Eloquent\ModelNotFoundException; //自加
use Illuminate\Auth\AuthenticationException; //自加

class Handler extends ExceptionHandler
{
    /**
     * A list of the exception types that are not reported.
     *
     * @var array
     */
    protected $dontReport = [
        //
    ];

    /**
     * A list of the inputs that are never flashed for validation exceptions.
     *
     * @var array
     */
    protected $dontFlash = [
        'password',
        'password_confirmation',
    ];

    /**
     * Report or log an exception.
     *
     * @param  \Exception  $exception
     * @return void
     */
    public function report(Exception $exception)
    {
        parent::report($exception);
    }

    /**
     * Render an exception into an HTTP response.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Exception  $exception
     * @return \Illuminate\Http\Response
     */
    public function render($request, Exception $exception)
    {  
        //自加
        if ($exception instanceof ModelNotFoundException) {
            return response()->json(['status' => 'error', 'message' => 'Id Not Found']);
        }
       
        //原本設定:回傳html
        return parent::render($request, $exception);
    }

    //自加
    protected function unauthenticated($request, AuthenticationException $exception)
    {
        return response()->json(['error' => 'Unauthenticated'], 401);
    }
}

=========================
app\Article.php

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Article extends Model
{
    protected $fillable = ['title', 'body'];
}

=========================
app\User.php

<?php

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];

    //自加 generateToken
    //用於 app\Http\Controllers\Auth\RegisterController.php
    public function generateToken()
    {
       $this->api_token = str_random(60);
       $this->save();
   
       return $this->api_token;
    }
}

=========================
database\migrations\2019_01_21_052638_create_articles_table.php

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateArticlesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('articles', function (Blueprint $table) {
            $table->increments('id');
            $table->string('title',30);
            $table->text('body');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('articles');
    }
}

=========================
database\migrations\2014_10_12_000000_create_users_table.php

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name',30);
            $table->string('email',50)->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password',60);
            $table->rememberToken();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('users');
    }
}

=========================
database\migrations\2019_01_25_122630_adds_api_token_to_users_table.php

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class AddsApiTokenToUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->string('api_token', 60)->unique()->nullable();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->dropColumn(['api_token']);
        });
    }
}

=========================
database\seeds\ArticlesTableSeeder.php

<?php

use Illuminate\Database\Seeder;
use App\Article;

class ArticlesTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        DB::table('articles')->truncate();

        for ($i=0; $i<50; $i++) {
            Article::create([
                'title'=>str_random(10),
                'body'=>str_random(255)
            ]);
        }
    }
}

=========================
database\seeds\UsersTableSeeder.php

<?php

use Illuminate\Database\Seeder;
use App\User;

class UsersTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        //清除users table
        User::truncate();

        $faker=\Faker\Factory::create();

        $password=Hash::make('toptal');

        User::create([
            'name'=>'Administrator',
            'email'=>'admin@test.com',
            'password'=>$password
        ]);

        for ($i=0; $i<10 ; $i++) {
            User::create([
                'name'=>$faker->name,
                'email'=>$faker->email,
                'password'=>$password
            ]);
        }
    }
}

=========================
database\seeds\DatabaseSeeder.php

<?php

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run()
    {
        $this->call(UsersTableSeeder::class);
        $this->call(ArticlesTableSeeder::class);
    }
}

=========================

沒有留言:

張貼留言