《Asp.Net Core3 + Vue3入坑教程》 - 6.異常處理與UserFriendlyException

簡介

《Asp.Net Core3 + Vue3入坑教程》 此教程適合新手入門或者先後端分離嘗試者。能夠根據圖文一步一步進操做編碼也能夠選擇直接查看源碼。每一篇文章都有對應的源碼html

目錄

《Asp.Net Core3 + Vue3入坑教程》系列教程目錄

Asp.Net Core後端項目前端

  1. 後端項目搭建與Swagger配置步驟
  2. 配置CROS策略解決跨域問題
  3. AutoMapper & Restful API & DI
  4. EF Core & Postgresql
  5. .Net Core 3升級成 .Net 5 & JWT
  6. (本文)異常處理與UserFriendlyException

Vue3 前端項目vue

  1. 使用vue-cli建立vue項目
  2. (暫未發表敬請期待...)使用Ant Design of Vue編寫頁面 & vue-router 初試
  3. (暫未發表敬請期待...)將Antd導航菜單與vue-router綁定
  4. (暫未發表敬請期待...) 保存用戶登入狀態vuex初試

本文簡介

本文爲《Asp.Net Core3 + Vue3入坑教程》系列教程的後端第六篇 - 異常處理與UserFriendlyException上文已經爲Simple項目升級了SDK而且應用了JWT,本文繼續爲Simple項目增長異常處理與使用友好異常(UserFriendlyException)。git

爲何須要使用友好異常的方式進行開發呢?github

在不少狀況下,咱們在一個方法中每每包含着校驗參數返回結果兩個動做,這時候咱們的返回結果就須要考慮用對象來包裹校驗結果返回結果。 若是咱們使用友好異常,默認方法能順利經過校驗並返回正確的結果,若是校驗出現失敗的狀況則將失敗緣由經過友好異常的方式返回給調用者,可讓方法的返回內容不須要考慮校驗的結果,代碼更簡潔明瞭!vue-router

用戶友好參照了開源項目ABP項目 https://docs.abp.io/zh-Hans/abp/latest/Exception-Handlingsql

異常處理與UserFriendlyException

第一步先增長測試代碼,修改SqlCommanderRepo.cs

代碼調整以下:vuex

using Simple_Asp.Net_Core.Models;
using Simple_Asp.Net_Core.ServiceProvider;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Simple_Asp.Net_Core.Data
{
    public class SqlCommanderRepo : ICommanderRepo
    {
        private readonly CommanderContext _context;

        public SqlCommanderRepo(CommanderContext context)
        {
            _context = context;
        }

        public void CreateCommand(Command cmd)
        {
            if (cmd == null)
            {
                throw new ArgumentNullException(nameof(cmd));
            }

            _context.Commands.Add(cmd);
        }

        public void DeleteCommand(Command cmd)
        {
            if (cmd == null)
            {
                throw new ArgumentNullException(nameof(cmd));
            }
            _context.Commands.Remove(cmd);
        }

        public IEnumerable<Command> GetAllCommands()
        {
            return _context.Commands.ToList();
        }

        public Command GetCommandById(int id)
        {
            if (id == 0)
                throw new Exception("id不能爲0!");

            return _context.Commands.First(p => p.Id == id);
        }

        public bool SaveChanges()
        {
            return (_context.SaveChanges() >= 0);
        }

        public void UpdateCommand(Command cmd)
        {
            //Nothing
        }
    }
}

運行項目,調用接口api/commands/{id}接口,當請求參數id設置爲0時,後端會拋出異常信息。

當前的異常信息將程序內部內容都暴露出來,而且返回信息也不清晰,調用者難以處理。vue-cli

接着在ServiceProvider文件夾下增長自定義異常類UserFriendlyException.cs

代碼以下:json

using System;

namespace Simple_Asp.Net_Core.ServiceProvider
{
    public class UserFriendlyException : Exception
    {
        public UserFriendlyException(string message) : base(message)
        {
        }

        public UserFriendlyException(string message, Exception inner) : base(message, inner)
        {
        }
    }
}

在ServiceProvider文件夾下增長類ExceptionHandler.cs,用來處理異常

在捕捉到程序異常的時候須要寫入日誌方便問題追蹤

代碼以下:

using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
using System.Text;
using System.Threading.Tasks;

namespace Simple_Asp.Net_Core.ServiceProvider
{
    public class ExceptionHandler
    {
        public static Task ErrorEvent(HttpContext context)
        {
            var feature = context.Features.Get<IExceptionHandlerFeature>();
            var error = feature?.Error;

            if (error.GetType() == typeof(UserFriendlyException))
            {
                SetResponse(context);
                var content = GetApiResponse(error.Message);

                return context.Response.WriteAsync(JsonConvert.SerializeObject(content), Encoding.UTF8);
            }
            else
            {
                // 寫入日誌
                // error.Message
                // error.StackTrace

                SetResponse(context);
                var content = GetApiResponse("程序發生錯誤,請聯繫客服!");
                return context.Response.WriteAsync(JsonConvert.SerializeObject(content), Encoding.UTF8);
            }
        }

