return and show in browser JSON parsing errors maybe caused by network problems

server sends HTTP status "400 Bad Request" with JSON payload with "error" and "errno" attributes.
error is json_last_error_msg() prefixed with "JSON ".
Not yet implemented is resending the request (max. twice) for JSON parsing errors to try to work around network problems
This commit is contained in:
Ralf Becker 2018-11-01 11:59:31 +01:00
parent a635ac34b4
commit 2f1333a116
3 changed files with 46 additions and 12 deletions

View File

@ -149,9 +149,19 @@ egw.extend('json', egw.MODULE_WND_LOCAL, function(_app, _wnd)
this.egw.message.call(this.egw, this.egw.lang('A request to the EGroupware server returned with an error')+': '+_xmlhttp.statusText+' ('+_xmlhttp.status+
")\n\n"+this.egw.lang('Please reload the EGroupware desktop (F5 / Cmd+r).')+"\n"+
this.egw.lang('If the error persists, contact your administrator for help and ask to check the error-log of the webserver.')+
"\n\nURL: "+this.url+"\n"+(_xmlhttp.getAllResponseHeaders() ? _xmlhttp.getAllResponseHeaders().match(/^Date:.*$/m)[0]:''));
"\n\nURL: "+this.url+"\n"+(_xmlhttp.getAllResponseHeaders() ? _xmlhttp.getAllResponseHeaders().match(/^Date:.*$/mi)[0]:'')+
// if EGroupware send JSON payload with error, errno show it here too
(_err === 'error' && _xmlhttp.status === 400 && typeof _xmlhttp.responseJSON === 'object' && _xmlhttp.responseJSON.error ?
"\nError: "+_xmlhttp.responseJSON.error+' ('+_xmlhttp.responseJSON.errno+')' : ''));
this.egw.debug('error', 'Ajax request to', this.url, ' failed: ', _err, _xmlhttp.status, _xmlhttp.statusText);
this.egw.debug('error', 'Ajax request to', this.url, ' failed: ', _err, _xmlhttp.status, _xmlhttp.statusText, _xmlhttp.responseJSON);
// check of unparsable JSON on server-side, which might be caused by some network problem --> resend max. twice
if (_err === 'error' && _xmlhttp.status === 400 && typeof _xmlhttp.responseJSON === 'object' &&
_xmlhttp.responseJSON.errno && _xmlhttp.responseJSON.error.substr(0, 5) === 'JSON ')
{
// ToDo: resend request max. twice
}
}
};

View File

@ -54,23 +54,36 @@ class Request
*
* @param string menuaction to call
* @param string $input_data is the RAW input data as it was received from the client
* @throws \InvalidArgumentException if JSON can not be parsed (json_last_error())
* or did not contain request[parameters] array (999)
*/
public function parseRequest($menuaction, $input_data)
{
// Remember that we currently are in a JSON request - e.g. used in the redirect code
self::$_hadJSONRequest = true;
// no or empty payload is eg. used by dynamicly loading tree nodes (uses just GET parameters)
if (!isset($input_data) || $input_data === '')
{
$parameters = array();
}
else
{
if (get_magic_quotes_gpc()) $input_data = stripslashes($input_data);
$json_data = json_decode($input_data,true);
if (is_array($json_data) && isset($json_data['request']) && isset($json_data['request']['parameters']) && is_array($json_data['request']['parameters']))
if (($json_data = json_decode($input_data,true)) === null && json_last_error() !== JSON_ERROR_NONE)
{
throw new \InvalidArgumentException('JSON '.json_last_error_msg(), json_last_error());
}
elseif (is_array($json_data) && isset($json_data['request']) && isset($json_data['request']['parameters']) && is_array($json_data['request']['parameters']))
{
//error_log(__METHOD__.__LINE__.array2string($json_data['request']).function_backtrace());
$parameters =& $json_data['request']['parameters'];
}
else
{
$parameters = array();
throw new \InvalidArgumentException('Missing request:parameters object', 999);
}
}
// do we have a single request or an array of queued requests
if ($menuaction == 'api.queue')

View File

@ -70,8 +70,11 @@ function ajax_exception_handler($e)
// set our own exception handler, to not get the html from eGW's default one
set_exception_handler('ajax_exception_handler');
if (isset($_GET['menuaction']))
try {
if (!isset($_GET['menuaction']))
{
throw new InvalidArgumentException('Missing menuaction GET parameter', 998);
}
if (strpos($_GET['menuaction'],'::') !== false && strpos($_GET['menuaction'],'.') === false) // static method name app_something::method
{
@list($className,$functionName,$handler) = explode('::',$_GET['menuaction']);
@ -127,5 +130,13 @@ if (isset($_GET['menuaction']))
Json\Response::get();
exit();
}
// missing menuaction GET parameter or request:parameters object or unparsable JSON
catch (\InvalidArgumentException $e) {
if (isset($json)) $json->isJSONRequest(false); // no regular json request processing
throw new Json\Exception($_SERVER['PHP_SELF'] . ' Invalid AJAX JSON Request');
// give a proper HTTP status 400 Bad Request with some JSON payload explaining the problem
http_response_code(400);
header('Content-Type: application/json');
echo json_encode(array('error' => $e->getMessage(), 'errno' => $e->getCode()));
}
// other exceptions are handled by our ajax_exception_handler sending them back as alerts to client-side