Writing in the front

I have been using Laravel to develop API projects for several years, and I found that every time I started a new API project, I would do some pre-processing based on Laravel, including the structure design for API project, the encapsulation of unified response structure, the capture and handling of exceptions, and the configuration of authorization module, etc. You’re always doing the same thing, so just wrap these common basics into a “startup template.”

Project address: stamp here

Update the content

  • Implemented according to actual business scenarios custom response code and multi-language response described, any reference [@ mingzaily] directory structure (https://learnku.com/users/34707), the implementation of the students (2020-06-05)
  • Better support for API Resource: Usewith()and$additonalAttach additional data for use before the response is returnedwithResponse()Processing the response (2020-06-04)
  • Example of use when supplementing a successful response (2020-06-03)
  • Message prompt in canonical Reponse (2020-06-02)

Why the Lumen?

Today, in medium to large projects, front-end development is usually done in a separate way, with the front-end maintaining the project code through a separate code repository. Laravel is only responsible for the API part of the project, providing the API to the front-end for calls. In this scenario, using Laravel for the development API is a little “bloated.”

Lumen, by contrast, simplifies many parts of Laravel for the API development scenario in the project, making it more suitable for API development. With experience with Laravel, it was easy to switch to Lumen.

An overview of

  • Adapt the new HttpClient client in Laravel 7
  • Use Laravel’s native API Resource
  • Normalize a unified response structure
  • Use JWT-Auth for authorization
  • Logging to MongoDB is supported
  • Reasonable and effective “Repository & Service” architecture design (?)

Canonical response structure

Excerpt from: RESTful Services Best Practices

Code – Contains an HTTP response status code of type integer.


Status — contains text: “success”, “fail” or” error”. HTTP status response codes between 500-599 are “fail”, between 400-499 are “error”, and all others are “success” (for example, response status codes are 1XX, 2XX, and 3XX).


Message – valid when the status value is “fail” and “error”, used to display the error message. Referencing the Internationalization (IL8N) standard, it can contain either an information number or a code, it can contain only one of them, or it can contain both and separate them with delimiters.


Data — The body that contains the response. When the status value is “fail” or” error”, the data contains only the cause of the error or the name of the exception.

instructions

The overall response structure is designed with reference to the above, relatively strictly following the RESTful design criteria and returning a reasonable HTTP status code.

Given that businesses often need to return different “business description processing results,” passing in messages that match the business scenario is supported in all response structures.

  • data:

    • When querying a single piece of data, the object structure is returned directly to reduce the data hierarchy.
    • Returns an array structure when querying list data;
    • Create or update successfully, return the modified data; (You can also return an empty object without returning data)
    • An empty object is returned on successful deletion
  • status:

    • HTTP status response code is between 400-599. For example, passing in incorrect parameters, accessing non-existent data resources, and so on
    • Fail, server (backend) error, HTTP status response code between 500-599. For example, code syntax error, empty object call function, connect database failure, undefined index, etc
    • SUCCESS, with HTTP response status codes of 1XX, 2XX, and 3XX, is used to indicate successful business processing.
  • Message: Describes the result of the processing of the request operation performed; Internationalization can also be supported, switching according to actual business needs.
  • Code: HTTP response status code; It can be adjusted into business operation codes according to actual business requirements

Code implementation

<? php namespace App\Http; use Illuminate\Http\Resources\Json\JsonResource; use \Illuminate\Http\Response as HttpResponse; class Response { public function errorNotFound($message = 'Not Found') { $this->fail($message, HttpResponse::HTTP_NOT_FOUND); } /** * @param string $message * @param int $code * @param null $data * @param array $header * @param int $options * @throws \Illuminate\Http\Exceptions\HttpResponseException */ public function fail(string $message = '', int $code = HttpResponse::HTTP_INTERNAL_SERVER_ERROR, $data = null, array $header = [], int $options = 0) { $status = ($code >= 400 && $code <= 499) ? 'error' : 'fail'; $message = (! $message && isset(HttpResponse::$statusTexts[$code])) ? HttpResponse::$statusTexts[$code] : 'Service error'; response()->json([ 'status' => $status, 'code' => $code, $data => (object) $data], $code, $header, $options)->throwResponse(); } public function errorBadRequest($message = 'Bad Request') { $this->fail($message, HttpResponse::HTTP_BAD_REQUEST); } public function errorForbidden($message = 'Forbidden') { $this->fail($message, HttpResponse::HTTP_FORBIDDEN); } public function errorInternal($message = 'Internal Error') { $this->fail($message, HttpResponse::HTTP_INTERNAL_SERVER_ERROR); } public function errorUnauthorized($message = 'Unauthorized') { $this->fail($message, HttpResponse::HTTP_UNAUTHORIZED); } public function errorMethodNotAllowed($message = 'Method Not Allowed') { $this->fail($message, HttpResponse::HTTP_METHOD_NOT_ALLOWED); } public function accepted($message = 'Accepted') { return $this->success(null, $message, HttpResponse::HTTP_ACCEPTED); } /** * @param JsonResource|array|null $data * @param string $message * @param int $code * @param array $headers * @param int $option * @return \Illuminate\Http\JsonResponse|JsonResource */ public function success($data, string $message = '', $code = HttpResponse::HTTP_OK, array $headers = [], $option = 0) { $message = (! $message && isset(HttpResponse::$statusTexts[$code])) ? HttpResponse::$statusTexts[$code] : 'OK'; $additionalData = [ 'status' => 'success', 'code' => $code, 'message' => $message ]; if ($data instanceof JsonResource) { return $data->additional($additionalData); } return response()->json(array_merge($additionalData, ['data' => $data ?: (object) $data]), $code, $headers, $option); } /** * @param JsonResource|array|null $data * @param string $message * @param string $location * @return \Illuminate\Http\JsonResponse|JsonResource */ public function created($data = null, $message = 'Created', string $location = '') { $response = $this->success($data, $message, HttpResponse::HTTP_CREATED); if ($location) { $response->header('Location', $location); } return $response; } public function noContent($message = 'No content') { return $this->success(null, $message, HttpResponse::HTTP_NO_CONTENT); }}

use

Use \\App\\Traits\\Helpers to call the encapsulated Response method in \\App\\ HTTP \\Response where an HTTP Response is required.

The usual use is to respond in the Controller layer based on the results of the business processing, so a HelperStrait has been introduced in the \\App\\Http\\Controllers base class and can be called directly in the Controller as follows:

$this->response->success($data,$message); $this->response->success(new UserCollection($resource), 'success '); $this->response->success(new userResource ($user), 'success '); $this->response->success($user)); // return API Resouce $user = ["name"=>" NICKNAME ","email"=>"[email protected]"]; $this->response->success($user, 'success '); $this->response->created($data,$message); $this->response->accepted($message); $this->response->noContent($message); $this->response->fail($message); $this->response->errorNotFound(); $this->response->errorBadRequest(); $this->response->errorForbidden(); $this->response->errorInternal(); $this->response->errorUnauthorized(); $this->response->errorMethodNotAllowed();

