網站開發時,未採前後端分離時,都是自己做前後端 API,也沒啥文件管理,當工程師離職後,接手的人得必須重新看原始程式碼去理解之前工程師的邏輯,無形浪費接手的工程師很多時間,如果有良好的文件管理,接手的工程師就可以很快速的理解該 API 的用途。
安裝 DarkaOnLine/L5-Swagger 套件
# 根據 Laravel 版本安裝
composer require "darkaonline/l5-swagger:6.*"
產生設定檔
php artisan vendor:publish --provider "L5Swagger\L5SwaggerServiceProvider"
執行完上述步驟後就可以在瀏覽器輸入 http://localhost/api/documentation 可以看到套件已經安裝起來,但是出現錯誤訊息,因為還沒做好所有的定義。
修改 .env
# 設定 API Documentation Server 網址
L5_SWAGGER_CONST_HOST=https://localhost
# 自動更新文件
L5_SWAGGER_GENERATE_ALWAYS=true
在 Controller 中建立描述檔
app\Http\Controller\Controller.php 這邊主要放置 API Documentation 的 Info 、 Server 及 SecurityScheme,這樣就不用在每一個 Controller 都寫這段。
<?php
/**
* @OA\Info(
* version="1.0.0",
* title="Codinglab API Documentation",
* description="My API Documentation description",
* @OA\Contact(
* email="admin@mail.com"
* ),
* @OA\License(
* name="Apache 2.0",
* url="http://www.apache.org/licenses/LICENSE-2.0.html"
* )
* )
*
* @OA\Server(
* url=L5_SWAGGER_CONST_HOST,
* description="API Server"
* )
*
* @OA\SecurityScheme(
* type="http",
* description="Login with email and password to get the authentication token",
* name="Token based Based",
* in="header",
* scheme="bearer",
* bearerFormat="JWT",
* securityScheme="apiAuth",
* )
*/
namespace App\Http\Controllers;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
}
修改 API\UserLoginController.php
<?php
namespace App\Http\Controllers\API;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Validator;
use App\User as UserEloquent;
use Illuminate\Support\Facades\Auth;
/**
* @OA\Tag(
* name="USER",
* description="OPERATIONS ABOUT USER"
* )
*/
class UserLoginController extends Controller
{
/**
* Create a new AuthController instance.
*
* @return void
*/
public function __construct()
{
//除了 login 其餘 function 都要經過 api 的 middleware 檢查
$this->middleware('api', ['except' => ['login']]);
}
/**
* Get a JWT via given credentials.
*
* @return \Illuminate\Http\JsonResponse
*/
/**
* @OA\Post(
* path="/api/login",
* summary="User Login",
* tags={"USER"},
* operationId="login",
*
* @OA\Parameter(
* name="email",
* in="query",
* required=true,
* @OA\Schema(
* type="string"
* )
* ),
* @OA\Parameter(
* name="password",
* in="query",
* required=true,
* @OA\Schema(
* type="string"
* )
* ),
* @OA\Response(
* response=200,
* description="Success",
* @OA\MediaType(
* mediaType="application/json",
* )
* ),
* @OA\Response(
* response=401,
* description="Unauthenticated"
* ),
* @OA\Response(
* response=400,
* description="Bad Request"
* ),
* )
**/
public function login()
{
$credentials = request(['email', 'password']);
//驗證
$validator = Validator::make($credentials, [
'email' => 'required|email',
'password' => 'required|min:4',
]);
//驗證失敗返回訊息
if ($validator->fails()) {
return response()->json(['error' => '帳號密碼錯誤'], 400);
}
//透過Auth取得JWT token
if (! $token = auth('api')->attempt($credentials)) {
return response()->json(['error' => '授權失敗'], 401);
}
//將token拋出
return $this->respondWithToken($token);
}
/**
* Get the authenticated User.
*
* @return \Illuminate\Http\JsonResponse
*/
/**
* @OA\POST(
* path="/api/me",
* summary="GET Curent User Information",
* tags={"USER"},
* operationId="users",
* security={{ "apiAuth": {} }},
* @OA\Response(
* response=200,
* description="Success"
* ),
* )
*/
public function me()
{
return response()->json(auth('api')->user());
}
/**
* Log the user out (Invalidate the token).
*
* @return \Illuminate\Http\JsonResponse
*/
/**
* @OA\POST(
* path="/api/logout",
* summary="Log the user out (Invalidate the token).",
* tags={"USER"},
* operationId="users",
* security={{ "apiAuth": {} }},
* @OA\Response(
* response=200,
* description="Success"
* ),
* @OA\Response(
* response=401,
* description="Unauthenticated"
* ),
* )
*/
public function logout()
{
//找出登入者資料
$user = auth('api')->user();
//JWT RAW data
// $payload = auth('api')->payload();
//重新更新token
// $newtoken = auth('api')->refresh();
// return $this->respondWithToken($newtoken);
//抓取是否有返回token
// $token = request(['token']);
//如果沒使用者存在則給出錯誤訊息
if (!$user) {
return response()->json(['error' => 'Unauthorized'], 401);
}
//登出並清除token
auth('api')->logout();
return response()->json(['message' => 'Successfully logged out'], 200);
}
/**
* Refresh a token.
*
* @return \Illuminate\Http\JsonResponse
*/
public function refresh()
{
return $this->respondWithToken(auth()->refresh());
}
/**
* Get the token array structure.
*
* @param string $token
*
* @return \Illuminate\Http\JsonResponse
*/
protected function respondWithToken($token)
{
return response()->json([
'user' => auth('api')->user(), //將使用者資料一起帶出
'access_token' => $token,
'token_type' => 'bearer',
'expires_in' => auth('api')->factory()->getTTL() * 60
]);
}
/**
* @OA\Get(
* path="/api/users",
* tags={"USER"},
* summary="GET LIST OF USERS",
* operationId="users",
* @OA\Response(
* response=200,
* description="Success"
* )
* )
*/
public function users()
{
$users = UserEloquent::all();
return response()->json($users, 200);
}
}
每次修改完註解必須執行產生器重新產生文件,如果覺得麻煩,可以直接在 .env 中設定 L5_SWAGGER_GENERATE_ALWAYS=true,這樣只要在瀏覽文件資料時 https://localhost/api/documentation 就會自動產生相關的文件。
php artisan l5-swagger:generate
產生出來的文件 api-docs.json
{
"openapi": "3.0.0",
"info": {
"title": "API Documentation",
"description": "My API Documentation description",
"contact": {
"email": "admin@mail.com"
},
"license": {
"name": "Apache 2.0",
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
},
"version": "1.0.0"
},
"servers": [
{
"url": "https://localhost",
"description": "API Server"
}
],
"paths": {
"/api/login": {
"post": {
"tags": [
"USER"
],
"summary": "User Login",
"operationId": "login",
"parameters": [
{
"name": "email",
"in": "query",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "password",
"in": "query",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Success",
"content": {
"application/json": {}
}
},
"401": {
"description": "Unauthenticated"
},
"400": {
"description": "Bad Request"
}
}
}
},
"/api/me": {
"post": {
"tags": [
"USER"
],
"summary": "GET Curent User Information",
"operationId": "users",
"responses": {
"200": {
"description": "Success"
}
},
"security": [
{
"apiAuth": []
}
]
}
},
"/api/logout": {
"post": {
"tags": [
"USER"
],
"summary": "Log the user out (Invalidate the token).",
"operationId": "users",
"responses": {
"200": {
"description": "Success"
},
"401": {
"description": "Unauthenticated"
}
},
"security": [
{
"apiAuth": []
}
]
}
},
"/api/users": {
"get": {
"tags": [
"USER"
],
"summary": "GET LIST OF USERS",
"operationId": "users",
"responses": {
"200": {
"description": "Success"
}
}
}
}
},
"components": {
"securitySchemes": {
"apiAuth": {
"type": "http",
"description": "Login with email and password to get the authentication token",
"name": "Token based Based",
"in": "header",
"bearerFormat": "JWT",
"scheme": "bearer"
}
}
},
"tags": [
{
"name": "USER",
"description": "OPERATIONS ABOUT USER"
}
]
}
可以直接在 API Documentation 中執行,如同 Postman 一樣,讓前端或介接的工程師直接做測試,相當方便。
參考資料:
About Swagger Specification | Documentation | Swagger
OpenAPI-Specification/3.0.2.md at master · OAI/OpenAPI-Specification (github.com)