欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

ASP.NET Core 2.2 WebApi 系列【八】统一返回格式(返回值、模型验证、异常)

程序员文章站 2022-07-02 12:25:26
现阶段,基本上都是前后端分离项目,这样一来,就需要前后端配合,没有统一返回格式,那么对接起来会很麻烦,浪费时间。我们需要把所有接口及异常错误信息都返回一定的Json格式,有利于前端处理,从而提高了工作效率。 一、准备工作 定义响应实体类 /// /// 响应实体类 ///

现阶段,基本上都是前后端分离项目,这样一来,就需要前后端配合,没有统一返回格式,那么对接起来会很麻烦,浪费时间。我们需要把所有接口及异常错误信息都返回一定的json格式,有利于前端处理,从而提高了工作效率。

一、准备工作

定义响应实体类

    /// <summary>
    /// 响应实体类
    /// </summary>
    public class resultmodel
    {
        /// <summary>
        /// 状态码
        /// </summary>
        public int returncode { get; set; }

        /// <summary>
        /// 内容
        /// </summary>
        public object data { get; set; }

        /// <summary>
        /// 错误信息
        /// </summary>
        public string errormessage { get; set; }

        /// <summary>
        /// 是否成功
        /// </summary>
        public bool issuccess { get; set; }
    }

修改controller层

在controller层处理业务请求,new 一个resultmodel 对象,返回给前端。

        /// <summary>
        /// 查询用户
        /// </summary>
        /// <returns></returns>
        [route("getuser")]
        [httpget]
        public resultmodel getuser()
        {
            var data = _userrepository.getall().tolist();
            return new resultmodel() { data = data, errormessage = null, issuccess = true, returncode = 200 };
        }

这样需要每个方法都需要重新new一个resultmodel 对象,感觉有点代码冗余。

我们只需要加几个静态方法,每个方法返回都是resultmodel对象,成功的时候调用resultmodel.ok,失败的时候调用resultmodel.error即可。

优化

添加两个静态方法

        /// <summary>
        /// 成功
        /// </summary>
        /// <param name="data">返回数据</param>
        /// <returns></returns>
        public static resultmodel ok(object data)
        {
            return new resultmodel { data = data, errormessage = null, issuccess = true, returncode = 200 };
        }
        /// <summary>
        /// 失败
        /// </summary>
        /// <param name="str">错误信息</param>
        /// <param name="code">状态码</param>
        /// <returns></returns>
        public static resultmodel error(string str,int code)
        {
            return new resultmodel { data = null, errormessage = str, issuccess = false, returncode = code };
        }

修改控制器

        /// <summary>
        /// 查询用户
        /// </summary>
        /// <returns></returns>
        [route("getuser")]
        [httpget]
        public resultmodel getuser()
        {
            var data = _userrepository.getall().tolist();
            return resultmodel.ok(data);
        }

二、全局异常处理

通过全局异常处理,任何错误信息都会被拦截,返回统一格式。

定义全局异常处理中间件

using system;
using system.threading.tasks;
using microsoft.aspnetcore.http;
using netcorewebapi.util;
using newtonsoft.json;

namespace netcorewebapi.filter
{
    /// <summary>
    /// 处理全局信息中间件
    /// </summary>
    public class exceptionmiddleware
    {
        /// <summary>
        /// 处理http请求的函数。
        /// </summary>
        private readonly requestdelegate _next;
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="next"></param>
        public exceptionmiddleware(requestdelegate next)
        {
            _next = next;
        }

        public async task invoke(httpcontext context)
        {
            try
            {
                //抛给下一个中间件
                await _next(context);
            }
            catch (exception ex)
            {
                await writeexceptionasync(context, ex);
            }
            finally
            {
                await writeexceptionasync(context, null);
            }
        }

        private async task writeexceptionasync(httpcontext context, exception exception)
        {
            if (exception != null)
            {
                var response = context.response;
                var message = exception.innerexception == null ? exception.message : exception.innerexception.message;
                response.contenttype = "application/json";
                await response.writeasync(jsonconvert.serializeobject(resultmodel.error(message, 400))).configureawait(false);
            }
            else
            {
                var code = context.response.statuscode;
                switch (code)
                {
                    case 200:
                        return;
                    case 204:
                        return;
                    case 401:
                        context.response.contenttype = "application/json";
                        await context.response.writeasync(jsonconvert.serializeobject(resultmodel.error("token已过期,请重新登录.", code))).configureawait(false);
                        break;
                    default:
                        context.response.contenttype = "application/json";
                        await context.response.writeasync(jsonconvert.serializeobject(resultmodel.error("未知错误", code))).configureawait(false);
                        break;
                }
            }
        }
    }
}

