C # (. ashx 파일)으로 JSON 서비스를 작성하고 있습니다. 서비스에 대한 성공적인 요청에서 일부 JSON 데이터를 반환합니다. 예외가 발생했거나 (예 : 데이터베이스 시간 초과) 요청이 어떤 방식 으로든 잘못 되었기 때문에 (예 : 데이터베이스에 존재하지 않는 ID가 인수로 제공 되었기 때문에) 요청이 실패하면 서비스는 어떻게 응답해야합니까? 어떤 HTTP 상태 코드가 합리적이며 데이터가 있으면 반환해야합니까?
서비스가 주로 jQuery.form 플러그인을 사용하여 jQuery에서 호출 될 것으로 예상하고 있는데, jQuery 또는이 플러그인에 오류 응답을 처리하는 기본 방법이 있습니까?
편집 : 성공하면 jQuery + .ashx + HTTP [상태 코드]를 사용하기로 결정했습니다. JSON을 반환하지만 오류가 발생하면 문자열을 반환합니다. 이것이 jQuery의 오류 옵션 인 것처럼 보입니다. 아약스는 기대합니다.
답변
반환하는 HTTP 상태 코드는 발생한 오류 유형에 따라 달라집니다. 데이터베이스에 ID가 없으면 404를 반환합니다. 사용자에게 Ajax 호출을 할 수있는 충분한 권한이 없으면 403을 반환합니다. 레코드를 찾기 전에 데이터베이스 시간이 초과되면 500 (서버 오류)을 반환합니다.
jQuery는 이러한 오류 코드를 자동으로 감지하고 Ajax 호출에서 정의한 콜백 함수를 실행합니다. 문서 : http://api.jquery.com/jQuery.ajax/
$.ajax
오류 콜백 의 간단한 예 :
$.ajax({
type: 'POST',
url: '/some/resource',
success: function(data, textStatus) {
// Handle success
},
error: function(xhr, textStatus, errorThrown) {
// Handle error
}
});
답변
상황에 맞는 모범 사례에 대한 통찰력을 얻으려면 이 질문 을 참조하십시오 .
(상기 링크에서) 최상위 제안은 핸들러가 찾는 응답 구조 (성공 및 실패 모두에 대해)를 표준화하고 서버 계층에서 모든 예외를 포착하여 동일한 구조로 변환하는 것입니다. 예를 들어 ( 이 답변에서 ) :
{
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가 사용하는 접근 방식입니다 (다른 사람들이 이런 종류의 일을 어떻게하는지 궁금한 경우); 투표가 허용되었는지 여부에 관계없이 voting have "Success"
및 "Message"
fields 와 같은 쓰기 작업 :
{ Success:true, NewScore:1, Message:"", LastVoteTypeId:3 }
으로 Phil.H 지적 @ , 당신은 당신이 무엇을 선택에 일치해야합니다. 이것은 말처럼 쉬운 일입니다 (개발중인 모든 것이 그렇듯이!).
예를 들어, 일관성 있고 답답하지 않고 너무 빨리 댓글을 제출하면
{ Success: false, Message: "Can only comment once every blah..." }
그래서 서버 예외 ( HTTP 500
)를 던지고 error
콜백 에서 포착합니다 .
jQuery + .ashx
+ HTTP [상태 코드] IMO 를 사용하는 것이 “적절하게 느껴지는”만큼 클라이언트 측 코드베이스에 가치가있는 것보다 더 복잡해집니다. jQuery는 오류 코드를 “감지”하는 것이 아니라 성공 코드의 부족을 인식합니다. 이것은 jQuery를 사용하여 http 응답 코드를 중심으로 클라이언트를 설계 할 때 중요한 차이점입니다. 두 가지 선택 ( “성공”또는 “오류”였습니까?) 만 얻을 수 있으며,이를 직접 분기해야합니다. 적은 수의 페이지를 구동하는 적은 수의 WebService가 있으면 괜찮을 수 있지만 더 큰 규모는 지저분해질 수 있습니다.
.asmx
WebService (또는 WCF)에서는 HTTP 상태 코드를 사용자 지정하는 것보다 사용자 지정 개체를 반환하는 것이 훨씬 더 자연 스럽습니다 . 또한 JSON 직렬화를 무료로받을 수 있습니다.
답변
HTTP 상태 코드를 사용하는 것은 RESTful 방법이지만 리소스 URI 등을 사용하여 나머지 인터페이스를 RESTful로 만들 것을 제안합니다.
사실, 인터페이스를 원하는대로 정의하십시오 (예 : 오류가있는 속성을 자세히 설명하는 오류 개체 및이를 설명하는 HTML 청크 등을 반환).하지만 프로토 타입에서 작동하는 것을 결정한 후에는 , 무자비하게 일관성을 유지하십시오.
답변
예외를 버블 링 하면 ‘error’옵션에 전달 되는 jQuery 콜백 에서 처리되어야한다고 생각합니다 . (또한 서버 측의이 예외를 중앙 로그에 기록합니다). 특별한 HTTP 오류 코드는 필요하지 않지만 다른 사람들도 무엇을하는지 궁금합니다.
이게 내가하는 일이지만 그건 내 $ .02
RESTful이되어 오류 코드를 반환하려면 W3C에서 규정 한 표준 코드를 따르십시오 : http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
답변
이 문제를 해결하는 데 몇 시간을 보냈습니다. 내 솔루션은 다음과 같은 희망 사항 / 요구 사항을 기반으로합니다.
- 모든 JSON 컨트롤러 작업에 반복적 인 상용구 오류 처리 코드가 없어야합니다.
- HTTP (오류) 상태 코드를 보존합니다. 왜? 높은 수준의 우려는 낮은 수준의 구현에 영향을 미치지 않기 때문입니다.
- 서버에서 오류 / 예외가 발생하면 JSON 데이터를 가져올 수 있습니다. 왜? 풍부한 오류 정보를 원할 수 있기 때문입니다. 예 : 오류 메시지, 도메인 별 오류 상태 코드, 스택 추적 (디버그 / 개발 환경에서).
- 사용 편의성 클라이언트 측-jQuery를 사용하는 것이 좋습니다.
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);
이 모든 것에서 무엇을 얻습니까? 최종 결과는
- 내 컨트롤러 작업에는 HandleErrorAttributes에 대한 요구 사항이 없습니다.
- 내 컨트롤러 작업에는 반복적 인 보일러 플레이트 오류 처리 코드가 포함되어 있지 않습니다.
- 로깅 및 기타 오류 처리 관련 항목을 쉽게 변경할 수있는 단일 오류 처리 코드가 있습니다.
- 간단한 요구 사항 : JsonResult를 반환하는 컨트롤러 작업에는 ActionResult와 같은 일부 기본 유형이 아닌 반환 유형 JsonResult가 있어야합니다. 이유 : FooHandleErrorAttribute의 코드 주석을 참조하십시오.
클라이언트 측 예 :
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);
의견을 환영합니다! 언젠가는이 솔루션에 대해 블로그를 작성할 것입니다.
답변
ASP.NET AJAX “ScriptService”오류가를 반환하는 방식 과 유사하게 오류 조건을 설명하는 JSON 개체와 함께 500 오류를 확실히 반환 합니다 . 나는 이것이 상당히 표준이라고 믿습니다. 잠재적으로 예상치 못한 오류 조건을 처리 할 때 일관성을 유지하는 것이 좋습니다.
따로, C #으로 작성하는 경우 .NET의 기본 제공 기능을 사용하지 않는 이유는 무엇입니까? WCF 및 ASMX 서비스를 사용하면 바퀴를 다시 만들지 않고도 데이터를 JSON으로 쉽게 직렬화 할 수 있습니다.