Laravel API 连接到多个数据库
Laravel API connecting to multiple databases
我正在使用 Laravel (Lumen) 构建 REST API。这个想法是 API 为多个订餐网站提供后端。它们共享相同的后端逻辑(模型、控制器等)。这样每个网站只需要它自己的前端应用程序,我打算为此使用 Angular。每个网站都有自己的数据(产品、页面等),这些数据必须存储在不同的数据库中。
出于测试目的,我在 config/databases.php
中定义了多个连接。现在我可以在查询相应的数据库之前动态设置连接,像这样:
class ProductController extends Controller
{
/**
* Display a listing of the resource.
*
* @return Response
*/
public function index()
{
$products = new Product;
$products->setConnection('customer_two'); // <--
$products = $products->get();
return response()->json($products);
}
}
例如,缓存也可以做到这一点。
让 API 知道哪个客户的网站发出了请求的最佳方式是什么?我需要指向正确的数据库。另外,这种方法是否会在性能方面造成任何问题?
我会使用双管齐下的方法来解决这个问题,我会使用第一个而不使用第二个。
第一个会根据你使用的路由请求api。例如,您可以使用 /api/{site}
这样的前缀来定义您的路由。这样,您所有的 api 端点都将基于请求的站点变量。例如。 /api/site1/login
将使用数据库 site1
而 /api/site2/login
将使用数据库 site2
.
第二部分是像您上面提到的那样使用 JWT 进行身份验证,并且在每个请求中使用中间件来检查经过身份验证的用户是否实际上是该特定站点用户的一部分。这只对经过身份验证的路由非常有用,但是仍然会让您的未经身份验证的路由容易被滥用,但是如果合法用户在您的站点上并且您的站点正在请求来自 api 的数据,您应该有正确的 site
数据被返回,任何恶意访问都只会获取 public 数据。
可能还有第三种选择。使用 JWT,您可以创建自定义声明。这些自定义声明可用于存储正在使用的站点以及要访问的数据库。我自己还没有这样做,但一直在考虑做一些类似的事情来根据我的 api 对客户端进行身份验证,并在此基础上进行基于用户的身份验证。这意味着每个端点至少要经过客户端身份验证,其他端点也将经过用户身份验证和客户端身份验证。
使用中间件在运行时轻松更改数据库连接。
中间件:app/Http/Middleware/DatabaseConnectionChooser.php
<?php namespace App\Http\Middleware;
use Closure;
use Illuminate\Routing\Route;
class DatabaseConnectionChooser
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
/** @var Route $route */
$route = app('router')->getRoutes()->match($request);
$connection = $route->getParameter('connection');
app('db')->setDefaultConnection($connection);
return $next($request);
}
}
将此中间件添加到 app/Http/Kernel.php class 到 $middleware 属性.
protected $middleware = [
...
\App\Http\Middleware\DatabaseConnectionChooser::class,
];
创建您的路线以指定站点,也就是数据库连接。
app/Http/routes.php
app('router')->get('/{connection}/', function () {
return app('db')->getDefaultConnection();
});
在配置中设置数据库连接。
config/database.php
'connections' => [
...
'site1' => [
'driver' => 'mysql',
'host' => env('DB_HOST', 'localhost'),
'database' => env('DB_DATABASE', 'forge1'),
'username' => env('DB_USERNAME', 'forge1'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => false,
],
'site2' => [
'driver' => 'mysql',
'host' => env('DB_HOST', 'localhost'),
'database' => env('DB_DATABASE', 'forge2'),
'username' => env('DB_USERNAME', 'forge2'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => false,
],
...
]
我正在使用 Laravel (Lumen) 构建 REST API。这个想法是 API 为多个订餐网站提供后端。它们共享相同的后端逻辑(模型、控制器等)。这样每个网站只需要它自己的前端应用程序,我打算为此使用 Angular。每个网站都有自己的数据(产品、页面等),这些数据必须存储在不同的数据库中。
出于测试目的,我在 config/databases.php
中定义了多个连接。现在我可以在查询相应的数据库之前动态设置连接,像这样:
class ProductController extends Controller
{
/**
* Display a listing of the resource.
*
* @return Response
*/
public function index()
{
$products = new Product;
$products->setConnection('customer_two'); // <--
$products = $products->get();
return response()->json($products);
}
}
例如,缓存也可以做到这一点。
让 API 知道哪个客户的网站发出了请求的最佳方式是什么?我需要指向正确的数据库。另外,这种方法是否会在性能方面造成任何问题?
我会使用双管齐下的方法来解决这个问题,我会使用第一个而不使用第二个。
第一个会根据你使用的路由请求api。例如,您可以使用 /api/{site}
这样的前缀来定义您的路由。这样,您所有的 api 端点都将基于请求的站点变量。例如。 /api/site1/login
将使用数据库 site1
而 /api/site2/login
将使用数据库 site2
.
第二部分是像您上面提到的那样使用 JWT 进行身份验证,并且在每个请求中使用中间件来检查经过身份验证的用户是否实际上是该特定站点用户的一部分。这只对经过身份验证的路由非常有用,但是仍然会让您的未经身份验证的路由容易被滥用,但是如果合法用户在您的站点上并且您的站点正在请求来自 api 的数据,您应该有正确的 site
数据被返回,任何恶意访问都只会获取 public 数据。
可能还有第三种选择。使用 JWT,您可以创建自定义声明。这些自定义声明可用于存储正在使用的站点以及要访问的数据库。我自己还没有这样做,但一直在考虑做一些类似的事情来根据我的 api 对客户端进行身份验证,并在此基础上进行基于用户的身份验证。这意味着每个端点至少要经过客户端身份验证,其他端点也将经过用户身份验证和客户端身份验证。
使用中间件在运行时轻松更改数据库连接。
中间件:app/Http/Middleware/DatabaseConnectionChooser.php
<?php namespace App\Http\Middleware;
use Closure;
use Illuminate\Routing\Route;
class DatabaseConnectionChooser
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
/** @var Route $route */
$route = app('router')->getRoutes()->match($request);
$connection = $route->getParameter('connection');
app('db')->setDefaultConnection($connection);
return $next($request);
}
}
将此中间件添加到 app/Http/Kernel.php class 到 $middleware 属性.
protected $middleware = [
...
\App\Http\Middleware\DatabaseConnectionChooser::class,
];
创建您的路线以指定站点,也就是数据库连接。
app/Http/routes.php
app('router')->get('/{connection}/', function () {
return app('db')->getDefaultConnection();
});
在配置中设置数据库连接。
config/database.php
'connections' => [
...
'site1' => [
'driver' => 'mysql',
'host' => env('DB_HOST', 'localhost'),
'database' => env('DB_DATABASE', 'forge1'),
'username' => env('DB_USERNAME', 'forge1'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => false,
],
'site2' => [
'driver' => 'mysql',
'host' => env('DB_HOST', 'localhost'),
'database' => env('DB_DATABASE', 'forge2'),
'username' => env('DB_USERNAME', 'forge2'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => false,
],
...
]