MXS-1220: Add error messages to bad request

Requests now contain a JSON API-conforming, human-readable error message.
This commit is contained in:
Markus Mäkelä
2017-05-16 23:45:15 +03:00
parent ac29abd679
commit 05cb49d48a
4 changed files with 90 additions and 23 deletions

View File

@ -268,13 +268,14 @@ complete the request.
### 4xx Client Error ### 4xx Client Error
The 4xx class of status code is when the client seems to have erred. Except when The 4xx class of status code is when the client seems to have erred. Except when
responding to a HEAD request, the body of the response contains a JSON responding to a HEAD request, the body of the response *MAY* contains a JSON
representation of the error in the following format. representation of the error.
``` ```javascript
{ {
"error": "Method not supported", "error": {
"description": "The `/service` resource does not support POST." "detail" : "The new `/server/` resource is missing the `port` parameter"
}
} }
``` ```

View File

@ -24,6 +24,7 @@
#include <maxscale/atomic.h> #include <maxscale/atomic.h>
#include <maxscale/paths.h> #include <maxscale/paths.h>
#include <maxscale/platform.h>
#include <maxscale/spinlock.h> #include <maxscale/spinlock.h>
#include <maxscale/jansson.hh> #include <maxscale/jansson.hh>
#include <maxscale/json_api.h> #include <maxscale/json_api.h>
@ -40,6 +41,8 @@ using mxs::Closer;
static SPINLOCK crt_lock = SPINLOCK_INIT; static SPINLOCK crt_lock = SPINLOCK_INIT;
thread_local stringstream runtime_errmsg;
bool runtime_link_server(SERVER *server, const char *target) bool runtime_link_server(SERVER *server, const char *target)
{ {
spinlock_acquire(&crt_lock); spinlock_acquire(&crt_lock);
@ -55,6 +58,11 @@ bool runtime_link_server(SERVER *server, const char *target)
service_serialize(service); service_serialize(service);
rval = true; rval = true;
} }
else
{
runtime_errmsg << "Service '" << service->name << "' already uses server '"
<< server->unique_name << "'";
}
} }
else if (monitor) else if (monitor)
{ {
@ -63,6 +71,10 @@ bool runtime_link_server(SERVER *server, const char *target)
monitor_serialize(monitor); monitor_serialize(monitor);
rval = true; rval = true;
} }
else
{
runtime_errmsg << "Server '" << server->unique_name << "' is already monitored";
}
} }
if (rval) if (rval)
@ -165,6 +177,10 @@ bool runtime_create_server(const char *name, const char *address, const char *po
rval = true; rval = true;
} }
} }
else
{
runtime_errmsg << "Server '" << name << "' already exists";
}
spinlock_release(&crt_lock); spinlock_release(&crt_lock);
return rval; return rval;
@ -177,6 +193,8 @@ bool runtime_destroy_server(SERVER *server)
if (service_server_in_use(server) || monitor_server_in_use(server)) if (service_server_in_use(server) || monitor_server_in_use(server))
{ {
runtime_errmsg << "Cannot destroy server '" << server->unique_name <<
"' as it is used by at least one service or monitor";
MXS_ERROR("Cannot destroy server '%s' as it is used by at least one " MXS_ERROR("Cannot destroy server '%s' as it is used by at least one "
"service or monitor", server->unique_name); "service or monitor", server->unique_name);
} }
@ -319,10 +337,17 @@ bool runtime_alter_server(SERVER *server, const char *key, const char *value)
} }
} }
if (valid && server_serialize(server)) if (valid)
{
if (server_serialize(server))
{ {
MXS_NOTICE("Updated server '%s': %s=%s", server->unique_name, key, value); MXS_NOTICE("Updated server '%s': %s=%s", server->unique_name, key, value);
} }
}
else
{
runtime_errmsg << "Invalid server parameter: " << key;
}
spinlock_release(&crt_lock); spinlock_release(&crt_lock);
return valid; return valid;
@ -468,6 +493,10 @@ bool runtime_alter_monitor(MXS_MONITOR *monitor, const char *key, const char *va
MXS_NOTICE("Updated monitor '%s': %s=%s", monitor->name, key, value); MXS_NOTICE("Updated monitor '%s': %s=%s", monitor->name, key, value);
} }
else
{
runtime_errmsg << "Invalid monitor parameter: " << key;
}
spinlock_release(&crt_lock); spinlock_release(&crt_lock);
return valid; return valid;
@ -537,6 +566,7 @@ bool runtime_alter_service(SERVICE *service, const char* zKey, const char* zValu
} }
else else
{ {
runtime_errmsg << "Invalid service parameter: " << key;
MXS_ERROR("Unknown parameter for service '%s': %s=%s", MXS_ERROR("Unknown parameter for service '%s': %s=%s",
service->name, key.c_str(), value.c_str()); service->name, key.c_str(), value.c_str());
valid = false; valid = false;
@ -703,7 +733,7 @@ bool runtime_create_monitor(const char *name, const char *module)
} }
else else
{ {
MXS_WARNING("Can't create monitor, it already exists"); runtime_errmsg << "Can't create monitor '" << name << "', it already exists";
} }
spinlock_release(&crt_lock); spinlock_release(&crt_lock);
@ -908,10 +938,14 @@ SERVER* runtime_create_server_from_json(json_t* json)
rval = NULL; rval = NULL;
} }
} }
else
{
runtime_errmsg << "Invalid relationships in request JSON";
}
} }
else else
{ {
MXS_WARNING("Invalid request JSON: %s", mxs::json_dump(json).c_str()); runtime_errmsg << "Missing or bad parameters in request JSON";
} }
return rval; return rval;
@ -1082,7 +1116,7 @@ MXS_MONITOR* runtime_create_monitor_from_json(json_t* json)
} }
else else
{ {
MXS_WARNING("Invalid request JSON: %s", mxs::json_dump(json).c_str()); runtime_errmsg << "Missing or bad parameters in request JSON";
} }
return rval; return rval;
@ -1386,6 +1420,31 @@ bool runtime_create_listener_from_json(SERVICE* service, json_t* json)
ssl_key, ssl_cert, ssl_ca_cert, ssl_version, ssl_key, ssl_cert, ssl_ca_cert, ssl_version,
ssl_cert_verify_depth); ssl_cert_verify_depth);
} }
else
{
runtime_errmsg << "Missing or bad parameters in request JSON";
}
return rval; return rval;
} }
json_t* runtime_get_json_error()
{
json_t* obj = NULL;
string errmsg = runtime_errmsg.str();
if (errmsg.length())
{
runtime_errmsg.str("");
json_t* err = json_object();
json_object_set_new(err, "detail", json_string(errmsg.c_str()));
json_t* arr = json_array();
json_array_append_new(arr, err);
obj = json_object();
json_object_set_new(obj, "errors", arr);
}
return obj;
}

