programing

오류 상태 코드 MVC와 함께 JSON을 반환합니다.

lastmoon 2023. 4. 2. 11:50
반응형

오류 상태 코드 MVC와 함께 JSON을 반환합니다.

클라이언트가 적절한 액션을 취할 수 있도록 이 링크의 조언에 따라 콜에 대한 오류를 컨트롤러에 반환하려고 했습니다.컨트롤러는 jquery AJAX를 통해 javascript에 의해 호출됩니다.상태를 error로 설정하지 않은 경우에만 Json 객체를 돌려받습니다.여기 샘플 코드가 있습니다.

if (response.errors.Length > 0)
   Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(response);

상태 코드를 설정하지 않으면 Json이 나타납니다.상태 코드를 설정하면 상태 코드가 반환되지만 Json 오류 개체는 반환되지 않습니다.

업데이트 오류 개체를 JSON으로 전송하여 Ajax 오류 콜백을 처리할 수 있도록 합니다.

제가 찾은 솔루션 중 가장 깔끔한 것은 원래 구현을 확장하고 HttpStatusCode를 지정할 수 있는 자체 JsonResult를 만드는 것입니다.

public class JsonHttpStatusResult : JsonResult
{
    private readonly HttpStatusCode _httpStatus;

    public JsonHttpStatusResult(object data, HttpStatusCode httpStatus)
    {
        Data = data;
        _httpStatus = httpStatus;
    }

    public override void ExecuteResult(ControllerContext context)
    {
        context.RequestContext.HttpContext.Response.StatusCode = (int)_httpStatus;
        base.ExecuteResult(context);
    }
}

다음으로 컨트롤러 동작에서 다음과 같이 사용할 수 있습니다.

if(thereWereErrors)
{
    var errorModel = new { error = "There was an error" };
    return new JsonHttpStatusResult(errorModel, HttpStatusCode.InternalServerError);
}

나는 여기서 해결책을 찾았다.

MVC의 기본 동작을 재정의하기 위해 작업 필터를 생성해야 했습니다.

제 예외수업입니다.

class ValidationException : ApplicationException
{
    public JsonResult exceptionDetails;
    public ValidationException(JsonResult exceptionDetails)
    {
        this.exceptionDetails = exceptionDetails;
    }
    public ValidationException(string message) : base(message) { }
    public ValidationException(string message, Exception inner) : base(message, inner) { }
    protected ValidationException(
    System.Runtime.Serialization.SerializationInfo info,
    System.Runtime.Serialization.StreamingContext context)
        : base(info, context) { }
}

JSON을 초기화하는 컨스트럭터가 있습니다.액션 필터는 다음과 같습니다.

