Я пишу сервис JSON на C # (файл .ashx). При успешном запросе к сервису я возвращаю некоторые данные JSON. Если запрос не выполняется, либо из-за того, что было сгенерировано исключение (например, тайм-аут базы данных), либо из-за того, что запрос был каким-то образом неправильным (например, в качестве аргумента был указан идентификатор, который не существует в базе данных), как должна ответить служба? Какие коды состояния HTTP являются разумными и следует ли возвращать какие-либо данные, если таковые имеются?
Я ожидаю, что служба будет в основном вызываться из jQuery с использованием плагина jQuery.form, есть ли у jQuery или этого плагина какой-либо способ обработки ответа об ошибке по умолчанию?
РЕДАКТИРОВАТЬ: Я решил, что буду использовать jQuery + .ashx + HTTP [коды состояния] в случае успеха, я верну JSON, но при ошибке я верну строку, поскольку, похоже, это то, что вариант ошибки для jQuery. ajax ожидает.
источник
См. Этот вопрос, чтобы получить представление о передовых методах работы в вашей ситуации.
Предложение верхнего уровня (из указанной ссылки) состоит в том, чтобы стандартизировать структуру ответа (как для успеха, так и для неудачи), которую ищет ваш обработчик, перехватывая все исключения на уровне сервера и преобразовывая их в ту же структуру. Например (из этого ответа ):
{ success:false, general_message:"You have reached your max number of Foos for the day", errors: { last_name:"This field is required", mrn:"Either SSN or MRN must be entered", zipcode:"996852 is not in Bernalillo county. Only Bernalillo residents are eligible" } }
Это подход, который использует stackoverflow (если вам интересно, как другие делают подобные вещи); операции записи, такие как голосование, имеют
"Success"
и"Message"
поля, независимо от того, было ли голосование разрешено или нет:{ Success:true, NewScore:1, Message:"", LastVoteTypeId:3 }
Как отметил @ Phil.H , вы должны быть последовательны в том, что выбираете. Легче сказать, чем сделать (как и все в разработке!).
Например, если вы слишком быстро отправляете комментарии на SO, вместо того, чтобы быть последовательными и возвращать
{ Success: false, Message: "Can only comment once every blah..." }
SO выдаст исключение сервера (
HTTP 500
) и поймает его вerror
обратном вызове.Насколько "кажется правильным" использовать jQuery +
.ashx
+ HTTP [коды состояния] IMO, это добавит больше сложности вашей клиентской базе кода, чем это того стоит. Поймите, что jQuery «обнаруживает» не коды ошибок, а скорее отсутствие кода успеха. Это важное различие при попытке создать клиента на основе кодов ответа http с помощью jQuery. У вас есть только два варианта (был ли это «успех» или «ошибка»?), Которые вы должны продолжить самостоятельно. Если у вас небольшое количество WebServices, управляющих небольшим количеством страниц, это может быть нормально, но что-то более крупное может стать беспорядочным.В
.asmx
WebService (или WCF, если на то пошло) гораздо естественнее возвращать настраиваемый объект, чем настраивать код состояния HTTP. Кроме того, вы бесплатно получаете сериализацию JSON.источник
Использование кодов состояния HTTP было бы способом RESTful сделать это, но это предполагает, что вы можете сделать остальную часть интерфейса RESTful, используя URI ресурсов и так далее.
По правде говоря, определите интерфейс так, как вам нравится (верните объект ошибки, например, с подробным описанием свойства с ошибкой и фрагмент HTML, который объясняет это, и т.д.), но как только вы выбрали что-то, что работает в прототипе , будьте безжалостно последовательны.
источник
error
(возможно, пустого) массива и добавлениеfatal_error: bool
параметра даст вам некоторую гибкость.Я думаю, что если вы просто выдавите исключение, оно должно быть обработано в обратном вызове jQuery, который передается для параметра «ошибка» . (Мы также регистрируем это исключение на стороне сервера в центральном журнале). Никакого специального кода ошибки HTTP не требуется, но мне любопытно посмотреть, что делают и другие люди.
Я так и делаю, но это всего лишь мои 0,02 доллара.
Если вы собираетесь использовать RESTful и возвращать коды ошибок, попробуйте придерживаться стандартных кодов, установленных W3C: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
источник
Я потратил несколько часов на решение этой проблемы. Мое решение основано на следующих пожеланиях / требованиях:
Я создаю HandleErrorAttribute (подробности см. В комментариях к коду). Некоторые детали, включая «использование», были упущены, поэтому код может не компилироваться. Я добавляю фильтр к глобальным фильтрам при инициализации приложения в Global.asax.cs вот так:
GlobalFilters.Filters.Add(new UnikHandleErrorAttribute());
Атрибут:
namespace Foo { using System; using System.Diagnostics; using System.Linq; using System.Net; using System.Reflection; using System.Web; using System.Web.Mvc; /// <summary> /// Generel error handler attribute for Foo MVC solutions. /// It handles uncaught exceptions from controller actions. /// It outputs trace information. /// If custom errors are enabled then the following is performed: /// <ul> /// <li>If the controller action return type is <see cref="JsonResult"/> then a <see cref="JsonResult"/> object with a <c>message</c> property is returned. /// If the exception is of type <see cref="MySpecialExceptionWithUserMessage"/> it's message will be used as the <see cref="JsonResult"/> <c>message</c> property value. /// Otherwise a localized resource text will be used.</li> /// </ul> /// Otherwise the exception will pass through unhandled. /// </summary> [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public sealed class FooHandleErrorAttribute : HandleErrorAttribute { private readonly TraceSource _TraceSource; /// <summary> /// <paramref name="traceSource"/> must not be null. /// </summary> /// <param name="traceSource"></param> public FooHandleErrorAttribute(TraceSource traceSource) { if (traceSource == null) throw new ArgumentNullException(@"traceSource"); _TraceSource = traceSource; } public TraceSource TraceSource { get { return _TraceSource; } } /// <summary> /// Ctor. /// </summary> public FooHandleErrorAttribute() { var className = typeof(FooHandleErrorAttribute).FullName ?? typeof(FooHandleErrorAttribute).Name; _TraceSource = new TraceSource(className); } public override void OnException(ExceptionContext filterContext) { var actionMethodInfo = GetControllerAction(filterContext.Exception); // It's probably an error if we cannot find a controller action. But, hey, what should we do about it here? if(actionMethodInfo == null) return; var controllerName = filterContext.Controller.GetType().FullName; // filterContext.RouteData.Values[@"controller"]; var actionName = actionMethodInfo.Name; // filterContext.RouteData.Values[@"action"]; // Log the exception to the trace source var traceMessage = string.Format(@"Unhandled exception from {0}.{1} handled in {2}. Exception: {3}", controllerName, actionName, typeof(FooHandleErrorAttribute).FullName, filterContext.Exception); _TraceSource.TraceEvent(TraceEventType.Error, TraceEventId.UnhandledException, traceMessage); // Don't modify result if custom errors not enabled //if (!filterContext.HttpContext.IsCustomErrorEnabled) // return; // We only handle actions with return type of JsonResult - I don't use AjaxRequestExtensions.IsAjaxRequest() because ajax requests does NOT imply JSON result. // (The downside is that you cannot just specify the return type as ActionResult - however I don't consider this a bad thing) if (actionMethodInfo.ReturnType != typeof(JsonResult)) return; // Handle JsonResult action exception by creating a useful JSON object which can be used client side // Only provide error message if we have an MySpecialExceptionWithUserMessage. var jsonMessage = FooHandleErrorAttributeResources.Error_Occured; if (filterContext.Exception is MySpecialExceptionWithUserMessage) jsonMessage = filterContext.Exception.Message; filterContext.Result = new JsonResult { Data = new { message = jsonMessage, // Only include stacktrace information in development environment stacktrace = MyEnvironmentHelper.IsDebugging ? filterContext.Exception.StackTrace : null }, // Allow JSON get requests because we are already using this approach. However, we should consider avoiding this habit. JsonRequestBehavior = JsonRequestBehavior.AllowGet }; // Exception is now (being) handled - set the HTTP error status code and prevent caching! Otherwise you'll get an HTTP 200 status code and running the risc of the browser caching the result. filterContext.ExceptionHandled = true; filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError; // Consider using more error status codes depending on the type of exception filterContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache); // Call the overrided method base.OnException(filterContext); } /// <summary> /// Does anybody know a better way to obtain the controller action method info? /// See http://stackoverflow.com/questions/2770303/how-to-find-in-which-controller-action-an-error-occurred. /// </summary> /// <param name="exception"></param> /// <returns></returns> private static MethodInfo GetControllerAction(Exception exception) { var stackTrace = new StackTrace(exception); var frames = stackTrace.GetFrames(); if(frames == null) return null; var frame = frames.FirstOrDefault(f => typeof(IController).IsAssignableFrom(f.GetMethod().DeclaringType)); if (frame == null) return null; var actionMethod = frame.GetMethod(); return actionMethod as MethodInfo; } } }
Я разработал следующий плагин jQuery для удобства использования на стороне клиента:
(function ($, undefined) { "using strict"; $.FooGetJSON = function (url, data, success, error) { /// <summary> /// ********************************************************** /// * UNIK GET JSON JQUERY PLUGIN. * /// ********************************************************** /// This plugin is a wrapper for jQuery.getJSON. /// The reason is that jQuery.getJSON success handler doesn't provides access to the JSON object returned from the url /// when a HTTP status code different from 200 is encountered. However, please note that whether there is JSON /// data or not depends on the requested service. if there is no JSON data (i.e. response.responseText cannot be /// parsed as JSON) then the data parameter will be undefined. /// /// This plugin solves this problem by providing a new error handler signature which includes a data parameter. /// Usage of the plugin is much equal to using the jQuery.getJSON method. Handlers can be added etc. However, /// the only way to obtain an error handler with the signature specified below with a JSON data parameter is /// to call the plugin with the error handler parameter directly specified in the call to the plugin. /// /// success: function(data, textStatus, jqXHR) /// error: function(data, jqXHR, textStatus, errorThrown) /// /// Example usage: /// /// $.FooGetJSON('/foo', { id: 42 }, function(data) { alert('Name :' + data.name); }, function(data) { alert('Error: ' + data.message); }); /// </summary> // Call the ordinary jQuery method var jqxhr = $.getJSON(url, data, success); // Do the error handler wrapping stuff to provide an error handler with a JSON object - if the response contains JSON object data if (typeof error !== "undefined") { jqxhr.error(function(response, textStatus, errorThrown) { try { var json = $.parseJSON(response.responseText); error(json, response, textStatus, errorThrown); } catch(e) { error(undefined, response, textStatus, errorThrown); } }); } // Return the jQueryXmlHttpResponse object return jqxhr; }; })(jQuery);
Что я получаю от всего этого? Конечный результат:
Пример на стороне клиента:
var success = function(data) { alert(data.myjsonobject.foo); }; var onError = function(data) { var message = "Error"; if(typeof data !== "undefined") message += ": " + data.message; alert(message); }; $.FooGetJSON(url, params, onSuccess, onError);
Комментарии приветствуются! Я, наверное, когда-нибудь напишу об этом решении ...
источник
Я определенно вернул бы ошибку 500 с объектом JSON, описывающим состояние ошибки, подобно тому, как возвращается ошибка «ScriptService» ASP.NET AJAX . Я считаю, что это довольно стандартно. Определенно приятно иметь такую последовательность при обработке потенциально неожиданных ошибок.
Кроме того, почему бы просто не использовать встроенные функции .NET, если вы пишете их на C #? Службы WCF и ASMX упрощают сериализацию данных в формате JSON, не изобретая велосипед.
источник
422 Unprocessable Entity
Для подобных ошибок используются скаффолды Rails . См. RFC 4918 для получения дополнительной информации.источник
Да, вам следует использовать коды состояния HTTP. А также желательно возвращать описания ошибок в несколько стандартизированном формате JSON, например в предложении Ноттингема , см. Отчет об ошибках apigility :
источник
Если пользователь предоставляет недопустимые данные, это обязательно должно быть
400 Bad Request
( запрос содержит неверный синтаксис или не может быть выполнен. )источник
Я не думаю, что вам следует возвращать какие-либо коды ошибок http, а скорее настраиваемые исключения, которые полезны для клиентской части приложения, чтобы интерфейс знал, что на самом деле произошло. Я бы не стал скрывать реальные проблемы с кодами ошибок 404 или чем-то подобным.
источник
Для ошибок сервера / протокола я бы попытался использовать как можно более REST / HTTP (сравните это с тем, как вы вводите URL-адрес в своем браузере):
Для конкретных ошибок домена / бизнес-логики я бы сказал, что протокол используется правильно и нет внутренней ошибки сервера, поэтому ответьте с ошибкой объекта JSON / XML или того, что вы предпочитаете описывать свои данные (сравните это с тем, что вы заполняете формы на сайте):
источник