MDL-86866 core: Web 404 responses should generate HTML not JSON
This commit is contained in:
@@ -547,8 +547,7 @@ class component {
|
||||
// Always keep moodle_exception in place.
|
||||
$keyclasses = [
|
||||
\core\exception\moodle_exception::class,
|
||||
\core\output\bootstrap_renderer::class,
|
||||
\core_cache\cache::class,
|
||||
\core\router\middleware\api_validation_middleware::class,
|
||||
];
|
||||
foreach ($keyclasses as $classname) {
|
||||
if (!array_key_exists($classname, $cache['classmap'])) {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
namespace core;
|
||||
|
||||
use core\router\middleware\api_validation_middleware;
|
||||
use core\router\middleware\cors_middleware;
|
||||
use core\router\middleware\error_handling_middleware;
|
||||
use core\router\middleware\moodle_api_authentication_middleware;
|
||||
@@ -251,7 +252,7 @@ class router {
|
||||
// Add a Middleware to set the CORS headers for all REST Responses.
|
||||
->add(di::get(cors_middleware::class))
|
||||
->add(di::get(moodle_api_authentication_middleware::class))
|
||||
->add(di::get(validation_middleware::class));
|
||||
->add(di::get(api_validation_middleware::class));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace core\router\middleware;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
|
||||
/**
|
||||
* Middleware to handle validation of API requests and responses based on the route data.
|
||||
*
|
||||
* @package core
|
||||
* @copyright Andrew Lyons <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class api_validation_middleware extends validation_middleware {
|
||||
#[\Override]
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface {
|
||||
try {
|
||||
$request = $this->requestvalidator->validate_request($request);
|
||||
} catch (\Exception $e) {
|
||||
return $this->responsehandler->get_response_from_exception($request, $e);
|
||||
}
|
||||
|
||||
$response = $handler->handle($request);
|
||||
|
||||
try {
|
||||
$this->responsevalidator->validate_response($request, $response);
|
||||
} catch (\Exception $e) {
|
||||
return $this->responsehandler->get_response_from_exception($request, $e);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
@@ -53,27 +53,9 @@ class validation_middleware implements MiddlewareInterface {
|
||||
|
||||
#[\Override]
|
||||
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface {
|
||||
global $USER;
|
||||
|
||||
try {
|
||||
$request = $this->requestvalidator->validate_request($request);
|
||||
} catch (\Exception $e) {
|
||||
$response = $this->responsehandler->get_response_from_exception($request, $e);
|
||||
// Throw 'page not found' exception for non-admins.
|
||||
// This hides stacktrace and errorcodes in detailed payload responses.
|
||||
if (!is_siteadmin($USER->id) && $response->getStatusCode() == 404) {
|
||||
return \core\router\util::throw_page_not_found($request, $response, $response->getReasonPhrase());
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
$request = $this->requestvalidator->validate_request($request);
|
||||
$response = $handler->handle($request);
|
||||
|
||||
try {
|
||||
$this->responsevalidator->validate_response($request, $response);
|
||||
} catch (\Exception $e) {
|
||||
return $this->responsehandler->get_response_from_exception($request, $e);
|
||||
}
|
||||
$this->responsevalidator->validate_response($request, $response);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
@@ -97,6 +97,37 @@ class util {
|
||||
return self::redirect($response, $url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw a specialized HTTP exception based on the response status code.
|
||||
*
|
||||
* @param \Psr\Http\Message\ServerRequestInterface $request
|
||||
* @param \Psr\Http\Message\ResponseInterface $response
|
||||
*/
|
||||
public static function throw_specialized_exception(
|
||||
ServerRequestInterface $request,
|
||||
ResponseInterface $response,
|
||||
?string $message = null,
|
||||
?\Throwable $previous = null,
|
||||
): void {
|
||||
$exceptionclass = match ($response->getStatusCode()) {
|
||||
400 => \Slim\Exception\HttpBadRequestException::class,
|
||||
401 => \Slim\Exception\HttpUnauthorizedException::class,
|
||||
403 => \Slim\Exception\HttpForbiddenException::class,
|
||||
404 => \Slim\Exception\HttpNotFoundException::class,
|
||||
405 => \Slim\Exception\HttpMethodNotAllowedException::class,
|
||||
410 => \Slim\Exception\HttpGoneException::class,
|
||||
429 => \Slim\Exception\HttpTooManyRequestsException::class,
|
||||
500 => \Slim\Exception\HttpInternalServerErrorException::class,
|
||||
501 => \Slim\Exception\HttpNotImplementedException::class,
|
||||
|
||||
default => null,
|
||||
};
|
||||
|
||||
if ($exceptionclass !== null) {
|
||||
throw new $exceptionclass($request, $message, $previous);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a Page Not Found result.
|
||||
*
|
||||
@@ -109,7 +140,7 @@ class util {
|
||||
ServerRequestInterface $request,
|
||||
ResponseInterface $response,
|
||||
): ResponseInterface {
|
||||
throw new \Slim\Exception\HttpNotFoundException($request);
|
||||
self::throw_specialized_exception($request, $response->withStatus(404));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -45,7 +45,7 @@ final class validation_middleware_test extends \advanced_testcase {
|
||||
$requestvalidator->expects($this->once())
|
||||
->method('validate_request')
|
||||
->with($request)
|
||||
->willThrowException(new \Exception('Invalid request'));
|
||||
->willThrowException(new \Slim\Exception\HttpException($request, 'Invalid request'));
|
||||
|
||||
// If the request fails validation, it will not be passed to next Middleware.
|
||||
$handler = $this->getMockBuilder(RequestHandlerInterface::class)->getMock();
|
||||
@@ -60,8 +60,10 @@ final class validation_middleware_test extends \advanced_testcase {
|
||||
|
||||
// Execute the middleware.
|
||||
$middleware = di::get(validation_middleware::class);
|
||||
$returns = $middleware->process($request, $handler);
|
||||
$this->assertInstanceOf(ResponseInterface::class, $returns);
|
||||
|
||||
$this->expectException(\Exception::class);
|
||||
$this->expectExceptionMessage('Invalid request');
|
||||
$middleware->process($request, $handler);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,9 +99,10 @@ final class validation_middleware_test extends \advanced_testcase {
|
||||
|
||||
// Execute the middleware.
|
||||
$middleware = di::get(validation_middleware::class);
|
||||
$returns = $middleware->process($request, $handler);
|
||||
$this->assertInstanceOf(ResponseInterface::class, $returns);
|
||||
$this->assertNotEquals($response, $returns);
|
||||
|
||||
$this->expectException(\Exception::class);
|
||||
$this->expectExceptionMessage('Invalid response');
|
||||
$middleware->process($request, $handler);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user