        /// <summary>
        /// 解決異常消息返回跨域問題
        /// </summary>
        private static void SetResponse(HttpContext context)
        {
            context.Response.Clear();
            context.Response.Headers.Add("Access-Control-Allow-Origin", "*");
            context.Response.Headers.Add("Access-Control-Allow-Methods", "POST,GET");
            context.Response.ContentType = "application/json";
        }

        /// <summary>
        /// 響應Response
        /// </summary>
        private static ErrorResponse GetApiResponse(string message)
        {
            return new ErrorResponse() { success = false, message = message };
        }

        private class ErrorResponse
        {
            public bool success { get; set; }
            public bool Success { get { return success; } }
            public string message { get; set; }
            public string Message { get { return message; } }
        }
    }
}

調整Startup.cs,增長異常捕捉

代碼以下:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Newtonsoft.Json.Serialization;
using Simple_Asp.Net_Core.Data;
using Simple_Asp.Net_Core.ServiceProvider;
using System;

namespace Simple_Asp.Net_Core
{
    public class Startup
    {
        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddJWT();

            services.AddDbContext<CommanderContext>(options =>
                options.UseNpgsql("Host=localhost;Database=postgres;Username=postgres;Password=123456"));

            services.AddCORS();
            services.AddMvc();
            services.AddSwagger();

            services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());

            services.AddScoped<ICommanderRepo, SqlCommanderRepo>();

            services.AddControllers().AddNewtonsoftJson(s =>
            {
                s.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
            });
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseSwagger();
                app.UseSwaggerUI(c =>
                {
                    c.SwaggerEndpoint("/swagger/v1/swagger.json", "ApiHelp V1");
                });
            }
            app.UseExceptionHandler(builder => builder.Run(async context => await ExceptionHandler.ErrorEvent(context)));
            app.UseCors("CorsTest");
            app.UseAuthentication();
            app.UseRouting();
            app.UseAuthorization();
            app.UseEndpoints(endpoints => endpoints.MapDefaultControllerRoute());
        }
    }
}

啓動項目,調用api/commands/{id}接口,能夠看出後端的接口發生了異常,此時的異常比較清晰。

最後咱們將SqlCommanderRepo.cs裏的異常改成友好異常

再次修改SqlCommanderRepo.cs

代碼調整以下:

using Simple_Asp.Net_Core.Models;
using Simple_Asp.Net_Core.ServiceProvider;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Simple_Asp.Net_Core.Data
{
    public class SqlCommanderRepo : ICommanderRepo
    {
        private readonly CommanderContext _context;

        public SqlCommanderRepo(CommanderContext context)
        {
            _context = context;
        }

        public void CreateCommand(Command cmd)
        {
            if (cmd == null)
            {
                throw new ArgumentNullException(nameof(cmd));
            }

            _context.Commands.Add(cmd);
        }

        public void DeleteCommand(Command cmd)
        {
            if (cmd == null)
            {
                throw new ArgumentNullException(nameof(cmd));
            }
            _context.Commands.Remove(cmd);
        }

        public IEnumerable<Command> GetAllCommands()
        {
            return _context.Commands.ToList();
        }

        public Command GetCommandById(int id)
        {
            if (id == 0)
                throw new Exception("id不能爲0!");

            return _context.Commands.First(p => p.Id == id);
        }

        public bool SaveChanges()
        {
            return (_context.SaveChanges() >= 0);
        }

        public void UpdateCommand(Command cmd)
        {
            //Nothing
        }
    }
}

最後啓動項目,調用api/commands/{id}接口,這時候咱們能夠獲得友好的提示!

總結

本文爲Simple項目增長異常處理與使用友好異常(UserFriendlyException),在捕捉到程序異常的時候須要寫入日誌方便問題追蹤!

目前Simple項目還未使用日誌組件,後續會補上

異常捕捉爲了可以將異常內容進行收集,而且能以統一的方式返回給客戶端,保證服務器的安全、幫助咱們追蹤問題而且客戶端的體驗也能有所保證。

異常捕捉結合友好異常的方式可以爲咱們減小代碼量,而且讓代碼更直觀明瞭,推薦你們一試

GitHub源碼

注意:源碼調試過程當中若是出現xml文件路徑錯誤,須要參照第一章(後端項目搭建與Swagger配置步驟)Swagger配置「配置XML 文檔文件」步驟,取消勾選而後再選中 ,將XML路徑設置成與你的電腦路徑匹配!

https://github.com/Impartsoft/Simple_Asp.Net_Core/tree/master/Simple_Asp.Net_Core 6.Exception Handling %26 UserFriendlyException

參考資料

ABP開源項目異常處理 https://docs.abp.io/zh-Hans/abp/latest/Exception-Handling

相關文章
相關標籤/搜索