注册中间件

在startup.cs启动类的configure方法中添加usemiddleware方法

            //异常处理中间件
            app.usemiddleware(typeof(exceptionmiddleware));

三、验证实体模型

有两种方式:

1.使用自定义过滤器

2.使用默认自带的400模型验证,需要在控制器上面加上【apicontroller】,这种方式优先级比较高,如果需要自定义模型验证,则需要先关闭默认的模型验证    

【apicontroller】

自动推断参数绑定:可以省略[frombody]等参数特性

自动模型验证:自动验证模型是否合法

参考:https://blog.csdn.net/sd7o95o/article/details/81844154

            services.addmvc(e =>
                {
                    e.filters.add<checkmodel>();
                })
                .setcompatibilityversion(compatibilityversion.version_2_2)
                .configureapibehavioroptions(e =>
                {
                    //关闭默认模型验证
                    e.suppressmodelstateinvalidfilter = true;
                });

参考:

自定义过滤器

创建checkmodel过滤器继承actionfilterattribute抽象类,重写其中需要的方法

using microsoft.aspnetcore.mvc;
using microsoft.aspnetcore.mvc.filters;
using netcorewebapi.util;

namespace netcorewebapi.filter
{
    /// <summary>
    /// 验证实体对象是否合法
    /// </summary>
    public class checkmodel : actionfilterattribute
    {
        /// <summary>
        /// action 调用前执行
        /// </summary>
        /// <param name="actioncontext"></param>
        public override void onactionexecuting(actionexecutingcontext actioncontext)
        {
            if (!actioncontext.modelstate.isvalid)
            {
                //初始化返回结果
                var result = new resultmodel { issuccess = false, returncode = 400 };
                foreach (var item in actioncontext.modelstate.values)
                {
                    foreach (var error in item.errors)
                    {
                        result.errormessage += error.errormessage + "|";
                    }
                }
                actioncontext.result = new badrequestobjectresult(result);
            }
        }
        /// <summary>
        /// action 方法调用后,result 方法调用前执行
        /// </summary>
        /// <param name="context"></param>
        public override void onactionexecuted(actionexecutedcontext context)
        {
} } }

将写的过滤器注册到全局

            services.addmvc(e =>
                {
                    //注册过滤器
                    e.filters.add<checkmodel>();
                })
                .setcompatibilityversion(compatibilityversion.version_2_2)
                .configureapibehavioroptions(e =>
                {
                    //关闭默认模型验证
                    e.suppressmodelstateinvalidfilter = true;
                });

创建userdto

using system.componentmodel.dataannotations;

namespace netcorewebapi.repository.dto
{
    /// <summary>
    /// 用户传输对象
    /// </summary>
    public class userdto
    {
        /// <summary>
        /// 用户id
        /// </summary>
        [stringlength(32, errormessage = "{0}最多{1}个字符"), display(name = "用户id")]
        public string userid { get; set; }
        /// <summary>
        /// 用户名
        /// </summary>
        [stringlength(20, errormessage = "{0}最多{1}个字符"), display(name = "用户名")]
        public string username { get; set; }
        /// <summary>
        /// 邮箱
        /// </summary>
        [stringlength(30, errormessage = "{0}最多{1}个字符"), display(name = "邮箱")]
        public string email { get; set; }
    }
}

测试

ASP.NET Core 2.2 WebApi 系列【八】统一返回格式(返回值、模型验证、异常)

默认模型验证

            services.addmvc(e =>
                {
                    //注册过滤器
                    //e.filters.add<checkmodel>();
                })
                .setcompatibilityversion(compatibilityversion.version_2_2)
                .configureapibehavioroptions(e =>
                {
                    ////关闭默认模型验证
                    //e.suppressmodelstateinvalidfilter = true;
                    e.invalidmodelstateresponsefactory = actioncontext =>
                    {
                        //获取验证失败的模型字段 
                        var errors = actioncontext.modelstate
                            .where(e1 => e1.value.errors.count > 0)
                            .select(e1 => e1.value.errors.first().errormessage)
                            .tolist();
                        var str = string.join("|", errors);
                        return new badrequestobjectresult(resultmodel.error(str, 400));
                    };
                });

两种验证方法效果是一致的