Response structure when the operation succeeds

  • Returns a single piece of data
{ "data": { "nickname": "Jiannei", "email": "[email protected]" }, "status": "success", "code": 200, "message": "success"}
  • Return list data
{ "data": [ { "nickname": "Jiannei", "email": "[email protected]" }, { "nickname": "Qian", "email": "[email protected]" }, { "nickname": "Turbo", "email": "[email protected]" } // ... ] , "links": { "first": "http://lumen-api.test/users? page=1", "last": null, "prev": null, "next": null }, "meta": { "current_page": 1, "from": 1, "path": "Http://lumen-api.test/users", "per_page" : 15, "to" : 13}, "status" : "success", "code" : 200, the "message" : "success"}

Response structure when the operation fails

{
    "status": "fail",
    "code": 500,
    "message": "Service error",
    "data": {}
}

Response structure when exceptions are caught

The overall format is consistent with that in the case of successful and failed business operations. Compared with that in the case of failure, the Data section will add additional exception information to facilitate rapid problem localization in the project development stage.

  • Custom implementationValidationExceptionResponse structure of
{
    "status": "error",
    "code": 422,
    "message": "Validation error",
    "data": {
        "email": [
            "The email has already been taken."
        ],
        "password": [
            "The password field is required."
        ]
    }
}
  • NotFoundExceptionResponse structure for exception capture

When Debug is turned off:

{
    "status": "error",
    "code": 404,
    "message": "Service error",
    "data": {
        "message": "No query results for model [App\\Models\\User] 19"
    }
}

When debug is enabled:

{ "status": "error", "code": 404, "message": "Service error", "data": { "message": "No query results for model [App\\Models\\User] 19", "exception": "Symfony\\Component\\HttpKernel\\Exception\\NotFoundHttpException", "file": "/var/www/lumen-api-starter/vendor/laravel/lumen-framework/src/Exceptions/Handler.php", "line": 107, "trace": [ { "file": "/var/www/lumen-api-starter/app/Exceptions/Handler.php", "line": 55, "function": "render", "class": "Laravel\\Lumen\\Exceptions\\Handler", "type": "->" }, { "file": "/var/www/lumen-api-starter/vendor/laravel/lumen-framework/src/Routing/Pipeline.php", "line": 72, "function": "render", "class": "App\\Exceptions\\Handler", "type": "->" }, { "file": "/var/www/lumen-api-starter/vendor/laravel/lumen-framework/src/Routing/Pipeline.php", "line": 50, "function": "handleException", "class": "Laravel\\Lumen\\Routing\\Pipeline", "type": "->" } // ... ] }}
  • Response structure when other types of exceptions are caught
{ "status": "fail", "code": 500, "message": "syntax error, unexpected '$user' (T_VARIABLE)", "data": { "message": "syntax error, unexpected '$user' (T_VARIABLE)", "exception": "ParseError", "file": "/var/www/lumen-api-starter/app/Http/Controllers/UsersController.php", "line": 34, "trace": [ { "file": "/var/www/lumen-api-starter/vendor/composer/ClassLoader.php", "line": 322, "function": "Composer\\Autoload\\includeFile" }, { "function": "loadClass", "class": "Composer\\Autoload\\ClassLoader", "type": "->" }, { "function": "spl_autoload_call" } // ... ] }}

Note: The use of API testing tools such as Postman requires the addition of X-Requested-With: XMLHttpRequest or Accept: application/jsonheader information to show is the Api request, otherwise, the exception handling to return JSON response may not be expected.

Rich logging mode support

  • It supports logging (including business error logging and exception information captured, etc.) to MongoDB to facilitate troubleshooting of online problems
  • Log to MongoDB, with support for daily, monthly, and annual table breakdowns
  • Support for logging SQL statements

Repository & Service pattern architecture

Andersao/L5-Repository is used for project structure design, and Service layer is added.

Job description

To be added.

specification

Naming conventions: to be added

Usage specification: to be added

Packages

  • guzzlehttp/guzzle
  • jenssegers/mongodb
  • tymon/jwt-auth
  • prettus/l5-repository
  • overtrue/laravel-query-logger

other

As a general rule, if you want to help or inspire you in your daily work, please click Triple Link Star + Fork + Follow.

If you have any comments or suggestions, I can be reached by email ([email protected]) if I keep checking my email every day.

In short, welcome heroes of all kinds.

reference

  • RESTful API best practices (thanks to [@ liuqing_hu] (https://learnku.com/users/17343) provides a very detailed mind maps: + 1)
  • Best practices for RESTful services
  • DingoApi