Skip to content

Commit a1a17b8

Browse files
authored
Merge pull request #1 from hankz1108/feat/exception-handler-and-service-provider
Feat/exception handler and service provider
2 parents e4d0b81 + 8b1bfa2 commit a1a17b8

File tree

7 files changed

+279
-16
lines changed

7 files changed

+279
-16
lines changed

composer.json

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
{
22
"name": "hankz/laravel-plus-api",
3+
"description": "Convenient Laravel API response tools and automated error handling functionality are provided.",
34
"type": "library",
45
"license": "MIT",
6+
"keywords": [
7+
"laravel",
8+
"laravel-plus",
9+
"api",
10+
"response"
11+
],
512
"autoload": {
613
"psr-4": {
714
"Hankz\\LaravelPlusApi\\": "src/"
@@ -14,9 +21,14 @@
1421
}
1522
],
1623
"require": {
17-
"php": "^7.4|^8.0"
24+
"php": "^7.4|^8.0",
25+
"laravel/framework": ">=7"
1826
},
19-
"require-dev": {
20-
"laravel/framework": "7"
27+
"extra": {
28+
"laravel": {
29+
"providers": [
30+
"Hankz\\LaravelPlusApi\\LaravelPlusApiServiceProvider"
31+
]
32+
}
2133
}
22-
}
34+
}
Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<?php
22

