使用场景
我们在使用laravel
来写API
时,经常需要返回一个json
字符串或JsonResponse
,通常我们的做法可能有两种。
1、在BaseController
中定义一个返回Json
响应de方法,然后继承该BaseController
。如:
//BaseController.phppublic function json($data = null, $status = 200, $headers = [], $options = 0){ return new JsonResponse($data, $status, $headers, $options);}//YourController.phpclass YourController extends BaseController{ public function users(UserRepository $userRepository) { return $this->json($userRepository->allUser()); }}
然而这写法确实挺方便,然而当你在其他地方需要使用到Json
响应时(如中间件验证失败时你想要返回一个Json
响应)。你无法使用到$this->json(...)
。
2、直接在需要用到Json
响应得地方使用return new JsonResponse
或者使用Response Facade
。
但这种做法当需要修改Response
响应时得全部改动,不可取。
Response 宏
Laravel
提供了一个非常方便的响应宏
来处理这一情况
首先,我们需要先注册一个响应宏,在任意一个ServiceProvider
的boot
方法里(ResponseMacroServiceProvider
),使用Response Facade
注册
Response::macro('success', function ($data = [], $message = 'success') { return new JsonResponse([ 'code' => 0, 'data' => $data, 'message' => $message ], 200);});
接下来, 你可以再任何地方使用它response()
。
//UserController.phppublic function users(UserRepository $userRepository){ return response()->success($userRepository->all(), 'success');}
注意,你只能通过response()
这个全局方法或是app('Illuminate\Routing\ResponseFactory')
来使用它
response()->success();//OKapp('Illuminate\Routing\ResponseFactory')->success();//OK//Response FacadeResponse::success();//ok(new \Illuminate\Http\Response)->success();//Error
原理
我们在ServiceProvider
里使用Response Facade
来注册的success
宏,我们先看看Response
这个Facade
的正真类是什么。
// Illuminate\Support\Facades.phpprotected static function getFacadeAccessor(){ return 'Illuminate\Contracts\Routing\ResponseFactory';}
该Facade
返回了一个ResponseFactory
接口,那该接口的具体实列对象时什么呢。
//Illuminate\Routing\RoutingServiceProvider.php/** * Register the response factory implementation. * * @return void */protected function registerResponseFactory(){ $this->app->singleton('Illuminate\Contracts\Routing\ResponseFactory', function ($app) { return new ResponseFactory($app['Illuminate\Contracts\View\Factory'], $app['redirect']); });}
可以看到,该RoutingServiceProvider
注册了一个Illuminate\Routing\ResponseFactory
的实列给Response Facade
。
我们在Illuminate\Routing\ResponseFactory
的源码中可以看到,它引用了一个Illuminate\Support\Traits\Macroable trait
。
namespace Illuminate\Routing;use Illuminate\Support\Traits\Macroable;class ResponseFactory implements FactoryContract{ use Macroable;}
该Trait
源码如下,看完源码就知道为什么调用response()
就能正常访问success
方法了。
trait Macroable{ protected static $macros = []; public static function macro($name, callable $macro) { static::$macros[$name] = $macro; } public static function hasMacro($name) { return isset(static::$macros[$name]); } public static function __callStatic($method, $parameters) { if (! static::hasMacro($method)) { throw new BadMethodCallException("Method {$method} does not exist."); } if (static::$macros[$method] instanceof Closure) { return call_user_func_array(Closure::bind(static::$macros[$method], null, static::class), $parameters); } return call_user_func_array(static::$macros[$method], $parameters); } public function __call($method, $parameters) { if (! static::hasMacro($method)) { throw new BadMethodCallException("Method {$method} does not exist."); } if (static::$macros[$method] instanceof Closure) { return call_user_func_array(static::$macros[$method]->bindTo($this, static::class), $parameters); } return call_user_func_array(static::$macros[$method], $parameters); }}
其实该trait Illuminate\Support\Traits\Macroable
在很多地方都有使用,包括FileSystem
、Database-Builder
。