View File

@ -257,4 +257,11 @@ bool runtime_create_listener_from_json(SERVICE* service, json_t* json);
*/ */
bool runtime_alter_logs_from_json(json_t* json); bool runtime_alter_logs_from_json(json_t* json);
/**
* @brief Get current runtime error in JSON format
*
* @return The latest runtime error in JSON format or NULL if no error has occurred
*/
json_t* runtime_get_json_error();
MXS_END_DECLS MXS_END_DECLS

View File

@ -223,7 +223,7 @@ HttpResponse cb_create_server(const HttpRequest& request)
return HttpResponse(MHD_HTTP_NO_CONTENT); return HttpResponse(MHD_HTTP_NO_CONTENT);
} }
return HttpResponse(MHD_HTTP_FORBIDDEN); return HttpResponse(MHD_HTTP_FORBIDDEN, runtime_get_json_error());
} }
HttpResponse cb_alter_server(const HttpRequest& request) HttpResponse cb_alter_server(const HttpRequest& request)
@ -240,7 +240,7 @@ HttpResponse cb_alter_server(const HttpRequest& request)
} }
} }
return HttpResponse(MHD_HTTP_FORBIDDEN); return HttpResponse(MHD_HTTP_FORBIDDEN, runtime_get_json_error());
} }
HttpResponse cb_create_monitor(const HttpRequest& request) HttpResponse cb_create_monitor(const HttpRequest& request)
@ -252,7 +252,7 @@ HttpResponse cb_create_monitor(const HttpRequest& request)
return HttpResponse(MHD_HTTP_NO_CONTENT); return HttpResponse(MHD_HTTP_NO_CONTENT);
} }
return HttpResponse(MHD_HTTP_FORBIDDEN); return HttpResponse(MHD_HTTP_FORBIDDEN, runtime_get_json_error());
} }
HttpResponse cb_create_service_listener(const HttpRequest& request) HttpResponse cb_create_service_listener(const HttpRequest& request)
@ -265,7 +265,7 @@ HttpResponse cb_create_service_listener(const HttpRequest& request)
return HttpResponse(MHD_HTTP_NO_CONTENT); return HttpResponse(MHD_HTTP_NO_CONTENT);
} }
return HttpResponse(MHD_HTTP_FORBIDDEN); return HttpResponse(MHD_HTTP_FORBIDDEN, runtime_get_json_error());
} }
HttpResponse cb_alter_monitor(const HttpRequest& request) HttpResponse cb_alter_monitor(const HttpRequest& request)
@ -282,7 +282,7 @@ HttpResponse cb_alter_monitor(const HttpRequest& request)
} }
} }
return HttpResponse(MHD_HTTP_FORBIDDEN); return HttpResponse(MHD_HTTP_FORBIDDEN, runtime_get_json_error());
} }
HttpResponse cb_alter_service(const HttpRequest& request) HttpResponse cb_alter_service(const HttpRequest& request)
@ -299,7 +299,7 @@ HttpResponse cb_alter_service(const HttpRequest& request)
} }
} }
return HttpResponse(MHD_HTTP_FORBIDDEN); return HttpResponse(MHD_HTTP_FORBIDDEN, runtime_get_json_error());
} }
HttpResponse cb_alter_logs(const HttpRequest& request) HttpResponse cb_alter_logs(const HttpRequest& request)
@ -311,7 +311,7 @@ HttpResponse cb_alter_logs(const HttpRequest& request)
return HttpResponse(MHD_HTTP_NO_CONTENT); return HttpResponse(MHD_HTTP_NO_CONTENT);
} }
return HttpResponse(MHD_HTTP_FORBIDDEN); return HttpResponse(MHD_HTTP_FORBIDDEN, runtime_get_json_error());
} }
HttpResponse cb_delete_server(const HttpRequest& request) HttpResponse cb_delete_server(const HttpRequest& request)
@ -323,7 +323,7 @@ HttpResponse cb_delete_server(const HttpRequest& request)
return HttpResponse(MHD_HTTP_NO_CONTENT); return HttpResponse(MHD_HTTP_NO_CONTENT);
} }
return HttpResponse(MHD_HTTP_FORBIDDEN); return HttpResponse(MHD_HTTP_FORBIDDEN, runtime_get_json_error());
} }
HttpResponse cb_delete_monitor(const HttpRequest& request) HttpResponse cb_delete_monitor(const HttpRequest& request)
@ -335,7 +335,7 @@ HttpResponse cb_delete_monitor(const HttpRequest& request)
return HttpResponse(MHD_HTTP_NO_CONTENT); return HttpResponse(MHD_HTTP_NO_CONTENT);
} }
return HttpResponse(MHD_HTTP_FORBIDDEN); return HttpResponse(MHD_HTTP_FORBIDDEN, runtime_get_json_error());
} }
HttpResponse cb_all_servers(const HttpRequest& request) HttpResponse cb_all_servers(const HttpRequest& request)
@ -352,7 +352,7 @@ HttpResponse cb_get_server(const HttpRequest& request)
return HttpResponse(MHD_HTTP_OK, server_to_json(server, request.host())); return HttpResponse(MHD_HTTP_OK, server_to_json(server, request.host()));
} }
return HttpResponse(MHD_HTTP_INTERNAL_SERVER_ERROR); return HttpResponse(MHD_HTTP_INTERNAL_SERVER_ERROR, runtime_get_json_error());
} }
HttpResponse cb_all_services(const HttpRequest& request) HttpResponse cb_all_services(const HttpRequest& request)
@ -369,7 +369,7 @@ HttpResponse cb_get_service(const HttpRequest& request)
return HttpResponse(MHD_HTTP_OK, service_to_json(service, request.host())); return HttpResponse(MHD_HTTP_OK, service_to_json(service, request.host()));
} }
return HttpResponse(MHD_HTTP_INTERNAL_SERVER_ERROR); return HttpResponse(MHD_HTTP_INTERNAL_SERVER_ERROR, runtime_get_json_error());
} }
HttpResponse cb_get_service_listeners(const HttpRequest& request) HttpResponse cb_get_service_listeners(const HttpRequest& request)
@ -392,7 +392,7 @@ HttpResponse cb_get_filter(const HttpRequest& request)
return HttpResponse(MHD_HTTP_OK, filter_to_json(filter, request.host())); return HttpResponse(MHD_HTTP_OK, filter_to_json(filter, request.host()));
} }
return HttpResponse(MHD_HTTP_INTERNAL_SERVER_ERROR); return HttpResponse(MHD_HTTP_INTERNAL_SERVER_ERROR, runtime_get_json_error());
} }
HttpResponse cb_all_monitors(const HttpRequest& request) HttpResponse cb_all_monitors(const HttpRequest& request)
@ -409,7 +409,7 @@ HttpResponse cb_get_monitor(const HttpRequest& request)
return HttpResponse(MHD_HTTP_OK, monitor_to_json(monitor, request.host())); return HttpResponse(MHD_HTTP_OK, monitor_to_json(monitor, request.host()));
} }
return HttpResponse(MHD_HTTP_INTERNAL_SERVER_ERROR); return HttpResponse(MHD_HTTP_INTERNAL_SERVER_ERROR, runtime_get_json_error());
} }
HttpResponse cb_all_sessions(const HttpRequest& request) HttpResponse cb_all_sessions(const HttpRequest& request)