3+
use Illuminate\Http\Response;
4+
35
return [
46
/*
57
|--------------------------------------------------------------------------
@@ -32,15 +34,25 @@
3234

3335
'default_response' => [
3436
'success' => [
35-
'api_code' => 0,
36-
'http_code' => 200,
37+
'api_code' => 200,
38+
'http_code' => Response::HTTP_OK,
3739
'message' => 'Success',
3840
],
3941

4042
'error' => [
41-
'api_code' => 1,
42-
'http_code' => 400,
43-
'message' => 'Unknown error',
43+
'api_code' => 500,
44+
'http_code' => Response::HTTP_INTERNAL_SERVER_ERROR,
45+
'message' => 'Unknown server error',
46+
],
47+
48+
'unauthenticated' => [
49+
'api_code' => 401,
50+
'http_code' => Response::HTTP_UNAUTHORIZED,
51+
'message' => 'Unauthenticated',
4452
],
45-
]
53+
54+
'validation_fail' => [
55+
'api_code' => 422,
56+
],
57+
],
4658
];

readme.md

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# Laravel Plus API
2+
3+
Convenient Laravel API response tools and automated error handling functionality are provided.
4+
5+
## Requirement
6+
7+
- PHP ^7.4|^8.0
8+
- Laravel >= 7
9+
10+
## Installation
11+
12+
### 1. Composer install
13+
Run the Composer require command from the Terminal:
14+
```bash
15+
composer require hankz/laravel-plus-api
16+
```
17+
### 2. Setup
18+
This package supports Laravel's auto-discovery feature and it's ready to use once installed.
19+
20+
### 3. Publishing the config file
21+
You need publish the config file.
22+
```bash
23+
php artisan vendor:publish --provider="Hankz\LaravelPlusApi\LaravelPlusApiServiceProvider
24+
```
25+
26+
### 4. Set Middleware
27+
Set middleware for routes where you intend to utilize API responses.
28+
29+
in `app/Http/Kernel.php`
30+
```php
31+
'api' => [
32+
//...
33+
34+
\Hankz\LaravelPlusApi\Middleware\SetHeaderAcceptJson::class,
35+
]
36+
```
37+
38+
Set the priority of the middleware.
39+
40+
in `app/Http/Kernel.php`
41+
```php
42+
protected $middlewarePriority = [
43+
\Illuminate\Session\Middleware\StartSession::class,
44+
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
45+
\Hankz\LaravelPlusApi\Middleware\SetHeaderAcceptJson::class,
46+
\Hankz\LaravelPlusApi\Exceptions\ApiException::class,
47+
\App\Http\Middleware\Authenticate::class,
48+
\Illuminate\Routing\Middleware\ThrottleRequests::class,
49+
\Illuminate\Session\Middleware\AuthenticateSession::class,
50+
\Illuminate\Routing\Middleware\SubstituteBindings::class,
51+
\Illuminate\Auth\Middleware\Authorize::class,
52+
];
53+
```
54+
55+
### 5. Exception Handler
56+
Override exception Handler.
57+
58+
in `app/Exceptions/Handler.php`,加入:
59+
```php
60+
use Illuminate\Auth\AuthenticationException;
61+
use Illuminate\Validation\ValidationException;
62+
use Symfony\Component\HttpFoundation\Response as HttpResponse;
63+
use Hankz\LaravelPlusApi\Classes\ApiResponseBuilder;
64+
65+
class Handler extends ExceptionHandler
66+
{
67+
use ApiResponse;
68+
69+
//...
70+
71+
protected function invalidJson($request, ValidationException $exception)
72+
{
73+
return ApiResponseBuilder::validationError($exception);
74+
}
75+
76+
protected function unauthenticated($request, AuthenticationException $exception)
77+
{
78+
return $this->error(config('plus-api.default_response.error.http_code'), Response::HTTP_UNAUTHORIZED);
79+
}
80+
81+
protected function prepareJsonResponse($request, Throwable $e)
82+
{
83+
return $this->exceptionError(
84+
$this->convertExceptionToArray($e),
85+
config('plus-api.default_response.error.http_code'),
86+
$this->isHttpException($e) ? $e->getStatusCode() : Response::HTTP_INTERNAL_SERVER_ERROR,
87+
$e->getMessage()
88+
$this->isHttpException($e) ? $e->getHeaders() : []
89+
);
90+
}
91+
```

src/Exceptions/ApiException.php

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php
2+
3+
namespace Hankz\LaravelPlusApi\Exceptions;
4+
5+
use Exception;
6+
use Hankz\LaravelPlusApi\Traits\ApiResponse;
7+
use Illuminate\Http\JsonResponse;
8+
use Illuminate\Http\Request;
9+
use Illuminate\Http\Response;
10+
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
11+
12+
abstract class ApiException extends Exception implements HttpExceptionInterface
13+
{
14+
use ApiResponse;
15+
16+
public const API_CODE = Response::HTTP_INTERNAL_SERVER_ERROR;
17+
18+
public const HTTP_CODE = Response::HTTP_INTERNAL_SERVER_ERROR;
19+
20+
/**
21+
* message.
22+
*
23+
* @var string
24+
*/
25+
protected $message;
26+
27+
/**
28+
* data.
29+
*
30+
* @var object|array|null data
31+
*/
32+
protected $data;
33+
34+
public function __construct(string $message = null, $data = null)
35+
{
36+
parent::__construct($message);
37+
38+
if (!is_null($message)) {
39+
$this->message = $message;
40+
}
41+
42+
if (!is_null($data)) {
43+
$this->data = $data;
44+
}
45+
}
46+
47+
/**
48+
* Report the exception.
49+
*/
50+
public function report(): bool
51+
{
52+
return true;
53+
}
54+
55+
/**
56+
* Render the exception into an HTTP response.
57+
*/
58+
public function render(Request $request): JsonResponse
59+
{
60+
return $this->error(
61+
static::API_CODE,
62+
static::HTTP_CODE,
63+
$this->message,
64+
$this->data
65+
);
66+
}
67+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
namespace Hankz\LaravelPlusApi;
4+
5+
use Hankz\LaravelPlusApi\Classes\ApiResponseBuilder;
6+
use Illuminate\Support\ServiceProvider;
7+
8+
class LaravelPlusApiServiceProvider extends ServiceProvider
9+
{
10+
/**
11+
* Register any package services.
12+
*
13+
* @return void
14+
*/
15+
public function register()
16+
{
17+
$this->mergeConfigFrom(__DIR__ . '/../config/laravel-plus-api.php', 'laravel-plus-api');
18+
}
19+
20+
/**
21+
* Get the services provided by the provider.
22+
*
23+
* @return array
24+
*/
25+
public function provides()
26+
{
27+
return [ApiResponseBuilder::class];
28+
}
29+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace Hankz\LaravelPlusApi\Middleware;
4+
5+
use Closure;
6+
7+
class SetHeaderAcceptJson
8+
{
9+
/**
10+
* Handle an incoming request.
11+
*
12+
* @param Illuminate\Http\Request $request
13+
*/
14+
public function handle($request, Closure $next)
15+
{
16+
$request->headers->set('Accept', 'application/json');
17+
18+
return $next($request);
19+
}
20+
}

src/Traits/ApiResponse.php

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Hankz\LaravelPlusApi\Classes\ApiResponseBuilder;
66
use Illuminate\Http\JsonResponse;
7+
use Illuminate\Validation\ValidationException;
78

89
trait ApiResponse
910
{
@@ -37,21 +38,52 @@ public static function success(
3738
* Builds error Response object. Supports optional arguments passed to Lang::get() if associated error
3839
* message uses placeholders as well as return data payload.
3940
*
40-
* @param string $apiCode your API code to be returned with the response object
41-
* @param string|null $message error message
42-
* @param int|null $httpCode HTTP code to be used for HttpResponse sent or @null
43-
* for default DEFAULT_HTTP_CODE_ERROR
44-
* @param int|null $headers Http Header
41+
* @param string $apiCode your API code to be returned with the response object
42+
* @param string|null $message error message
43+
* @param int|null $httpCode HTTP code to be used for HttpResponse sent or @null
44+
* for default DEFAULT_HTTP_CODE_ERROR
45+
* @param object|array|null $data array of primitives and supported objects to be returned in 'data' node
46+
* @param int|null $headers Http Header
4547
*/
4648
public static function error(
4749
string $apiCode,
48-
string $message = null,
4950
int $httpCode = null,
51+
string $message = null,
52+
$data = null,
5053
array $headers = null
5154
): JsonResponse {
5255
return ApiResponseBuilder::asError($apiCode)
5356
->withMessage($message)
5457
->withHttpCode($httpCode)
58+
->withData($data)
59+
->withHttpHeaders($headers)
60+
->build();
61+
}
62+
63+
/**
64+
* return validation error response.
65+
*/
66+
public static function validationError(ValidationException $e): JsonResponse
67+
{
68+
return ApiResponseBuilder::asError(config('plus-api.default_response.validation_fail.api_code'))
69+
->withErrors($e->errors())
70+
->withHttpCode($e->status)
71+
->build();
72+
}
73+
74+
public static function exceptionError(
75+
array $debug_data = null,
76+
string $apiCode = null,
77+
int $httpCode,
78+
string $message = null,
79+
array $headers = []
80+
): JsonResponse {
81+
$apiCode = $apiCode ?? config('plus-api.default_response.error.api_code');
82+
83+
return ApiResponseBuilder::asError($apiCode)
84+
->withDebugData($debug_data)
85+
->withHttpCode($httpCode)
86+
->withMessage($message)
5587
->withHttpHeaders($headers)
5688
->build();
5789
}

0 commit comments

Comments
 (0)