diff --git a/src/Api/ApiServiceProvider.php b/src/Api/ApiServiceProvider.php
index eeaa666e775ebc1e38178f1b3e78123f63de1c05..e3c2c8ab006e8f581d942587a86ec71839c2d5c0 100644
--- a/src/Api/ApiServiceProvider.php
+++ b/src/Api/ApiServiceProvider.php
@@ -130,33 +130,14 @@ class ApiServiceProvider extends ServiceProvider
require $this->app->basePath('routes/api.php');
});
} else if ($this->matchPrefix($reqUri, $pluginApiPrefix)) {
- $this->setPluginRoutes($route, $pluginName);
+// $this->setPluginRoutes($route,$plugins, $pluginName);
} else {
$route->group($userApiPrefix, function (RouteCollection $route) {
require $this->app->basePath('routes/api.php');
});
}
- }
-
-
- private function setPluginRoutes(RouteCollection $route, $pluginName)
- {
- $plugins = \Discuz\Common\Utils::getPluginList();
- $plugin = array_filter($plugins, function ($item) use ($pluginName) {
- return strtolower($item['name_en']) == strtolower($pluginName);
- });
- $plugin = current($plugin);
- if (empty($plugin)) exit('plugin ' . $pluginName . ' not exist.');
- $prefix = '/plugin/' . $plugin['name_en'] . '/api/';
- $route->group($prefix, function (RouteCollection $route) use ($plugin) {
- $pluginFiles = $plugin['plugin_' . $plugin['app_id']];
- Utils::setPluginAppId($plugin['app_id']);
- if (isset($pluginFiles['routes'])) {
- foreach ($pluginFiles['routes'] as $routeFile) {
- require_once $routeFile;
- }
- }
- });
+ \Discuz\Common\Utils::includePluginRoutes($route);
+ \Discuz\Common\Utils::setRouteMap($route->getRouteData());
}
private function matchPrefix($uri, $prefix)
diff --git a/src/Common/Utils.php b/src/Common/Utils.php
index f5fca08fece29d19146410c1669b96cd2b776d7d..4d2c461b5f10a610fa90152488629f6a3056f6f6 100644
--- a/src/Common/Utils.php
+++ b/src/Common/Utils.php
@@ -7,6 +7,7 @@ use App\Common\DzqConst;
use App\Common\ResponseCode;
use Discuz\Base\DzqCache;
use Discuz\Base\DzqLog;
+use Discuz\Http\RouteCollection;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Discuz\Http\DiscuzResponseFactory;
@@ -249,6 +250,27 @@ class Utils
return $plugins;
}
+ /**
+ * @desc 一次性加载所有插件的路由文件
+ * @param RouteCollection $route
+ * @return RouteCollection
+ */
+ public static function includePluginRoutes(RouteCollection &$route){
+ $plugins = self::getPluginList();
+ foreach ($plugins as $plugin) {
+ $prefix = '/plugin/' . $plugin['name_en'] . '/api/';
+ $route->group($prefix, function (RouteCollection $route) use ($plugin) {
+ $pluginFiles = $plugin['plugin_' . $plugin['app_id']];
+ \App\Common\Utils::setPluginAppId($plugin['app_id']);
+ if (isset($pluginFiles['routes'])) {
+ foreach ($pluginFiles['routes'] as $routeFile) {
+ require_once $routeFile;
+ }
+ }
+ });
+ }
+ return $route;
+ }
public static function runConsoleCmd($cmd, $params)
{
$reader = function & ($object, $property) {
@@ -406,4 +428,28 @@ class Utils
mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
);
}
+
+ public static function setAppKey($key, $value)
+ {
+ return app()->instance($key, $value);
+ }
+
+ public static function getAppKey($key)
+ {
+ if (app()->has($key)) {
+ return app()->get($key);
+ }
+ return null;
+ }
+
+ public static function setRouteMap($data)
+ {
+ return self::setAppKey('dzq_boot_route_data', $data);
+ }
+
+ public static function getRouteMap()
+ {
+ return self::getAppKey('dzq_boot_route_data');
+ }
+
}
diff --git a/src/Http/GroupCountBased.php b/src/Http/GroupCountBased.php
new file mode 100644
index 0000000000000000000000000000000000000000..87c1222641cc90a1ec2e723b6a6ff3be58340490
--- /dev/null
+++ b/src/Http/GroupCountBased.php
@@ -0,0 +1,57 @@
+staticRouteMap, $this->variableRouteData) = $data;
+ }
+
+ protected function dispatchVariableRoute($routeData, $uri)
+ {
+ foreach ($routeData as $data) {
+ if (!preg_match($data['regex'], $uri, $matches)) {
+ continue;
+ }
+
+ list($handler, $varNames) = $data['routeMap'][count($matches)];
+
+ $vars = [];
+ $i = 0;
+ foreach ($varNames as $varName) {
+ $vars[$varName] = $matches[++$i];
+ }
+ return [self::FOUND, $handler, $vars];
+ }
+
+ return [self::NOT_FOUND];
+ }
+
+ public function getStaticRouteMap()
+ {
+ return $this->staticRouteMap;
+ }
+
+ public function getVariableRouteData()
+ {
+ return $this->variableRouteData;
+ }
+}
diff --git a/src/Http/Middleware/AuthenticateWithHeader.php b/src/Http/Middleware/AuthenticateWithHeader.php
index e2800aeded838f7a6def5e54c94b4124c81f33ae..1fe257094c0e489a54a25c23a960cc0b66fe7382 100644
--- a/src/Http/Middleware/AuthenticateWithHeader.php
+++ b/src/Http/Middleware/AuthenticateWithHeader.php
@@ -18,16 +18,11 @@
namespace Discuz\Http\Middleware;
-use App\Common\CacheKey;
use App\Common\ResponseCode;
-use App\Models\Setting;
use App\Models\User;
use App\Passport\Repositories\AccessTokenRepository;
use Discuz\Auth\Guest;
-use Discuz\Base\DzqLog;
-use Discuz\Cache\CacheManager;
use Discuz\Common\Utils;
-use Discuz\Contracts\Setting\SettingsRepository;
use Illuminate\Support\Arr;
use League\OAuth2\Server\CryptKey;
use League\OAuth2\Server\ResourceServer;
@@ -38,250 +33,106 @@ use Psr\Http\Server\RequestHandlerInterface;
class AuthenticateWithHeader implements MiddlewareInterface
{
- const AUTH_USER_CACHE_TTL = 300;
- protected $cache;
-
- public function __construct(CacheManager $cache)
- {
- $this->cache = $cache;
- }
-
- private $apiFreq = [
- 'get' => [
- 'freq' => 500,
- 'forbidden' => 20
- ],
- 'post' => [
- 'freq' => 100,
- 'forbidden' => 30
- ]
- ];
-
- /**
- * @param ServerRequestInterface $request
- * @param RequestHandlerInterface $handler
- * @return ResponseInterface
- * @throws \League\OAuth2\Server\Exception\OAuthServerException
- */
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
- $api = Utils::getApiName();
- $this->getApiFreq($api);
-
- $headerLine = $request->getHeaderLine('authorization');
- if(empty($headerLine)){ //如果header头中没有 authorization,则从cookie里面找是否有access_token
- $cookies = $request->getCookieParams();
- if(!empty($cookies['access_token'])){
- $headerLine = $cookies['access_token'];
- $request = $request->withHeader('authorization', $headerLine);
- }
- }
-
- // 允许 get、cookie 携带 Token
- if (!$headerLine) {
- $headerLine = Arr::get($request->getQueryParams(), 'token');
-
- if ($headerLine) {
- $request = $request->withHeader('authorization', $headerLine);
- }
- }
-
- $request = $request->withAttribute('actor', new Guest());
+ list($headerLine, $request) = $this->getHeaderLine($request);
if ($headerLine) {
$accessTokenRepository = new AccessTokenRepository();
-
$publickey = new CryptKey(storage_path('cert/public.key'), '', false);
-
$server = new ResourceServer($accessTokenRepository, $publickey);
-
try {
$request = $server->validateAuthenticatedRequest($request);
- } catch (\Exception $e){
- $data = [
- 'api' => $api,
- 'token' => $headerLine
- ];
- DzqLog::error('invalid_token', $data, $e->getMessage());
+ } catch (\Exception $e) {
Utils::outPut(ResponseCode::INVALID_TOKEN);
}
-
- $this->checkLimit($api, $request);
+ }
+ if ($this->isBadRequest($request)) throw new \Exception('操作太频繁,请稍后重试');
+ if ($headerLine) {
// 获取Token位置,根据 Token 解析用户并查询到当前用户
$actor = $this->getActor($request);
-
if (!is_null($actor) && $actor->exists) {
$request = $request->withoutAttribute('oauth_access_token_id')->withoutAttribute('oauth_client_id')->withoutAttribute('oauth_user_id')->withoutAttribute('oauth_scopes')->withAttribute('actor', $actor);
}
- } else {
- $this->checkLimit($api, $request);
}
return $handler->handle($request);
}
- private function getApiFreq($api)
+ private function getHeaderLine($request)
{
- $cache = app('cache');
- $cacheKey = CacheKey::API_FREQUENCE;
- if ($api == 'cache.delete') {
- $cache->forget($cacheKey);
+ $headerLine = $request->getHeaderLine('authorization');
+ if (empty($headerLine)) { //如果header头中没有 authorization,则从cookie里面找是否有access_token
+ $cookies = $request->getCookieParams();
+ if (!empty($cookies['access_token'])) {
+ $headerLine = $cookies['access_token'];
+ $request = $request->withHeader('authorization', $headerLine);
+ }
}
- $apiFreq = $cache->get($cacheKey);
- if (!empty($apiFreq)) {
- $this->apiFreq = json_decode($apiFreq, true);
- } else {
- $apiFreqSetting = Setting::query()->where('key', 'api_freq')->first();
- if (!empty($apiFreqSetting)) {
- $this->apiFreq = json_decode($apiFreqSetting['value'], true);
- $cache->put($cacheKey, $apiFreqSetting['value'], 5 * 60);
+ // 允许 get、cookie 携带 Token
+ if (!$headerLine) {
+ $headerLine = Arr::get($request->getQueryParams(), 'token');
+
+ if ($headerLine) {
+ $request = $request->withHeader('authorization', $headerLine);
}
}
+ //初始化为游客
+ $request = $request->withAttribute('actor', new Guest());
+ return [$headerLine, $request];
}
+
private function getActor(ServerRequestInterface $request)
{
$userId = $request->getAttribute('oauth_user_id');
if (!$userId) {
return null;
}
- return $this->getActorFromDatabase($userId);
-
- //if (app()->config('middleware_cache')) {
- /*$ttl = static::AUTH_USER_CACHE_TTL;
- return $this->cache->remember(
- CacheKey::AUTH_USER_PREFIX.$userId,
- mt_rand($ttl, $ttl + 10),
- function () use ($userId) {
- return $this->getActorFromDatabase($userId);
- }
- );*/
- /*} else {
- return $this->getActorFromDatabase($userId);
- }*/
- }
-
- private function getActorFromDatabase($userId)
- {
- $actor = User::find($userId);
- if (!is_null($actor) && $actor->exists) {
- $actor->changeUpdateAt()->save();
- }
+ $cache = app('cache');
+ $key = 'dzq_login_user_by_id_' . $userId;
+ $actor = $cache->get($key);
+ if (!$actor) {
+ $actor = User::find($userId);
+ $cache->put($key, $actor, 3 * 60);
+ }
+// if (!is_null($actor) && $actor->exists) {
+// $actor->changeUpdateAt()->save();
+// }
return $actor;
}
- private function checkLimit($api, ServerRequestInterface $request)
- {
- $method = Arr::get($request->getServerParams(), 'REQUEST_METHOD', '');
- $userId = $request->getAttribute('oauth_user_id');
- if (strstr($api, 'backAdmin')) {
- return;
- }
- if (strtolower($method) == 'get') {
- if ($this->isForbidden($api, $userId, $request, $method, $this->apiFreq['get']['freq'])) {
- throw new \Exception('操作太频繁,请稍后重试');
- }
- } else {
- if ($this->isForbidden($api, $userId, $request, $method, $this->apiFreq['post']['freq'])) {
- throw new \Exception('操作太频繁,请稍后重试');
- }
- }
- }
- private function isForbidden($api, $userId, ServerRequestInterface $request, $method, $max = 10, $interval = 60)
+ private function isBadRequest(ServerRequestInterface $request)
{
-
- $ip = ip($request->getServerParams());
- if (empty($api)) {
- return true;
- }
- $method = strtolower($method);
-
- if ($this->isAttachments($api, $method) || $this->isCoskey($api, $method)) {
- $maxUploadNum = app()->make(SettingsRepository::class)->get('support_max_upload_attachment_num', 'default');
- $maxLimit = $maxUploadNum ? (int)$maxUploadNum : 20;
+ $api = $request->getUri()->getPath();
+ if (Utils::startWith($api, '/backAdmin')) {
+ return false;
}
-
+ $httpMethod = Arr::get($request->getServerParams(), 'REQUEST_METHOD', '');
+ $routeInfo = $this->getRouteInfo($api, $httpMethod);
+ $ip = ip($request->getServerParams());
+ $userId = $request->getAttribute('oauth_user_id');
if (empty($userId)) {
- $key = 'api_limit_by_ip_' . md5($ip . $api . $method);
+ $key = md5($ip . $api . $httpMethod);
} else {
- $key = 'api_limit_by_uid_' . md5($userId . '_' . $api . $method);
- }
- if ($this->isRegister($api, $method)) {
- return $this->setLimit($key, $method, 10, 10 * 60);
- }
- if ($this->isAttachments($api, $method)) {
- return $this->setLimit($key, $method, $maxLimit, 5 * 60);
- }
- if ($this->isPoll($api)) {
- return $this->setLimit($key, $method, 200, 60);
- }
-
- if ($this->isCoskey($api, $method)) {
- return $this->setLimit($key, $method, $maxLimit, 30);
+ $key = md5($userId . $api . $httpMethod);
}
- if ($this->isPayOrder($api, $method)) {
- return $this->setLimit($key, $method, 3, 10);
+ if($httpMethod == 'GET'){
+ $times = $routeInfo['times'] ?: 20;
+ $interval = $routeInfo['interval'] ?: 30;
+ }else{
+ $times = $routeInfo['times'] ?: 30;
+ $interval = $routeInfo['interval'] ?: 60;
}
- return $this->setLimit($key, $method, $max);
- }
-
- private function isRegister($api, $method)
- {
- return $api == 'users/username.register' && $method == 'post';
- }
-
- private function isAttachments($api, $method)
- {
- return $api == 'attachments' && $method == 'post';
- }
-
- private function isPoll($api)
- {
- $pollapi = [
- 'users/pc/wechat/h5.login',
- 'users/pc/wechat/h5.bind',
- 'users/pc/wechat/miniprogram.bind',
- 'users/pc/wechat/miniprogram.login',
- 'users/pc/wechat.rebind.poll',
- 'dialog/message',
- 'unreadnotification',
- 'dialog.update'
- ];
- return in_array($api, $pollapi);
- }
-
- private function isCoskey($api, $method)
- {
- return $api == 'coskey' && $method == 'post';
- }
-
- private function isPayOrder($api, $method){
- return $api == 'trade/pay/order' && $method == 'post';
- }
-
- /*
- * $max interage 每分钟最大调用次数
- * $defaultDelay Boolen 超过调用次数禁止秒数
- */
- private function setLimit($key, $method, $max, $defaultDelay = null)
- {
+ $delay = $routeInfo['delay'] ?: 300;//默认禁用5分钟
$cache = app('cache');
$count = $cache->get($key);
-
if (empty($count)) {
- $cache->add($key, 1, 60);
+ $cache->add($key, 1, $interval);
return false;
} else {
- if ($count >= $max) {
- if ($defaultDelay == null) {
- if ($method == 'get') {
- $cache->put($key, $count, $this->apiFreq['get']['forbidden']);
- } else {
- $cache->put($key, $count, $this->apiFreq['post']['forbidden']);
- }
- } else {
- $cache->put($key, $count, $defaultDelay);
- }
+ if ($count >= $times) {
+ $cache->put($key, $count, $delay);
return true;
} else {
$cache->increment($key);
@@ -289,4 +140,50 @@ class AuthenticateWithHeader implements MiddlewareInterface
}
}
}
+
+ /**
+ * author: 流火行者
+ * desc: 获取单个路由详情
+ * @param $api
+ * @param $httpMethod
+ * @return bool
+ */
+ private function getRouteInfo($api, $httpMethod)
+ {
+ $routeMaps = Utils::getRouteMap();
+ $staticMaps = $routeMaps[0] ?? [];
+ $variableMaps = $routeMaps[1] ?? [];
+ foreach ($staticMaps as $method => $staticMap) {
+ if ($method == $httpMethod && isset($staticMap[$api])) return $staticMap[$api];
+ }
+ foreach ($variableMaps as $method => $variableMap) {
+ $route = $this->dispatchVariableRoute($variableMap, $api);
+ if ($method == $httpMethod && $route) return $route;
+ }
+ return false;
+ }
+
+ /**
+ * author: 流火行者
+ * desc: 解析可变路由详情
+ * @param $routeData
+ * @param $uri
+ * @return bool
+ */
+ private function dispatchVariableRoute($routeData, $uri)
+ {
+ foreach ($routeData as $data) {
+ if (!preg_match($data['regex'], $uri, $matches)) {
+ continue;
+ }
+ list($handler, $varNames) = $data['routeMap'][count($matches)];
+ $vars = [];
+ $i = 0;
+ foreach ($varNames as $varName) {
+ $vars[$varName] = $matches[++$i];
+ }
+ return $handler;
+ }
+ return false;
+ }
}
diff --git a/src/Http/Middleware/DispatchRoute.php b/src/Http/Middleware/DispatchRoute.php
index b145e1dd5ccde07cf345d12bd39dfb98690122e6..5b7ddcf8d6b3fd58249f9268d865af690ab1a65d 100644
--- a/src/Http/Middleware/DispatchRoute.php
+++ b/src/Http/Middleware/DispatchRoute.php
@@ -20,6 +20,7 @@ namespace Discuz\Http\Middleware;
use Discuz\Http\Exception\MethodNotAllowedException;
use Discuz\Http\Exception\RouteNotFoundException;
+use Discuz\Http\GroupCountBased;
use Discuz\Http\RouteCollection;
use Discuz\Http\RouteHandlerFactory;
use Psr\Http\Message\ResponseInterface;
@@ -60,6 +61,7 @@ class DispatchRoute implements MiddlewareInterface
* @param ServerRequestInterface $request
* @param RequestHandlerInterface $handler
* @return ResponseInterface
+ * @throws \Exception
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
@@ -72,17 +74,42 @@ class DispatchRoute implements MiddlewareInterface
case Dispatcher::METHOD_NOT_ALLOWED:
throw new MethodNotAllowedException($method);
case Dispatcher::FOUND:
- $handler = $routeInfo[1];
+ $handlerInfo = $routeInfo[1];
$parameters = $routeInfo[2];
+ $handler = $this->getReplaceHandler($method, $handlerInfo);
return $this->factory->toController($handler)($request, $parameters);
}
}
protected function getDispatcher()
{
- if (! isset($this->dispatcher)) {
- $this->dispatcher = new Dispatcher\GroupCountBased($this->routes->getRouteData());
+ if (!isset($this->dispatcher)) {
+ $this->dispatcher = new GroupCountBased($this->routes->getRouteData());
}
return $this->dispatcher;
}
+
+ protected function getReplaceHandler($method, $handlerInfo)
+ {
+ $dispatcher = $this->getDispatcher();
+ $staticRouteMap = $dispatcher->getStaticRouteMap();
+ //$variableRouteData = $dispatcher->getVariableRouteData(); //不支持动态路由
+ $replaceHandlers = [];
+ foreach ($staticRouteMap as $m => $staticRoutes) {
+ foreach ($staticRoutes as $urlPath => $staticRoute) {
+ if(!empty($staticRoute['replaceHandler'])){
+ $replaceHandlers[$staticRoute['replaceHandler']] = $staticRoute;
+ }
+ }
+ }
+ $handler = $handlerInfo['handler'];
+ if(isset($replaceHandlers[$handler])){
+ if($replaceHandlers[$handler]['method'] == $method){
+ return $replaceHandlers[$handler]['handler'];
+ }else{
+ throw new \Exception('handler (' . $handler . ') method not matched');
+ }
+ }
+ return $handler;
+ }
}
diff --git a/src/Http/RegexBasedAbstract.php b/src/Http/RegexBasedAbstract.php
new file mode 100644
index 0000000000000000000000000000000000000000..86657f3ecc87f6a32dbe0868f2a70e0b20e240fb
--- /dev/null
+++ b/src/Http/RegexBasedAbstract.php
@@ -0,0 +1,104 @@
+staticRouteMap[$httpMethod][$uri])) {
+ $handler = $this->staticRouteMap[$httpMethod][$uri];
+ return [self::FOUND, $handler, []];
+ }
+
+ $varRouteData = $this->variableRouteData;
+ if (isset($varRouteData[$httpMethod])) {
+ $result = $this->dispatchVariableRoute($varRouteData[$httpMethod], $uri);
+ if ($result[0] === self::FOUND) {
+ return $result;
+ }
+ }
+
+ // For HEAD requests, attempt fallback to GET
+ if ($httpMethod === 'HEAD') {
+ if (isset($this->staticRouteMap['GET'][$uri])) {
+ $handler = $this->staticRouteMap['GET'][$uri];
+ return [self::FOUND, $handler, []];
+ }
+ if (isset($varRouteData['GET'])) {
+ $result = $this->dispatchVariableRoute($varRouteData['GET'], $uri);
+ if ($result[0] === self::FOUND) {
+ return $result;
+ }
+ }
+ }
+
+ // If nothing else matches, try fallback routes
+ if (isset($this->staticRouteMap['*'][$uri])) {
+ $handler = $this->staticRouteMap['*'][$uri];
+ return [self::FOUND, $handler, []];
+ }
+ if (isset($varRouteData['*'])) {
+ $result = $this->dispatchVariableRoute($varRouteData['*'], $uri);
+ if ($result[0] === self::FOUND) {
+ return $result;
+ }
+ }
+
+ // Find allowed methods for this URI by matching against all other HTTP methods as well
+ $allowedMethods = [];
+
+ foreach ($this->staticRouteMap as $method => $uriMap) {
+ if ($method !== $httpMethod && isset($uriMap[$uri])) {
+ $allowedMethods[] = $method;
+ }
+ }
+
+ foreach ($varRouteData as $method => $routeData) {
+ if ($method === $httpMethod) {
+ continue;
+ }
+
+ $result = $this->dispatchVariableRoute($routeData, $uri);
+ if ($result[0] === self::FOUND) {
+ $allowedMethods[] = $method;
+ }
+ }
+
+ // If there are no allowed methods the route simply does not exist
+ if ($allowedMethods) {
+ return [self::METHOD_NOT_ALLOWED, $allowedMethods];
+ }
+
+ return [self::NOT_FOUND];
+ }
+}
diff --git a/src/Http/RouteCollection.php b/src/Http/RouteCollection.php
index 063e7c04085d41ab1f72db3e66e03f6e29e35108..c4ebfbeff40b40ae2f3763575c75e1867be279d8 100644
--- a/src/Http/RouteCollection.php
+++ b/src/Http/RouteCollection.php
@@ -40,38 +40,46 @@ class RouteCollection
protected $currentGroupPrefix;
+ protected $times;
+ protected $interval;
+ protected $delay;
+
public function __construct()
{
$this->dataGenerator = new DataGenerator\GroupCountBased;
+
$this->routeParser = new RouteParser\Std;
$this->currentGroupPrefix = '';
+ $this->times = null;
+ $this->interval = null;
+ $this->delay = null;
}
- public function get($path, $name, $handler)
- {
- return $this->addRoute('GET', $path, $name, $handler);
- }
-
- public function post($path, $name, $handler)
- {
- return $this->addRoute('POST', $path, $name, $handler);
- }
-
- public function put($path, $name, $handler)
+ public function get($path, $name, $handler, $replaceHandler = null)
{
- return $this->addRoute('PUT', $path, $name, $handler);
+ return $this->addRoute('GET', $path, $name, $handler, $replaceHandler);
}
- public function patch($path, $name, $handler)
+ public function post($path, $name, $handler, $replaceHandler = null)
{
- return $this->addRoute('PATCH', $path, $name, $handler);
+ return $this->addRoute('POST', $path, $name, $handler, $replaceHandler);
}
- public function delete($path, $name, $handler)
- {
- return $this->addRoute('DELETE', $path, $name, $handler);
- }
+// public function put($path, $name, $handler)
+// {
+// return $this->addRoute('PUT', $path, $name, $handler);
+// }
+//
+// public function patch($path, $name, $handler)
+// {
+// return $this->addRoute('PATCH', $path, $name, $handler);
+// }
+//
+// public function delete($path, $name, $handler)
+// {
+// return $this->addRoute('DELETE', $path, $name, $handler);
+// }
public function group($prefix, callable $callback)
{
@@ -81,12 +89,37 @@ class RouteCollection
$this->currentGroupPrefix = $previousGroupPrefix;
}
- public function addRoute($method, $path, $name, $handler)
+ /**
+ * @param callable $callback
+ * @param int $times 访问次数
+ * @param int $interval 时间间隔(秒)
+ * @param int $delay 超过限频禁用时长(秒)
+ */
+ public function withFrequency(callable $callback, $times = 100, $interval = 60, $delay = 300)
+ {
+ $this->times = $times;
+ $this->interval = $interval;
+ $this->delay = $delay;
+ $callback($this);
+ $this->times = null;
+ $this->interval = null;
+ $this->delay = null;
+ }
+
+ public function addRoute($method, $path, $name, $handler, $replaceHandler = null)
{
$path = $this->currentGroupPrefix . $path;
- $path = str_replace('//','/',$path);
+ $path = str_replace('//', '/', $path);
$routeDatas = $this->routeParser->parse($path);
foreach ($routeDatas as $routeData) {
+ $handler = [
+ 'method' => $method,
+ 'handler' => $handler,
+ 'replaceHandler' => $replaceHandler,
+ 'times' => $this->times,
+ 'interval' => $this->interval,
+ 'delay' => $this->delay
+ ];
$this->dataGenerator->addRoute($method, $routeData, $handler);
}
@@ -111,7 +144,7 @@ class RouteCollection
if (isset($this->reverse[$name])) {
$parts = $this->reverse[$name][0];
array_walk($parts, [$this, 'fixPathPart'], $parameters);
- return '/'.ltrim(implode('', $parts), '/');
+ return '/' . ltrim(implode('', $parts), '/');
}
throw new \RuntimeException("Route $name not found");
}
diff --git a/src/Http/Server.php b/src/Http/Server.php
index 593c5479945012a535d9332f0022ac682bd64108..8f162f56cf454890e2774985118a983c87d2f900 100644
--- a/src/Http/Server.php
+++ b/src/Http/Server.php
@@ -98,10 +98,12 @@ class Server extends SiteApp
$line = $error->getLine();
$type = get_class($error);
$this->app->make('log')->error($error);
-
+ $trace = $error->getTraceAsString();
return <<
- thrown in $file on line $line
+ thrown in $file on line $line
+ $message
$trace
+
ERROR;
}
diff --git a/src/Web/WebServiceProvider.php b/src/Web/WebServiceProvider.php
index cbf89a8555cfe9b3a29bcb0586c6611ed2014e9b..1cb3a07ff7acd32a9e4ff291b0e3a07a6835613d 100644
--- a/src/Web/WebServiceProvider.php
+++ b/src/Web/WebServiceProvider.php
@@ -18,6 +18,7 @@
namespace Discuz\Web;
+use App\Common\Utils;
use Discuz\Http\Middleware\DispatchRoute;
use Discuz\Http\Middleware\HandleErrorsWithView;
use Discuz\Http\Middleware\HandleErrorsWithWhoops;
@@ -57,5 +58,7 @@ class WebServiceProvider extends ServiceProvider
$route->group('', function (RouteCollection $route) {
require $this->app->basePath('routes/web.php');
});
+ \Discuz\Common\Utils::includePluginRoutes($route);
+ \Discuz\Common\Utils::setRouteMap($route->getRouteData());
}
}