一、效果展示
最終實現(xiàn)效果如下圖所示:
支持i18n國際化數(shù)據(jù)返回泪酱,修改不同'locale' => 'zh_CN',
返回中文
'locale' => 'en',
返回英文。
當前使用版本:PHP Version 8.2.9 还最、Laravel10x墓阀。
image.png
- 有數(shù)據(jù)返回:
public function token(Request $request)
{
$request->validate([
'name' => 'required',
'password' => 'required',
'device_name' => 'required',
]);
$user = User::where('name', $request->name)->first();
if (!$user || !Hash::check($request->password, $user->password)) {
throw ValidationException::withMessages([
'email' => ['用戶名或密碼不正確!'],
]);
}
return $this->success(["token" => $user->createToken($request->device_name)->plainTextToken]);
}
{
"code": 200,
"message": "請求成功",
"data": {
"token": "49|SpUVNfkiPQhd8aY2BffAGNmN8e8ywMtHw9L0k2I97c7bf39a"
}
}
public function show(Request $request)
{
return $this->success(['user' => User::all()]);
}
{
"code": 200,
"message": "請求成功",
"data": {
"user": [
{
"id": 1,
"name": "admin",
"email": "admin@qq.com",
"email_verified_at": null,
"created_at": null,
"updated_at": null
}
]
}
}
- 無需數(shù)據(jù)返回
public function index()
{
return $this->ok();
}
{
"code": 200,
"message": "請求成功"
}
二拓轻、實現(xiàn)參考
在app目錄下斯撮,新建\Helpers\Api子目錄
image.png
代碼實現(xiàn)如下,
<?php
namespace App\Helpers\Api;
use Illuminate\Http\Response as FoundationResponse;
trait ApiResponse
{
protected $statusCode = FoundationResponse::HTTP_OK;
/**
* @return mixed
*/
public function getStatusCode()
{
return $this->statusCode;
}
/**
* @param $statusCode
* @return $this
*/
public function setStatusCode($statusCode)
{
$this->statusCode = $statusCode;
return $this;
}
/**
* 自定義發(fā)送數(shù)據(jù)
* @param $data
* @param array $header
* @return mixed
*/
public function respond($data, $header = [])
{
return Response()->json($data, $this->getStatusCode(), $header);
}
/**
* 生成發(fā)送數(shù)據(jù)
* @param string $message
* @param array $data
* @param $code
* @return mixed
*/
private function makeDataAndRespond(array $data, string $message, $code = 200)
{
$this->setStatusCode($code ?? 200);
$arrData = [
'code' => $this->statusCode,
'message' => $message
];
$ret = array_merge($arrData, ["data" => $data]);
return $this->respond($ret);
}
/**
* 僅發(fā)送消息
* @param string $message
* @param $code
* @return mixed
*/
public function message(string $message, $code = 200)
{
$this->setStatusCode($code ?? 200);
$arrData = [
'code' => $this->statusCode,
'message' => $message
];
return $this->respond($arrData);
}
/**
* 返回成功
* http_status始終為200
* code可以修改
* 不帶data參數(shù)
* @param string|NULL $message
* @param $code
* @return mixed
*/
public function ok(string $message = NULL, $code = 200)
{
$this->setStatusCode(200);
$arrData = [
'code' => $code ?? 200,
'message' => $message ?? __("http-statuses.200")
];
return $this->respond($arrData);
}
/**
* 發(fā)送數(shù)據(jù)
* @param $data
* @param string|NULL $message
* @return mixed
*/
public function success($data = null, string $message = NULL)
{
$code = FoundationResponse::HTTP_OK;
if (!is_array($data)) {
return $this->respond(['code' => $code,
'message' => $message ?? __("http-statuses." . $code),
'data' => $data]
);
}
return $this->makeDataAndRespond($data, $message ?? __("http-statuses." . $code));
}
/**
* 發(fā)送失敗消息
* @param $message
* @param $code
* @return mixed
*/
public function failed(string $message = NULL, $code = FoundationResponse::HTTP_BAD_REQUEST)
{
return $this->message($message ?? __("http-statuses." . $code), $code);
}
/**
* 已創(chuàng)建
* @param string|NULL $message
* @return mixed
*/
public function created(string $message = NULL)
{
return $this->failed($message, FoundationResponse::HTTP_CREATED);
}
public function locked(string $message = NULL)
{
return $this->failed($message, FoundationResponse::HTTP_LOCKED);
}
/**
* 內(nèi)部錯誤
* @param string|NULL $message
* @return mixed
*/
public function internalError(string $message = NULL)
{
return $this->failed($message, FoundationResponse::HTTP_INTERNAL_SERVER_ERROR);
}
/**
* 未找到
* @param string|null $message
* @return mixed
*/
public function notFound(string $message = null)
{
return $this->failed($message, Foundationresponse::HTTP_NOT_FOUND);
}
/**
* 禁止訪問
* @param string|NULL $message
* @return mixed
*/
public function forbidden(string $message = NULL)
{
return $this->failed($message, Foundationresponse::HTTP_FORBIDDEN);
}
/**
* 無內(nèi)容
* @param string|NULL $message
* @return mixed
*/
public function noContent(string $message = NULL)
{
return $this->failed($message, Foundationresponse::HTTP_NO_CONTENT);
}
/**
* 未認證
* @param string|NULL $message
* @return mixed
*/
public function unauthorized(string $message = NULL)
{
return $this->failed($message, Foundationresponse::HTTP_UNAUTHORIZED);
}
/**
* 網(wǎng)關(guān)錯誤
* @param string|NULL $message
* @return mixed
*/
public function badGateway(string $message = NULL)
{
return $this->failed($message, Foundationresponse::HTTP_BAD_GATEWAY);
}
/**
* 未知錯誤
* @param string|NULL $message
* @return mixed
*/
public function unknownError(string $message = NULL)
{
return $this->failed($message, 520);
}
/**
* 版本不支持
* @param string|NULL $message
* @return mixed
*/
public function versionNotSupported(string $message = NULL)
{
return $this->failed($message, Foundationresponse::HTTP_VERSION_NOT_SUPPORTED);
}
/**
* 連接超時
* @param string|NULL $message
* @return mixed
*/
public function connectionTimedOut(string $message = NULL)
{
return $this->failed($message, 522);
}
/**
* 已存在
* @param string|NULL $message
* @return mixed
*/
public function found(string $message = NULL)
{
return $this->failed($message, Foundationresponse::HTTP_FOUND);
}
/**
* 已沖突
* @param string|NULL $message
* @return mixed
*/
public function conflict(string $message = NULL)
{
return $this->failed($message, Foundationresponse::HTTP_CONFLICT);
}
/**
* 已不可用
* @param string|NULL $message
* @return mixed
*/
public function gone(string $message = NULL)
{
return $this->failed($message, Foundationresponse::HTTP_GONE);
}
/**
* 已接受
* @param string|NULL $message
* @return mixed
*/
public function accepted(string $message = NULL)
{
return $this->failed($message, Foundationresponse::HTTP_ACCEPTED);
}
}
修改更符合axios.js等移動端請求扶叉,建議修改以下函數(shù)勿锅。實現(xiàn)帕膜,http_status始終返回200,而code進行變化溢十。這樣統(tǒng)一一下垮刹,移動端更容易統(tǒng)一處理。
即:請求所有接口张弛,本身http狀態(tài)始終為200荒典,接口由code值變化而變化。
/**
* 自定義發(fā)送數(shù)據(jù)
* @param $data
* @param array $header
* @return mixed
*/
public function respond($data, $header = [])
{
return Response()->json($data, $this->getStatusCode(), $header);
}
修改為:
/**
* 自定義發(fā)送數(shù)據(jù)
* @param $data
* @param array $header
* @return mixed
*/
public function respond($data, $header = [])
{
return Response()->json($data, FoundationResponse::HTTP_OK, $header);
}
三吞鸭、使用
在控制器類中寺董,使用use ApiResponse;
即可。
image.png
【需要安裝
laravel-lang
】國際化支持刻剥。image.png
<?php
namespace App\Http\Controllers;
use App\Helpers\Api\ApiResponse;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\ValidationException;
class UserController extends Controller
{
use ApiResponse;
public function index()
{
return $this->ok();
}
public function show(Request $request)
{
return $this->success(['user' => User::all()]);
}
public function token(Request $request)
{
$request->validate([
'name' => 'required',
'password' => 'required',
'device_name' => 'required',
]);
$user = User::where('name', $request->name)->first();
if (!$user || !Hash::check($request->password, $user->password)) {
throw ValidationException::withMessages([
'email' => ['用戶名或密碼不正確螃征!'],
]);
}
return $this->success(["token" => $user->createToken($request->device_name)->plainTextToken]);
}
}
備注【注意事項】:Postman接口設(shè)置X-Requested-With=XMLHttpRequest
有利于調(diào)試,并且有異常都通過接口統(tǒng)一返回透敌。Lavavel框架,依賴此字段判斷是否為ajax的請求踢械,否則會返回頁面酗电。Laravel整體使用非常Nice。
image.png
- 這是uniapp中的配置參考
const BASE_URL = 'http://api.demo.test/api'
export const myRequest = (option) => {
return new Promise ((resolve, reject) => {
// 請求頭信息
const requestHeader = {
// 獲取手機C_ID 用于push動作
// #ifdef APP-PLUS
'C_ID': plus.push.getClientInfo().clientid || '',
// #endif
// 獲取用戶token
'Authorization': 'Bearer ' + uni.getStorageSync('token'),
// 做多點登錄 PC APP 可同時登錄兩個賬號
'Os-Type': 'APP',
"X-Requested-With":"XMLHttpRequest",
'accept':'application/json',
'content-type': 'application/x-www-form-urlencoded'
}
uni.request({
url: BASE_URL + option.url,
method: option.method || 'GET',
data: option.data || {},
header:requestHeader,
dataType: 'json',
success:(res) => {
if(res.statusCode !== 200) {
console.log(res)
uni.showToast({
title:res.data.message
})
return
}
resolve(res.data)
},fail: (err) => {
uni.showToast({
title: '請求接口失敗'
})
reject(err)
}
})
})
}
- 這是vue中内列,axios的配置參考
// 發(fā)送 POST 請求
axios({
url: "http://api.demo.test/api/user/token",
method: "POST",
headers: {
"X-Requested-With":"XMLHttpRequest",
'accept':'application/json',
'content-type': 'application/x-www-form-urlencoded',
},
data: {
name: "admin",
password: "123456786",
device_name:"WeX"
},
}).then((res) => {
console.log(JSON.stringify(res));
});
- 錯誤碼解釋參考:
http-statuses.php
<?php
declare(strict_types=1);
return [
'0' => '未知錯誤',
'100' => '繼續(xù)請求',
'101' => '切換協(xié)議',
'102' => '處理中',
'200' => '請求成功',
'201' => '已創(chuàng)建',
'202' => '已接受',
'203' => '非權(quán)威信息',
'204' => '無內(nèi)容',
'205' => '重置內(nèi)容',
'206' => '部分內(nèi)容',
'207' => '多狀態(tài)',
'208' => '已上報',
'226' => 'IM已使用',
'300' => '多種選擇',
'301' => '已永久移動',
'302' => '臨時移動',
'303' => '見其他',
'304' => '未修改',
'305' => '使用代理',
'307' => '臨時重定向',
'308' => '永久重定向',
'400' => '請求錯誤',
'401' => '未授權(quán)',
'402' => '需要付款',
'403' => '禁止',
'404' => '未找到',
'405' => '方法不允許',
'406' => '無法接受',
'407' => '需要代理驗證',
'408' => '請求超時',
'409' => '沖突',
'410' => '不可用',
'411' => '長度要求',
'412' => '前提條件未滿足',
'413' => '請求實體過大',
'414' => 'URI太長了',
'415' => '不支持的媒體類型',
'416' => '請求范圍不符合',
'417' => '期望不滿足',
'418' => '我是一個茶壺',
'419' => '會話已過期',
'421' => '錯誤的請求',
'422' => '不可處理的實體',
'423' => '鎖定',
'424' => '失敗的依賴',
'425' => '太早了',
'426' => '需要升級',
'428' => '前提要求',
'429' => '請求太多',
'431' => '請求標頭字段太大',
'444' => '連接關(guān)閉無響應(yīng)',
'449' => '重試',
'451' => '法律原因不可用',
'499' => '客戶端關(guān)閉請求',
'500' => '內(nèi)部服務(wù)器錯誤',
'501' => '未實現(xiàn)',
'502' => '網(wǎng)關(guān)錯誤',
'503' => '服務(wù)不可用',
'504' => '網(wǎng)關(guān)超時',
'505' => 'HTTP版本不支持',
'506' => '變體協(xié)商',
'507' => '存儲空間不足',
'508' => '檢測到環(huán)路',
'509' => '超出帶寬限制',
'510' => '未延期',
'511' => '需要網(wǎng)絡(luò)驗證',
'520' => '未知錯誤',
'521' => 'Web服務(wù)器已關(guān)閉',
'522' => '連接超時',
'523' => '原點無法到達',
'524' => '發(fā)生超時',
'525' => 'SSL握手失敗',
'526' => '無效的SSL證書',
'527' => '軌道炮錯誤',
'598' => '網(wǎng)絡(luò)讀取超時',
'599' => '網(wǎng)絡(luò)連接超時',
'unknownError' => '未知錯誤',
];
zh_CN.json
{
"(and :count more error)": "(還有 :count 個錯誤)",
"(and :count more errors)": "(以及另外 :count 個錯誤)",
"A Timeout Occurred": "發(fā)生超時",
"Accept": "接受",
"Accepted": "已接受",
"Action": "操作",
"Actions": "操作",
"Add": "添加",
"Admin": "行政",
"Agree": "同意",
"All rights reserved.": "版權(quán)所有撵术。",
"Already Reported": "已上報",
"Archive": "檔案",
"Assign": "分配",
"Attach": "附加",
"Bad Gateway": "網(wǎng)關(guān)錯誤",
"Bad Request": "請求錯誤",
"Bandwidth Limit Exceeded": "超出帶寬限制",
"Browse": "瀏覽",
"Cancel": "取消",
"Choose": "選擇",
"Choose :name": "選擇:name",
"Choose File": "選擇文件",
"Choose Image": "選擇圖片",
"Click to copy": "點擊復(fù)制",
"Client Closed Request": "客戶端關(guān)閉請求",
"Close": "關(guān)閉",
"Collapse": "坍塌",
"Collapse All": "全部收縮",
"Comment": "評論",
"Confirm": "確認",
"Conflict": "沖突",
"Connect": "連接",
"Connection Closed Without Response": "連接關(guān)閉無響應(yīng)",
"Connection Timed Out": "連接超時",
"Continue": "繼續(xù)請求",
"Create": "創(chuàng)建",
"Created": "已創(chuàng)建",
"Delete": "刪除",
"Detach": "分離",
"Details": "詳情",
"Disable": "禁用",
"Discard": "丟棄",
"Done": "完畢",
"Down": "向下",
"Duplicate": "復(fù)制",
"Duplicate: name": "重復(fù):名稱",
"Edit": "編輯",
"Edit :name": "編輯:name",
"Enable": "啟用",
"Expand": "擴張",
"Expand All": "展開全部",
"Expectation Failed": "期望不滿足",
"Explanation": "解釋",
"Export": "出口",
"Failed Dependency": "失敗的依賴",
"File": "文件",
"Files": "文件",
"Forbidden": "訪問被拒絕",
"Found": "臨時移動",
"Gateway Timeout": "網(wǎng)關(guān)超時",
"Go Home": "回首頁",
"Go to page :page": "前往第 :page 頁",
"Gone": "不可用",
"Hello!": "您好!",
"Hide": "隱藏",
"Hide :name": "隱藏 :name",
"Home": "家",
"HTTP Version Not Supported": "HTTP版本不支持",
"I'm a teapot": "我是一個茶壺",
"If you did not create an account, no further action is required.": "如果您未注冊帳號话瞧,請忽略此郵件嫩与。",
"If you did not request a password reset, no further action is required.": "如果您未申請重設(shè)密碼,請忽略此郵件交排。",
"If you're having trouble clicking the \":actionText\" button, copy and paste the URL below\ninto your web browser:": "如果您單擊「:actionText」按鈕時遇到問題划滋,請復(fù)制下方鏈接到瀏覽器中訪問:",
"IM Used": "IM已使用",
"Image": "圖像",
"Impersonate": "模擬登錄",
"Impersonation": "冒充",
"Import": "進口",
"Import :name": "進口 :name",
"Insufficient Storage": "存儲空間不足",
"Internal Server Error": "內(nèi)部服務(wù)器錯誤",
"Introduction": "介紹",
"Invalid JSON was returned from the route.": "從路由返回?zé)o效的 JSON。",
"Invalid SSL Certificate": "無效的SSL證書",
"Length Required": "長度要求",
"Like": "喜歡",
"Load": "加載",
"Localize": "本地化",
"Locked": "鎖定",
"Login": "登錄",
"Logout": "登出",
"Loop Detected": "檢測到環(huán)路",
"Maintenance Mode": "服務(wù)不可用",
"Method Not Allowed": "方法不允許",
"Misdirected Request": "錯誤的請求",
"Moved Permanently": "已永久移動",
"Multi-Status": "多狀態(tài)",
"Multiple Choices": "多種選擇",
"Network Authentication Required": "需要網(wǎng)絡(luò)驗證",
"Network Connect Timeout Error": "網(wǎng)絡(luò)連接超時",
"Network Read Timeout Error": "網(wǎng)絡(luò)讀取超時",
"New": "新建",
"New :name": "新 :name",
"No": "不",
"No Content": "無內(nèi)容",
"Non-Authoritative Information": "非權(quán)威信息",
"Not Acceptable": "無法接受",
"Not Extended": "未延期",
"Not Found": "頁面不存在",
"Not Implemented": "未實現(xiàn)",
"Not Modified": "未修改",
"of": "于",
"OK": "請求成功",
"Open": "打開",
"Open in a current window": "在當前窗口中打開",
"Open in a new window": "在新窗口中打開",
"Open in a parent frame": "在父框架中打開",
"Open in the topmost frame": "在最上面的框架中打開",
"Open on the website": "在網(wǎng)站上打開",
"Origin Is Unreachable": "原點無法到達",
"Page Expired": "頁面會話已超時",
"Pagination Navigation": "分頁導(dǎo)航",
"Partial Content": "部分內(nèi)容",
"Payload Too Large": "請求實體過大",
"Payment Required": "需要付款",
"Permanent Redirect": "永久重定向",
"Please click the button below to verify your email address.": "請點擊下面按鈕驗證您的 E-mail:",
"Precondition Failed": "前提條件未滿足",
"Precondition Required": "前提要求",
"Preview": "預(yù)覽",
"Price": "價格",
"Processing": "處理中",
"Proxy Authentication Required": "需要代理驗證",
"Railgun Error": "軌道炮錯誤",
"Range Not Satisfiable": "請求范圍不符合",
"Regards": "致敬",
"Register": "注冊",
"Request Header Fields Too Large": "請求標頭字段太大",
"Request Timeout": "請求超時",
"Reset Content": "重置內(nèi)容",
"Reset Password": "重置密碼",
"Reset Password Notification": "重置密碼通知",
"Restore": "恢復(fù)",
"Restore :name": "恢復(fù):name",
"results": "結(jié)果",
"Retry With": "重試",
"Save": "保存",
"Save & Close": "保存并關(guān)閉",
"Save & Return": "保存并返回",
"Save :name": "節(jié)省 :name",
"Search": "搜索",
"Search :name": "搜索 :name",
"See Other": "見其他",
"Select": "選擇",
"Select All": "全選",
"Send": "發(fā)送",
"Server Error": "服務(wù)器錯誤",
"Service Unavailable": "服務(wù)不可用",
"Session Has Expired": "會話已過期",
"Settings": "設(shè)置",
"Show": "展示",
"Show :name": "顯示 :name",
"Show All": "顯示所有",
"Showing": "顯示中",
"Solve": "解決",
"SSL Handshake Failed": "SSL握手失敗",
"Submit": "提交",
"Subscribe": "訂閱",
"Switch": "轉(zhuǎn)變",
"Switch To Role": "切換角色",
"Switching Protocols": "切換協(xié)議",
"Tag": "標簽",
"Tags": "標簽",
"Temporary Redirect": "臨時重定向",
"The given data was invalid.": "給定的數(shù)據(jù)無效埃篓。",
"The response is not a streamed response.": "該響應(yīng)不是流式響應(yīng)处坪。",
"The response is not a view.": "響應(yīng)不是視圖。",
"This password reset link will expire in :count minutes.": "這個重設(shè)密碼鏈接將會在 :count 分鐘后失效架专。",
"to": "至",
"Toggle navigation": "切換導(dǎo)航",
"Too Early": "太早了",
"Too Many Requests": "請求次數(shù)過多同窘。",
"Translate": "翻譯",
"Translate It": "翻譯它",
"Unauthorized": "未授權(quán)",
"Unavailable For Legal Reasons": "法律原因不可用",
"Unknown Error": "未知錯誤",
"Unpack": "打開包裝",
"Unprocessable Entity": "不可處理的實體",
"Unsubscribe": "退訂",
"Unsupported Media Type": "不支持的媒體類型",
"Up": "向上",
"Update": "更新",
"Update :name": "更新:name",
"Upgrade Required": "需要升級",
"URI Too Long": "URI太長了",
"Use Proxy": "使用代理",
"User": "用戶",
"Variant Also Negotiates": "變體協(xié)商",
"Verify Email Address": "驗證 E-mail",
"View": "查看",
"View :name": "查看 :name",
"Web Server is Down": "Web服務(wù)器已關(guān)閉",
"Whoops!": "哎呀!",
"Yes": "是的",
"You are receiving this email because we received a password reset request for your account.": "您收到此電子郵件是因為我們收到了您帳戶的密碼重設(shè)請求部脚。"
}