Tratamento de erros ASP.NET MVC Ajax

117

Como faço para lidar com exceções lançadas em um controlador quando jquery ajax chama uma ação?

Por exemplo, eu gostaria de um código javascript global que seja executado em qualquer tipo de exceção de servidor durante uma chamada ajax que exibe a mensagem de exceção se estiver no modo de depuração ou apenas uma mensagem de erro normal.

No lado do cliente, chamarei uma função no erro de ajax.

No lado do servidor, preciso escrever um filtro de ação personalizado?

Shawn Mclean
fonte
8
Veja o post de beckelmans para um bom exemplo. A resposta de Darins a esta postagem é boa, mas não defina o código de status correto para um erro.
Dan,
6
Infelizmente esse link foi quebrado
Chris Nevill,
1
Aqui está o link na máquina de retorno
BruceHill

Respostas:

161

Se o servidor enviar algum código de status diferente de 200, o retorno de chamada de erro será executado:

$.ajax({
    url: '/foo',
    success: function(result) {
        alert('yeap');
    },
    error: function(XMLHttpRequest, textStatus, errorThrown) {
        alert('oops, something bad happened');
    }
});

e para registrar um manipulador de erros global, você pode usar o $.ajaxSetup()método:

$.ajaxSetup({
    error: function(XMLHttpRequest, textStatus, errorThrown) {
        alert('oops, something bad happened');
    }
});

Outra maneira é usar JSON. Portanto, você pode escrever um filtro de ação personalizado no servidor que captura a exceção e a transforma em resposta JSON:

public class MyErrorHandlerAttribute : FilterAttribute, IExceptionFilter
{
    public void OnException(ExceptionContext filterContext)
    {
        filterContext.ExceptionHandled = true;
        filterContext.Result = new JsonResult
        {
            Data = new { success = false, error = filterContext.Exception.ToString() },
            JsonRequestBehavior = JsonRequestBehavior.AllowGet
        };
    }
}

e, em seguida, decore sua ação de controlador com este atributo:

[MyErrorHandler]
public ActionResult Foo(string id)
{
    if (string.IsNullOrEmpty(id))
    {
        throw new Exception("oh no");
    }
    return Json(new { success = true });
}

e finalmente invoque-o:

$.getJSON('/home/foo', { id: null }, function (result) {
    if (!result.success) {
        alert(result.error);
    } else {
        // handle the success
    }
});
Darin Dimitrov
fonte
1
Obrigado por isso, este último era o que eu estava procurando. Portanto, para a exceção do asp.net mvc, há uma maneira específica de lançá-la para que possa ser detectada pelo manipulador de erros jquery?
Shawn Mclean
1
@Lol coder, não importa como você lance uma exceção dentro da ação do controlador, o servidor retornará o código de status 500 e o errorretorno de chamada será executado.
Darin Dimitrov
Obrigado, perfeito, exatamente o que eu estava procurando.
Shawn Mclean
1
Um código de status 500 não estaria errado? Para citar este camarada broadcast.oreilly.com/2011/06/… : "Não perceber que um erro 4xx significa que errei e um 5xx significa que você erreou" - onde eu sou o cliente e você é o servidor.
Chris Nevill,
Esta resposta ainda é válida para as versões mais recentes do ASPNET?
gog
73

Depois de pesquisar no Google, escrevo um tratamento de exceção simples com base no filtro de ação MVC:

public class HandleExceptionAttribute : HandleErrorAttribute
{
    public override void OnException(ExceptionContext filterContext)
    {
        if (filterContext.HttpContext.Request.IsAjaxRequest() && filterContext.Exception != null)
        {
            filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
            filterContext.Result = new JsonResult
            {
                JsonRequestBehavior = JsonRequestBehavior.AllowGet,
                Data = new
                {
                    filterContext.Exception.Message,
                    filterContext.Exception.StackTrace
                }
            };
            filterContext.ExceptionHandled = true;
        }
        else
        {
            base.OnException(filterContext);
        }
    }
}

e escreva em global.ascx:

 public static void RegisterGlobalFilters(GlobalFilterCollection filters)
 {
      filters.Add(new HandleExceptionAttribute());
 }

e, em seguida, escreva este script no layout ou página mestra:

<script type="text/javascript">
      $(document).ajaxError(function (e, jqxhr, settings, exception) {
                       e.stopPropagation();
                       if (jqxhr != null)
                           alert(jqxhr.responseText);
                     });
</script>

Finalmente, você deve ativar o erro personalizado. e depois divirta-se :)

Arash
fonte
Posso ver o erro no Firebug, mas ele não está redirecionando para a página de erro.?
user2067567
1
Obrigado por isso! deve ser marcado como a resposta IMO como sua filtragem em solicitações ajax e herda a classe correta ao invés do que HandleErrorAttribute herda
mtbennett
2
Resposta maravilhosa! : D
Leniel Maccaferri
1
Acho que "Request.IsAjaxRequest ()" às vezes não é tão confiável.
Will Huang
Para a configuração de depuração, sempre funciona, mas nem sempre funciona na configuração de versão e retorna html em vez disso, alguém tem uma solução alternativa para esse caso?
Hitendra
9

