preface
After the introduction of lumen-API-starter, received a lot of attention and feedback, first in this thank you friends meng 🤟
Lumen-api-starter lumen 8 + API Starter lumen 8 + API Starter . On the basis of the previous article, the package is reorganized and independently developed, which can support the latest Laravel and Lumen projects at the same time.
Package address: laravel-Response laravel version Api development initialization project: Laravel-api-starter Lumen version Api development initialization project: Lumen-api-starter
Getting back to the point, before writing an API project in Laravel or Lumen, it’s common to define a few project specifications to make the subsequent development experience more comfortable. These include:
- Standardize unified response data structures: successful operations, failed operations, and abnormal operation responses
- Use enumerations to manage constants in your project, reducing bugs and improving scalability
- Log more effectively to improve troubleshooting efficiency online
- The other. . (Planning)
The implementation process
Best practices for RESTful services: How to design Http status codes and data return formats.
Train of thought
- Follow Laravel’s thinking as far as possible to expand and conform to certain norms
- Install as few dependencies as possible, preferably zero dependencies, without additional burden
- Unit testing as well as possible to ensure code quality (for examples, skip the introduction below and go directly to github.com/Jiannei/lar… Test case)
- Implementation needs to be simple, use needs to be elegant
function
- Unified data response format, fixed to include:
code
,status
,data
,message
,error
- Built-in SUPPORT for Http standard status codes and support for extending ResponseCodeEnum to define response codes for different business modules
- Message of response code COED supports localization and configuration of multiple languages
- Return the Http status code reasonably
- Return and verify abnormal information based on the debugging function
- Supports formatting Laravel
Api Resource
,Api Resource Collection
,Paginator
(simple paging),LengthAwarePaginator
(Normal paging),Eloquent\Model
,Eloquent\Collection
, and simplearray
和string
And so on format data return - Results and use after paging data formatting
league/fractal
The transformer (DingoApi uses this extension for data conversion) converts to a consistent format, that is, it smoothly switches from the Laravel Api Resource toleague/fractal
specification
- Appropriate Http status codes enable clients/browsers to better understand Http responses
- Fixed format
{
"status": "success".// Describe the HTTP response result: the HTTP status response code between 500 and 599 is "FAIL", the HTTP status response code between 400 and 499 is "error", and the rest is "success".
"code": 200.// Contains an integer HTTP response status code, which can also be a service description opcode. For example, 200001 indicates successful registration
"message": "Operation successful".// Multilingual response description
"data": {// Actual response data
"nickname": "Joaquin Ondricka"."email": "[email protected]"
},
"error": {}// Debugging information when exceptions occur
}
Copy the code
demand
Responding to API Json format data in Laravel without using any package usually looks like this:
return response()->json($data.$status.$headers.$options);
Copy the code
However, in a real development scenario, there are many data return requirements:
- More often it’s just simple success and failure responses, so there needs to be quick
success
和fail
Formatting method - A successful response may include:
User::all()
,User::first()
,UserResource
,UserCollection
,User::paginate()
,User::simplePaginate()
、Collection
And ordinaryArray
And so on, hoping that these different types of data can be formatted into a uniform structure - The failure response usually returns different error codes and error descriptions according to different service scenarios
- Exception response: For exceptions such as form authentication and Http, different responses can be made based on whether debugging is enabled, and the format is the same as before
Define the business opcode
namespace App\Repositories\Enums;
use Jiannei\Response\Laravel\Repositories\Enums\ResponseCodeEnum as BaseResponseCodeEnum;
class ResponseCodeEnum extends BaseResponseCodeEnum
{
// Correct service operation code: starts with 1xx, 2xx, and 3XX, and concatenates three digits
// 200 + 001 => 200001, that is, 001 to 999 numbers can be used to indicate service success. Of course, you can increase the number based on actual requirements, but the number must start with 200
// For example, you can define 001 to 099 as the system state; 100 to 199 indicates the authorization service. 200 to 299 indicates user services. .
const SERVICE_REGISTER_SUCCESS = 200101;
const SERVICE_LOGIN_SUCCESS = 200102;
// The client error code starts with 400 to 499 digits
const CLIENT_PARAMETER_ERROR = 400001;
const CLIENT_CREATED_ERROR = 400002;
const CLIENT_DELETED_ERROR = 400003;
const CLIENT_VALIDATION_ERROR = 422001; // Form validation error
// Server operation error code: 500 to 599, followed by three bits
const SYSTEM_ERROR = 500001;
const SYSTEM_UNAVAILABLE = 500002;
const SYSTEM_CACHE_CONFIG_ERROR = 500003;
const SYSTEM_CACHE_MISSED_ERROR = 500004;
const SYSTEM_CONFIG_ERROR = 500005;
// Business operation error code (external service or internal service invocation. ..)
const SERVICE_REGISTER_ERROR = 500101;
const SERVICE_LOGIN_ERROR = 500102;
}
Copy the code
Localized opcode description
// resources/lang/zh-CN/enums.php
use App\Repositories\Enums\ResponseCodeEnum;
return [
// Response status code
ResponseCodeEnum::class => [
/ / success
ResponseCodeEnum::HTTP_OK => 'Operation successful'.// Customize the HTTP status code return message
ResponseCodeEnum::HTTP_INTERNAL_SERVER_ERROR => 'Operation failed'.// Customize the HTTP status code return message
ResponseCodeEnum::HTTP_UNAUTHORIZED => 'Authorization failed'.// The service operation succeeds
ResponseCodeEnum::SERVICE_REGISTER_SUCCESS => 'Registration successful',
ResponseCodeEnum::SERVICE_LOGIN_SUCCESS => 'Login successful'.// Client error
ResponseCodeEnum::CLIENT_PARAMETER_ERROR => 'Parameter error',
ResponseCodeEnum::CLIENT_CREATED_ERROR => 'Data already exists',
ResponseCodeEnum::CLIENT_DELETED_ERROR => 'Data does not exist',
ResponseCodeEnum::CLIENT_VALIDATION_ERROR => 'Form validation error'.// Server error
ResponseCodeEnum::SYSTEM_ERROR => 'Server error',
ResponseCodeEnum::SYSTEM_UNAVAILABLE => 'Server is currently unavailable for maintenance',
ResponseCodeEnum::SYSTEM_CACHE_CONFIG_ERROR => 'Cache configuration error',
ResponseCodeEnum::SYSTEM_CACHE_MISSED_ERROR => 'Cache missed',
ResponseCodeEnum::SYSTEM_CONFIG_ERROR => 'System configuration error'.// Service operation failure: Authorize the service
ResponseCodeEnum::SERVICE_REGISTER_ERROR => 'Registration failed',
ResponseCodeEnum::SERVICE_LOGIN_ERROR => 'Login failed',]];Copy the code
Use the sample
A successful response
- The sample code
public function index()
{
$users = User::all();
return Response::success(new UserCollection($users));
}
public function paginate()
{
$users = User::paginate(5);
return Response::success(new UserCollection($users));
}
public function simplePaginate()
{
$users = User::simplePaginate(5);
return Response::success(new UserCollection($users));
}
public function item()
{
$user = User::first();
return Response::success(new UserResource($user));
}
public function array()
{
return Response::success([
'name'= >'Jiannel'.'email'= >'[email protected]'].' ', ResponseCodeEnum::SERVICE_REGISTER_SUCCESS);
}
Copy the code
- Return all data
{
"status": "success"."code": 200."message": "Operation successful"."data": [{"nickname": "Joaquin Ondricka"."email": "[email protected]"
},
{
"nickname": "Jermain D'Amore"."email": "[email protected]"
},
{
"nickname": "Erich Moore"."email": "[email protected]"}]."error": {}}Copy the code
- Paging data
{
"status": "success"."code": 200."message": "Operation successful"."data": {
"data": [{"nickname": "Joaquin Ondricka"."email": "[email protected]"
},
{
"nickname": "Jermain D'Amore"."email": "[email protected]"
},
{
"nickname": "Erich Moore"."email": "[email protected]"
},
{
"nickname": "Eva Quitzon"."email": "[email protected]"
},
{
"nickname": "Miss Gail Mitchell"."email": "[email protected]"}]."meta": {
"pagination": {
"count": 5."per_page": 5."current_page": 1."total": 12."total_pages": 3."links": {
"previous": null."next": "http://laravel-api.test/api/users/paginate? page=2"}}}},"error": {}}Copy the code
- Returns simple paging data
{
"status": "success"."code": 200."message": "Operation successful"."data": {
"data": [{"nickname": "Joaquin Ondricka"."email": "[email protected]"
},
{
"nickname": "Jermain D'Amore"."email": "[email protected]"
},
{
"nickname": "Erich Moore"."email": "[email protected]"
},
{
"nickname": "Eva Quitzon"."email": "[email protected]"
},
{
"nickname": "Miss Gail Mitchell"."email": "[email protected]"}]."meta": {
"pagination": {
"count": 5."per_page": 5."current_page": 1."links": {
"previous": null."next": "http://laravel-api.test/api/users/simple-paginate? page=2"}}}},"error": {}}Copy the code
- Returns a single piece of data
{
"status": "success"."code": 200."message": "Operation successful"."data": {
"nickname": "Joaquin Ondricka"."email": "[email protected]"
},
"error": {}}Copy the code
Other shortcuts
Response::accepted();
Response::created();
Response::noContent();
Copy the code
Failure response
Do not specify a meesage
public function fail()
{
Response::fail();// Return is not required
}
Copy the code
- No multilingual response description is configured. Data is returned
{
"status": "fail"."code": 500."message": "Http internal server error"."data": {},
"error": {}}Copy the code
- After the multi-language description is configured, data is returned
{
"status": "fail"."code": 500."message": "Operation failed"."data": {},
"error": {}}Copy the code
Specified message
public function fail()
{
Response::fail('error');// Return is not required
}
Copy the code
Return the data
{
"status": "fail"."code": 500."message": "error"."data": {},
"error": {}}Copy the code
Specify the code
public function fail()
{
Response::fail(' ',ResponseCodeEnum::SERVICE_LOGIN_ERROR);
}
Copy the code
Return the data
{
"status": "fail"."code": 500102."message": "Login failed"."data": {},
"error": {}}Copy the code
Other shortcuts
Response::errorBadRequest();
Response::errorUnauthorized();
Response::errorForbidden();
Response::errorNotFound();
Response::errorMethodNotAllowed();
Response::errorInternal();
Copy the code
Abnormal response
For abnormal data formatting, extra in app/Exceptions/Handler. Introduce the use PHP Jiannei \ Response \ Laravel, Support, Traits, ExceptionTrait; Since its introduction, exceptions generated by Ajax requests are returned with formatted data.
(Lumen to achieve the same effect, still need in Http/app/Controllers/Controller. Introduced in PHP ExceptionTrait)
- Form validation exception
{
"status": "error"."code": 422."message": "Verification failed"."data": {},
"error": {
"email": [
"The email field is required."]}}Copy the code
- An exception thrown outside Controller is returned
You can throw an HttpException directly using the abort helper function
abort(ResponseCodeEnum::SERVICE_LOGIN_ERROR);
// Return data
{
"status": "fail"."code": 500102."message": "Login failed"."data": {},
"error": {}}Copy the code
- Other exceptions
Open the debug
{
"status": "error"."code": 404."message": "Http not found"."data": {},
"error": {
"message": ""."exception": "Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException"."file": "/home/vagrant/code/laravel-api-starter/vendor/laravel/framework/src/Illuminate/Routing/AbstractRouteCollection.php"."line": 43."trace": [{"file": "/home/vagrant/code/laravel-api-starter/vendor/laravel/framework/src/Illuminate/Routing/RouteCollection.php"."line": 162."function": "handleMatchedRoute"."class": "Illuminate\\Routing\\AbstractRouteCollection"."type": "- >"
},
{
"file": "/home/vagrant/code/laravel-api-starter/vendor/laravel/framework/src/Illuminate/Routing/Router.php"."line": 646."function": "match"."class": "Illuminate\\Routing\\RouteCollection"."type": "- >"},... ] }}Copy the code
To close the debug
{
"status": "error"."code": 404."message": "Http not found"."data": {},
"error": {}}Copy the code
One more thing?
As a reminder, these wraps are all based onResponse ()-> JSON (), which returns a JsonResponse object, so we can still chain call methods on that object.
// Set the HTTP response code
return Response::success(new UserResource($user))->setStatusCode(ResponseCodeEnum::HTTP_CREATED);
Copy the code
other
As usual, if you have any help or inspiration for your daily work, welcome to star + fork + Follow.
If you have any criticism or suggestions, please contact me by email ([email protected]).
In a word, welcome all heroes and heroics.
QQ group: 1105120693