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 LaravelApi Resource,Api Resource Collection,Paginator(simple paging),LengthAwarePaginator(Normal paging),Eloquent\Model,Eloquent\Collection, and simplearraystringAnd so on format data return
  • Results and use after paging data formattingleague/fractalThe 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 quicksuccessfailFormatting method
  • A successful response may include:User::all(),User::first(),UserResource,UserCollection,User::paginate(),User::simplePaginate()CollectionAnd ordinaryArrayAnd 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