Infelizmente, nenhuma das respostas é boa para mim. Surpreendentemente, a solução é muito mais simples. Retorno do controlador:

return new HttpStatusCodeResult(HttpStatusCode.BadRequest, e.Response.ReasonPhrase);

E trate-o como erro HTTP padrão no cliente, conforme desejar.

Alehro
fonte
@Will Huang: o nome da instância de exceção
schmendrick
Eu tenho que lançar o primeiro argumento para int. Além disso, quando faço isso, o resultado é passado para o ajax successmanipulador, não para o errormanipulador. Este é o comportamento esperado?
Jonathan Wood
4

Fiz uma solução rápida porque estava com pouco tempo e funcionou bem. Embora eu ache que a melhor opção é usar um Filtro de Exceção, talvez minha solução possa ajudar no caso de uma solução simples ser necessária.

Eu fiz o seguinte. No método do controlador, retornei um JsonResult com uma propriedade "Success" dentro dos Dados:

    [HttpPut]
    public JsonResult UpdateEmployeeConfig(EmployeConfig employeToSave) 
    {
        if (!ModelState.IsValid)
        {
            return new JsonResult
            {
                Data = new { ErrorMessage = "Model is not valid", Success = false },
                ContentEncoding = System.Text.Encoding.UTF8,
                JsonRequestBehavior = JsonRequestBehavior.DenyGet
            };
        }
        try
        {
            MyDbContext db = new MyDbContext();

            db.Entry(employeToSave).State = EntityState.Modified;
            db.SaveChanges();

            DTO.EmployeConfig user = (DTO.EmployeConfig)Session["EmployeLoggin"];

            if (employeToSave.Id == user.Id)
            {
                user.Company = employeToSave.Company;
                user.Language = employeToSave.Language;
                user.Money = employeToSave.Money;
                user.CostCenter = employeToSave.CostCenter;

                Session["EmployeLoggin"] = user;
            }
        }
        catch (Exception ex) 
        {
            return new JsonResult
            {
                Data = new { ErrorMessage = ex.Message, Success = false },
                ContentEncoding = System.Text.Encoding.UTF8,
                JsonRequestBehavior = JsonRequestBehavior.DenyGet
            };
        }

        return new JsonResult() { Data = new { Success = true }, };
    }

Mais tarde, na chamada de ajax, pedi apenas esta propriedade para saber se havia uma exceção:

$.ajax({
    url: 'UpdateEmployeeConfig',
    type: 'PUT',
    data: JSON.stringify(EmployeConfig),
    contentType: "application/json;charset=utf-8",
    success: function (data) {
        if (data.Success) {
            //This is for the example. Please do something prettier for the user, :)
            alert('All was really ok');                                           
        }
        else {
            alert('Oups.. we had errors: ' + data.ErrorMessage);
        }
    },
    error: function (request, status, error) {
       alert('oh, errors here. The call to the server is not working.')
    }
});

Espero que isto ajude. Código feliz! : P

Daniel Silva
fonte
4

De acordo com a resposta de aleho, aqui está um exemplo completo. Funciona muito bem e é super simples.

Código do controlador

[HttpGet]
public async Task<ActionResult> ChildItems()
{
    var client = TranslationDataHttpClient.GetClient();
    HttpResponseMessage response = await client.GetAsync("childItems);

    if (response.IsSuccessStatusCode)
        {
            string content = response.Content.ReadAsStringAsync().Result;
            List<WorkflowItem> parameters = JsonConvert.DeserializeObject<List<WorkflowItem>>(content);
            return Json(content, JsonRequestBehavior.AllowGet);
        }
        else
        {
            return new HttpStatusCodeResult(response.StatusCode, response.ReasonPhrase);
        }
    }
}

Código Javascript na visualização

var url = '@Html.Raw(@Url.Action("ChildItems", "WorkflowItemModal")';

$.ajax({
    type: "GET",
    dataType: "json",
    url: url,
    contentType: "application/json; charset=utf-8",
    success: function (data) {
        // Do something with the returned data
    },
    error: function (xhr, status, error) {
        // Handle the error.
    }
});

Espero que isso ajude mais alguém!

Rymnel
fonte
0

Para lidar com erros de chamadas ajax no lado do cliente, você atribui uma função à erroropção da chamada ajax.

Para definir um padrão globalmente, você pode usar a função descrita aqui: http://api.jquery.com/jQuery.ajaxSetup .

Brian Ball
fonte
Uma resposta que dei há mais de 4 anos atrás de repente recebe uma votação negativa? Alguém se preocupa em dar um motivo?
Brian Ball
1
Entre em contato com a SOF e peça ao DBA para consultar quem deu o voto negativo. Em seguida, envie uma mensagem a essa pessoa para que ela possa explicar. Nem qualquer pessoa pode explicar o motivo.
JoshYates1980