Ich schreibe einen JSON-Dienst in C # (.ashx-Datei). Bei erfolgreicher Anforderung an den Dienst gebe ich einige JSON-Daten zurück. Wenn die Anforderung fehlschlägt, entweder weil eine Ausnahme ausgelöst wurde (z. B. Datenbank-Timeout) oder weil die Anforderung in irgendeiner Weise falsch war (z. B. wurde eine ID angegeben, die nicht in der Datenbank vorhanden ist), wie sollte der Dienst reagieren? Welche HTTP-Statuscodes sind sinnvoll und sollte ich gegebenenfalls Daten zurückgeben?
Ich gehe davon aus, dass der Dienst hauptsächlich von jQuery mit dem Plugin jQuery.form aufgerufen wird. Verfügt jQuery oder dieses Plugin über eine Standardmethode zur Behandlung einer Fehlerantwort?
BEARBEITEN: Ich habe beschlossen, bei Erfolg jQuery + .ashx + HTTP [Statuscodes] zu verwenden. Ich gebe JSON zurück, aber bei einem Fehler gebe ich eine Zeichenfolge zurück, da dies anscheinend die Fehleroption für jQuery ist. Ajax erwartet.
quelle
In dieser Frage erhalten Sie einen Einblick in Best Practices für Ihre Situation.
Der Topline-Vorschlag (über diesen Link) besteht darin, eine Antwortstruktur (sowohl für Erfolg als auch für Misserfolg) zu standardisieren, nach der Ihr Handler sucht, alle Ausnahmen auf der Serverebene abzufangen und in dieselbe Struktur zu konvertieren. Zum Beispiel (aus dieser Antwort ):
{ 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" } }
Dies ist der Ansatz, den Stackoverflow verwendet (falls Sie sich gefragt haben, wie andere so etwas tun). Schreibvorgänge wie Abstimmungen haben
"Success"
und"Message"
Felder, unabhängig davon, ob die Abstimmung erlaubt war oder nicht:{ Success:true, NewScore:1, Message:"", LastVoteTypeId:3 }
Wie @ Phil.H betonte , sollten Sie bei jeder Auswahl konsequent sein. Dies ist leichter gesagt als getan (wie alles in der Entwicklung!).
Zum Beispiel, wenn Sie Kommentare zu SO zu schnell einreichen, anstatt konsistent zu sein und zurückzukehren
{ Success: false, Message: "Can only comment once every blah..." }
SO löst eine Serverausnahme (
HTTP 500
) aus und fängt sie in ihremerror
Rückruf ab.So sehr es sich "richtig anfühlt", jQuery +
.ashx
+ HTTP [Statuscodes] IMO zu verwenden, wird Ihre clientseitige Codebasis komplexer, als es sich lohnt. Stellen Sie fest, dass jQuery keine Fehlercodes "erkennt", sondern das Fehlen eines Erfolgscodes. Dies ist eine wichtige Unterscheidung, wenn Sie versuchen, einen Client anhand von http-Antwortcodes mit jQuery zu entwerfen. Sie haben nur zwei Möglichkeiten (war es ein "Erfolg" oder ein "Fehler"?), Die Sie selbst weiter verzweigen müssen. Wenn Sie eine kleine Anzahl von WebServices haben, die eine kleine Anzahl von Seiten steuern, ist dies möglicherweise in Ordnung, aber alles, was größer ist, kann unordentlich werden.In einem
.asmx
WebService (oder WCF) ist es viel natürlicher , ein benutzerdefiniertes Objekt zurückzugeben, als den HTTP-Statuscode anzupassen. Außerdem erhalten Sie die JSON-Serialisierung kostenlos.quelle
Die Verwendung von HTTP-Statuscodes wäre eine RESTful-Methode, dies würde jedoch bedeuten, dass Sie den Rest der Schnittstelle mithilfe von Ressourcen-URIs usw. RESTful machen.
Definieren Sie die Schnittstelle in Wahrheit so, wie Sie möchten (geben Sie ein Fehlerobjekt zurück, z. B. die Eigenschaft mit dem Fehler detailliert, und einen Teil des HTML-Codes, der dies erklärt, usw.), aber sobald Sie sich für etwas entschieden haben, das in einem Prototyp funktioniert rücksichtslos konsequent sein.
quelle
error
ein (möglicherweise leeres) Array erstellen und einenfatal_error: bool
Parameter hinzufügen .Ich denke, wenn Sie nur eine Ausnahme auslösen, sollte dies im jQuery-Rückruf behandelt werden, der für die Option 'error' übergeben wird . (Wir protokollieren diese Ausnahme auch auf der Serverseite in einem zentralen Protokoll.) Kein spezieller HTTP-Fehlercode erforderlich, aber ich bin gespannt, was andere Leute auch tun.
Das ist was ich tue, aber das sind nur meine $ .02
Wenn Sie RESTful sein und Fehlercodes zurückgeben möchten, halten Sie sich an die vom W3C festgelegten Standardcodes: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
quelle
Ich habe einige Stunden damit verbracht, dieses Problem zu lösen. Meine Lösung basiert auf folgenden Wünschen / Anforderungen:
Ich erstelle ein HandleErrorAttribute (Erläuterungen zu den Details finden Sie in den Codekommentaren). Einige Details, einschließlich "Verwendungen", wurden weggelassen, sodass der Code möglicherweise nicht kompiliert werden kann. Ich füge den Filter den globalen Filtern während der Anwendungsinitialisierung in Global.asax.cs folgendermaßen hinzu:
GlobalFilters.Filters.Add(new UnikHandleErrorAttribute());
Attribut:
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; } } }
Ich habe das folgende jQuery-Plugin für die clientseitige Benutzerfreundlichkeit entwickelt:
(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);
Was bekomme ich von all dem? Das Endergebnis ist das
Client-seitiges Beispiel:
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);
Kommentare sind herzlich willkommen! Ich werde wahrscheinlich eines Tages über diese Lösung bloggen ...
quelle
Ich würde definitiv einen 500-Fehler mit einem JSON-Objekt zurückgeben, das die Fehlerbedingung beschreibt, ähnlich wie ein ASP.NET AJAX "ScriptService" -Fehler zurückkehrt . Ich glaube, das ist ziemlich normal. Es ist auf jeden Fall schön, diese Konsistenz bei der Behandlung potenziell unerwarteter Fehlerbedingungen zu haben.
Warum nicht einfach die in .NET integrierte Funktionalität verwenden, wenn Sie sie in C # schreiben? WCF- und ASMX-Dienste machen es einfach, Daten als JSON zu serialisieren, ohne das Rad neu zu erfinden.
quelle
Schienengerüste werden
422 Unprocessable Entity
für diese Art von Fehlern verwendet. Weitere Informationen finden Sie in RFC 4918 .quelle
Ja, Sie sollten HTTP-Statuscodes verwenden. Und auch vorzugsweise Fehlerbeschreibungen in einem etwas standardisierten JSON-Format zurückgeben, wie Nottinghams Vorschlag , siehe Apigility Error Reporting :
quelle
Wenn der Benutzer ungültige Daten angibt, sollte dies auf jeden Fall eine sein
400 Bad Request
( Die Anforderung enthält eine schlechte Syntax oder kann nicht erfüllt werden. )quelle
Ich denke nicht, dass Sie irgendwelche http-Fehlercodes zurückgeben sollten, sondern benutzerdefinierte Ausnahmen, die für das Client-Ende der Anwendung nützlich sind, damit die Schnittstelle weiß, was tatsächlich passiert ist. Ich würde nicht versuchen, echte Probleme mit 404-Fehlercodes oder etwas Ähnlichem zu maskieren.
quelle
Bei Server- / Protokollfehlern würde ich versuchen, so REST / HTTP wie möglich zu sein (Vergleichen Sie dies mit der Eingabe von URLs in Ihrem Browser):
Für domänen- / geschäftslogikspezifische Fehler würde ich sagen, dass das Protokoll richtig verwendet wird und kein serverinterner Fehler vorliegt. Antworten Sie daher mit einem Fehler-JSON / XML-Objekt oder einem beliebigen Fehler, mit dem Sie Ihre Daten beschreiben möchten (Vergleichen Sie dies mit dem Ausfüllen Formulare auf einer Website):
quelle