【问题标题】:Handling erros in ASP.NET webapi services处理 ASP.NET Web api 服务中的错误
【发布时间】:2021-04-16 11:55:03
【问题描述】:

我正在尝试处理 webapi 中服务部分的异常。 我试图实现的目标是:

当服务出现错误时,例如when didn't find the todo with certain id 控制器应该返回带有自定义状态码的自定义 json 响应

因为在我的项目中控制器和服务是分开的,不知道如何实现它。

提前致谢。

在我的代码下面:

Services/TodoRepository.cs

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using test.Data;
using test.DTO;
using test.Interfaces;
using test.Models;

namespace test.Services
{
    public class TodoRepository : ITodoRepository
    {
        private readonly DataContext _context;

        public TodoRepository(DataContext context)
        {
            _context = context;
        }
        public async Task<IEnumerable<Todo>> GetTodosAsync()
        {
            return await _context.Todos.ToListAsync();
        }

        public async Task<Todo> GetTodoAsync(int id)
        {
            return await _context.Todos.FindAsync(id);
        }

        public async Task<Todo> AddTodoAsync(TodoInput input)
        {
            var todo = new Todo 
            {
                Title = input.Title,
                Description = input.Description,
                IsDone = input.IsDone
            };
            await _context.Todos.AddAsync(todo);
            return todo;
        }

        public async Task<Todo> SetTodoAsync(int id, TodoInput input)
        {
            var todo = await _context.Todos.FindAsync(id);
            if (todo == null)
                //
                // CONTROLLER SHOULD RETURN CUSTOM 
                // JSON RESPONSE WITH CUSTOM STATUS CODE
                // INSTEAD OF EXCEPTION WITH INTERNAL SERVER ERROR
                //
                throw new Exception("Could not find an item with this id");
            todo.Title = input.Title;
            todo.Description = input.Description;
            todo.IsDone = input.IsDone;
            todo.Updated = DateTime.Now;
            _context.Todos.Update(todo);
            return todo;
        }

        public async Task<Todo> DeleteTodoAsync(int id)
        {
            var todo = await _context.Todos.FindAsync(id);
            if (todo == null)
                //
                // CONTROLLER SHOULD RETURN CUSTOM 
                // JSON RESPONSE WITH CUSTOM STATUS CODE
                // INSTEAD OF EXCEPTION WITH INTERNAL SERVER ERROR
                //
                throw new Exception("Could not find an item with this id");
            _context.Todos.Remove(todo);
            return todo;
        }

        public async Task<bool> SaveAllAsync()
        {
            return await _context.SaveChangesAsync() > 0;
        }
    }
}

Controllers/TodosController.cs

using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using test.Data;
using test.DTO;
using test.Interfaces;
using test.Models;

namespace test.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class TodosController : ControllerBase
    {
        private readonly DataContext _context;
        private ITodoRepository _repository;

        public TodosController(DataContext context, ITodoRepository repository)
        {
            _context = context;
            _repository = repository;
        }

        [HttpGet]
        public async Task<ActionResult<IEnumerable<Todo>>> GetTodos()
        {
            return Ok(await _repository.GetTodosAsync());
        }

        [HttpGet("{id}")]
        public async Task<ActionResult<Todo>> GetTodo([FromRoute] int id)
        {
            return Ok(await _repository.GetTodoAsync(id));
        }

        [HttpPost]
        public async Task<ActionResult<Todo>> AddTodo([FromBody] TodoInput input)
        {
            var todo = await _repository.AddTodoAsync(input);
            if (!await _repository.SaveAllAsync())
                return BadRequest("something gone wrong");
            return Ok(todo);
        }

        [HttpPut("{id}")]
        public async Task<ActionResult<Todo>> SetTodo([FromRoute] int id, [FromBody] TodoInput input)
        {
            var todo = await _repository.SetTodoAsync(id, input);
            if (!await _repository.SaveAllAsync())
                return BadRequest("something gone wrong");
            return Ok(todo);
        }
        [HttpDelete("{id}")]
        public async Task<ActionResult<Todo>> DeleteTodo([FromRoute] int id)
        {
            var todo = await _repository.DeleteTodoAsync(id);
            if (!await _repository.SaveAllAsync())
                return BadRequest("something gone wrong");
            return Ok(todo);
        }
    }
}

【问题讨论】:

    标签: c# asp.net-core asp.net-web-api


    【解决方案1】:

    假设您将在某些方法中抛出一些特定的异常,而您只想将其捕获为 System.Exception,也许您可​​以使用这种方法:

    您可以创建异常处理扩展方法(如果您也想使用 Logger,只需在方法中添加 ILogger 参数并从 Startup.Configure 传递它):

    public static class ExceptionHandler
        {
            /// <summary>
            /// 
            /// </summary>
            /// <param name="app"></param>
            public static void UseCustomExceptionHandler(this IApplicationBuilder app)
            {
                app.UseExceptionHandler(eApp =>
                {
                    eApp.Run(async context =>
                    {
                        context.Response.StatusCode = 500;
                        context.Response.ContentType = "application/json";
    
                        var errorCtx = context.Features.Get<IExceptionHandlerFeature>();
                        if (errorCtx != null)
                        {
                            var ex = errorCtx.Error;
                            var message = "Unspecified error ocurred.";
    
                            if (ex is ValidationException)
                            {
                                var validationException = ex as ValidationException;
                                context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
                                message = string.Join(" | ", validationException.Errors.Select(v => string.Join(",", v.Value)));
                            }
                            else if (ex is SomeCustomException)
                            {
                                var someCustomException = ex as SomeCustomException;
                                ...
                            }
    
                            var jsonResponse = JsonConvert.SerializeObject(new ErrorResponse
                            {
                                TraceId = traceId,
                                Message = message
                            });
                            await context.Response.WriteAsync(jsonResponse, Encoding.UTF8);
                        }
                    });
                });
            }
        }
    

    然后你只需在 Startup Configure 中注册它:

    public void Configure(IApplicationBuilder app)
            {
                ...
    
                app.UseCustomExceptionHandler();
    
                ...
            }
    

    对于不同的异常可以设置不同的状态码:context.Response.StatusCode = 404;

    【讨论】:

      【解决方案2】:

      因此,对于您的服务类,您将需要一个返回 BadResult 对象的 try/catch:

              try
              {
                 //TODO Test Case check
                 if(x != 1)
                 {
                    throw new Exception();
      
              }
              catch (Exception e)
              {
                  return BadRequest("Could not find an item with this id");
              }
      

      现在对于您的 ASP .Net 客户端上的控制器,当您检查您启动的任务的结果以调用您的 API 时:

                     var acctResult = responseAcctTask.Result;
      
                      if (acctResult.StatusCode == HttpStatusCode.BadRequest)
                      {   
                          //log response status here..
                          ModelState.AddModelError(string.Empty, "Server error.");
                      }
      

      此时您可以以任何方式处理错误,我更喜欢将其添加到 modelState 以便我可以访问我的视图中的错误信息。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-10-23
        • 1970-01-01
        • 2011-05-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-04-10
        相关资源
        最近更新 更多