public class HandleUIExceptionAttribute : FilterAttribute, IExceptionFilter
{
    public virtual void OnException(ExceptionContext filterContext)
    {
        if (filterContext == null)
        {
            throw new ArgumentNullException("filterContext");
        }
        if (filterContext.Exception != null)
        {
            filterContext.ExceptionHandled = true;
            filterContext.HttpContext.Response.Clear();
            filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
            filterContext.HttpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError;
            filterContext.Result = ((ValidationException)filterContext.Exception).myJsonError;
        }
    }

이제 액션 필터가 생겼으니 컨트롤러를 필터 속성으로 꾸밉니다.

[HandleUIException]
public JsonResult UpdateName(string objectToUpdate)
{
   var response = myClient.ValidateObject(objectToUpdate);
   if (response.errors.Length > 0)
     throw new ValidationException(Json(response));
}

오류가 발생하면 IExceptionFilter를 구현하는 액션필터가 호출되고 오류 콜백 시 클라이언트의 Json이 반환됩니다.

이 문제에 대한 매우 우아한 해결책이 있습니다.web.config를 사용하여 사이트를 설정하기만 하면 됩니다.

<system.webServer>
    <httpErrors errorMode="DetailedLocalOnly" existingResponse="PassThrough"/>
</system.webServer>

출처 : https://serverfault.com/questions/123729/iis-is-overriding-my-response-content-if-i-manually-set-the-response-statuscode

Json에 오류를 보내는 간단한 방법은 응답 개체의 Http 상태 코드를 제어하고 사용자 정의 오류 메시지를 설정하는 것입니다.

컨트롤러

public JsonResult Create(MyObject myObject) 
{
  //AllFine
  return Json(new { IsCreated = True, Content = ViewGenerator(myObject));

  //Use input may be wrong but nothing crashed
  return Json(new { IsCreated = False, Content = ViewGenerator(myObject));  

  //Error
  Response.StatusCode = (int)HttpStatusCode.InternalServerError;
  return Json(new { IsCreated = false, ErrorMessage = 'My error message');
}

JS

$.ajax({
     type: "POST",
     dataType: "json",
     url: "MyController/Create",
     data: JSON.stringify(myObject),
     success: function (result) {
       if(result.IsCreated)
     {
    //... ALL FINE
     }
     else
     {
    //... Use input may be wrong but nothing crashed
     }
   },
    error: function (error) {
            alert("Error:" + erro.responseJSON.ErrorMessage ); //Error
        }
  });

Richard Garside의 답변을 바탕으로 ASP를 소개합니다.넷코어 버전

public class JsonErrorResult : JsonResult
{
    private readonly HttpStatusCode _statusCode;

    public JsonErrorResult(object json) : this(json, HttpStatusCode.InternalServerError)
    {
    }

    public JsonErrorResult(object json, HttpStatusCode statusCode) : base(json)
    {
        _statusCode = statusCode;
    }

    public override void ExecuteResult(ActionContext context)
    {
        context.HttpContext.Response.StatusCode = (int)_statusCode;
        base.ExecuteResult(context);
    }

    public override Task ExecuteResultAsync(ActionContext context)
    {
        context.HttpContext.Response.StatusCode = (int)_statusCode;
        return base.ExecuteResultAsync(context);
    }
}

다음으로 컨트롤러에서 다음과 같이 돌아갑니다.

// Set a json object to return. The status code defaults to 500
return new JsonErrorResult(new { message = "Sorry, an internal error occurred."});

// Or you can override the status code
return new JsonErrorResult(new { foo = "bar"}, HttpStatusCode.NotFound);

(다른 stackoverflow 응답으로부터 취득한) 동작은, 플래그를 설정하는 것입니다.

Response.TrySkipIisCustomErrors = true;

다음과 같이 상태 코드를 설정한 후 JSON 오류 개체를 직접 반환해야 합니다.

if (BadRequest)
{
    Dictionary<string, object> error = new Dictionary<string, object>();
    error.Add("ErrorCode", -1);
    error.Add("ErrorMessage", "Something really bad happened");
    return Json(error);
}

또 다른 방법은, 다른 방법으로는JsonErrorModel그리고 그것을 채운다.

public class JsonErrorModel
{
    public int ErrorCode { get; set;}

    public string ErrorMessage { get; set; }
}

public ActionResult SomeMethod()
{

    if (BadRequest)
    {
        var error = new JsonErrorModel
        {
            ErrorCode = -1,
            ErrorMessage = "Something really bad happened"
        };

        return Json(error);
    }

   //Return valid response
}

여기도 정답을 보세요.

「HTTP 레벨 에러」(에러 코드의 용도) 또는 「어플리케이션 레벨 에러」(커스텀 JSON 응답의 용도) 중 어느 쪽을 사용할지를 결정해야 합니다.

오류 코드가 2xx(성공 범위)가 아닌 값으로 설정되어 있는 경우 HTTP를 사용하는 대부분의 상위 수준 개체는 응답 스트림을 조사하지 않습니다.이 경우 에러 코드를 failure(403 또는 500)로 명시적으로 설정하고 XMLHttp 오브젝트에 응답 본문을 무시하도록 강제합니다.

수정 방법 - 클라이언트 측에서 오류 상태를 처리하거나 오류 코드를 설정하지 않고 오류 정보와 함께 JSON을 반환합니다(자세한 내용은 Sbossb 응답 참조).

응답 중 일부는 예외가 느려지고 OnException 덮어쓰기로 처리되는 것에 의존합니다.제 경우, 예를 들어 사용자가 잘못된 ID로 통과했다면 잘못된 요청 등의 상태를 반환하고 싶었습니다.Controller Context를 사용하는 것이 효과적입니다.

var jsonResult = new JsonResult { JsonRequestBehavior = JsonRequestBehavior.AllowGet, Data = "whoops" };

ControllerContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;

return jsonResult;

또한 Sarath의 요구처럼 복잡하지 않다면 다음과 같은 간단한 작업을 수행할 수 있습니다.

[MyError]
public JsonResult Error(string objectToUpdate)
{
   throw new Exception("ERROR!");
}

public class MyErrorAttribute : FilterAttribute, IExceptionFilter
{
   public virtual void OnException(ExceptionContext filterContext)
   {
      if (filterContext == null)
      {
         throw new ArgumentNullException("filterContext");
      }
      if (filterContext.Exception != null)
      {
         filterContext.ExceptionHandled = true;
         filterContext.HttpContext.Response.Clear();
         filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
         filterContext.HttpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError;
         filterContext.Result = new JsonResult() { Data = filterContext.Exception.Message };
      }
   }
}

MVC만 사용하는 경우 가장 간단한 방법은 HttpStatusCodeResult를 사용하는 것입니다.

public ActionResult MyAjaxRequest(string args)
    {
        string error_message = string.Empty;
        try
        {
            // successful
            return Json(args);
        }
        catch (Exception e)
        {
            error_message = e.Message;
        }

        return new HttpStatusCodeResult(500, error_message);
    }

에러가 클라이언트에 반환되면, 에러를 표시하거나, 필요에 따라서 조작할 수 있습니다.

request.fail(function (jqXHR) {
        if (jqXHR.status == 500) {
            alert(jqXHR.statusText);
        }
    })

난 ASP를 실행하고 있었어Net Web API 5.2.7. JsonResult 클래스가 제네릭스와 비동기 실행 메서드를 사용하도록 변경된 것 같습니다.결국 리차드 가사이드의 해결책을 바꾸게 되었습니다.

public class JsonHttpStatusResult<T> : JsonResult<T>
{
    private readonly HttpStatusCode _httpStatus;

    public JsonHttpStatusResult(T content, JsonSerializerSettings serializer, Encoding encoding, ApiController controller, HttpStatusCode httpStatus) 
    : base(content, serializer, encoding, controller)
    {
        _httpStatus = httpStatus;
    }

    public override Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
    {
        var returnTask = base.ExecuteAsync(cancellationToken);
        returnTask.Result.StatusCode = HttpStatusCode.BadRequest;
        return returnTask;
    }
}

Richard의 예를 따라 이 클래스를 다음과 같이 사용할 수 있습니다.

if(thereWereErrors)
{
    var errorModel = new CustomErrorModel("There was an error");
    return new JsonHttpStatusResult<CustomErrorModel>(errorModel, new JsonSerializerSettings(), new UTF8Encoding(), this, HttpStatusCode.InternalServerError);
}

(예: 익명의 형식수 .CustomErrorType에 접속합니다.JsonHttpStatusResult, , 솔루션으로 한 서브클래스를 작성할 수 .ApiController을 것HttpStatusCode합니다.Jsonmethods를 합니다.

public abstract class MyApiController : ApiController
{
    protected internal virtual JsonHttpStatusResult<T> Json<T>(T content, HttpStatusCode httpStatus, JsonSerializerSettings serializerSettings, Encoding encoding)
    {
        return new JsonHttpStatusResult<T>(content, httpStatus, serializerSettings, encoding, this);
    }

    protected internal JsonHttpStatusResult<T> Json<T>(T content, HttpStatusCode httpStatus, JsonSerializerSettings serializerSettings)
    {
        return Json(content, httpStatus, serializerSettings, new UTF8Encoding());
    }

    protected internal JsonHttpStatusResult<T> Json<T>(T content, HttpStatusCode httpStatus)
    {
        return Json(content, httpStatus, new JsonSerializerSettings());
    }
}

그런 다음 다음과 같은 익명 유형으로 사용할 수 있습니다.

if(thereWereErrors)
{
    var errorModel = new { error = "There was an error" };
    return Json(errorModel, HttpStatusCode.InternalServerError);
}

ASP의 JsonResult 덮어쓰기 응답을 다음에 나타냅니다.NET v5+.테스트를 해봤는데 이전 버전과 동일하게 동작합니다.

public class JsonHttpStatusResult : JsonResult
{
    private readonly HttpStatusCode _httpStatus;

    public JsonHttpStatusResult(object data, HttpStatusCode httpStatus) : base(data)
    {
        _httpStatus = httpStatus;
    }

    public override Task ExecuteResultAsync(ActionContext context)
    {
        context.HttpContext.Response.StatusCode = (int)_httpStatus;
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        var services = context.HttpContext.RequestServices;
        var executor = services.GetRequiredService<IActionResultExecutor<JsonResult>>();
        return executor.ExecuteAsync(context, this);
    }
}

언급URL : https://stackoverflow.com/questions/11370251/return-json-with-error-status-code-mvc

반응형