在asp.net core 下定义统一的入参和出参格式

在使用.net core 开发Api的过程中,为了统一输入参数的格式,并增加一些全局必须含有的字段,比如:Code,Message,Lang等等,能采取的变通方式还是有几种的,然而都不够优雅,为了需求更优雅的解决方式,本文采用asp.net core支持的自定义模型绑定以及结果过滤方式解决,测试效果达到了预期目的。

MVC过滤器

对于熟悉asp.net 的朋友来说,使用过滤器还是比较容易的,过滤器本身有着严格的顺序,选用哪一种过滤器,必然是经过思考才能进行抉择的,对于输入参数来说,我们想修改参数值,可能比较方便的是选择在操作过滤器上实现。

  • 授权过滤器(Authorization)最先运行,用于确定是否已针对当前请求为当前用户授权。 如果请求未获授权,它们可以让管道短路。

  • 资源过滤器(Resource)是授权后最先处理请求的筛选器。 它们可以在筛选器管道的其余阶段运行之前以及管道的其余阶段完成之后运行代码。 出于性能方面的考虑,可以使用它们来实现缓存或以其他方式让筛选器管道短路。 它们在模型绑定之前运行,所以可以影响模型绑定。

  • 操作过滤器可(Action)以在调用单个操作方法之前和之后立即运行代码。 它们可用于处理传入某个操作的参数以及从该操作返回的结果。不可在 Razor Pages 中使用操作筛选器。

  • 异常过滤器(Exception)用于在向响应正文写入任何内容之前,对未经处理的异常应用全局策略。

  • 结果过滤器(Result)可以在执行单个操作结果之前和之后立即运行代码。 仅当操作方法成功执行时,它们才会运行。 对于必须围绕视图或格式化程序的执行的逻辑,它们很有用。

过滤器在 MVC 操作调用管道中执行,其大致位置如下:

 不同类型的过滤器操作顺序如下:

实现结果过滤器的目的

写过滤器的目的是把mvc控制器的Action返回结果加上固定返回格式,比如下图返回一个字符串数组,我们需要的格式是一个包含该内容的json格式。

 用户最终接收到的是下列内容。

{
    "Code":123,
    "Message":"这是一个异常信息",
    "Data": ["values1","values2"],
    "RequestId":"0001"
}

当然为了实现该目的,你可以定义一个模板类,每个返回值都增加上诸如:Result<IEnumerable<string>>,或者定义一个基类,大家都集成自这个基类,也都可以实现,就是感觉不够优雅。

结果过滤器

可以参考如下实现。

 难点:输入参数

输入参数的目的是从下列json中解析出Data结果数据绑定到mvc的action模型上。

{"Data":{
  "desc": "string",
  "parameters": "string", 
  "ttid": "string",
  "guid": "string"
},"Lang":"zh_cn","RequestId":"ee27c35e-f8f9-4619-aabf-3125e84bcf90"}

如上所示,你可以定义一个模板类或子类,然后同结果一样,但不够优雅优雅~~~~,啊哈哈,强迫症...

这里采用微软提供的模型绑定器解决该问题。ASP.NET Core MVC 中的模型绑定将 HTTP 请求中的数据映射到操作方法参数。 这些参数可能是简单类型的参数,如字符串、整数或浮点数,也可能是复杂类型的参数。 这是 MVC 的一项强大功能,因为不管数据的大小和复杂性,将传入数据映射到对应位置都是经常重复的方案。 MVC 通过将绑定抽象出来解决了这一问题,使开发者不必在每个应用中重写同一代码的稍微不同版本。 向类型转换器代码写入自己的文本不仅繁琐乏味,而且容易出错。

先实现一个继承自IModelBinder的类来实现绑定处理的核心工作。

public async Task BindModelAsync(ModelBindingContext bindingContext)
        {
            if (bindingContext == null)
            {
                throw new ArgumentNullException(nameof(bindingContext));
            }
            
            string modelBindingKey;
            if (bindingContext.IsTopLevelObject)
            {
                modelBindingKey = bindingContext.BinderModelName ?? string.Empty;
            }
            else
            {
                modelBindingKey = bindingContext.ModelName;
            }

            var httpContext = bindingContext.HttpContext;

            try
            {
                var args = await GetRequestValueAsync(httpContext.Request);
                httpContext.Items.Add("_Args", args);
                var type = bindingContext.ModelType;
                var argsType = typeof(Args<>).MakeGenericType(type);
                dynamic modelRtn = JsonConvert.DeserializeObject(args, argsType);
                if (modelRtn == null)
                {
                    var message = $"序列化输入参数为Args<{type.FullName}>时,结果为不期望的空值";
                    bindingContext.ModelState.AddModelError(modelBindingKey, message);
                    return;
                }
                
                bindingContext.Result = ModelBindingResult.Success(modelRtn.v);

                modelRtn.v = null;
                var model = modelRtn.ToNormalizing();
                if (string.IsNullOrEmpty(model.rid))
                {
                    model.rid = Guid.NewGuid().ToString("N");
                }
                
                httpContext.Items.Add("_ArgsObject", model);

            }
            catch (Exception exception)
            {
                bindingContext.ModelState.AddModelError(modelBindingKey, exception, bindingContext.ModelMetadata);
            }
        }

 是时候建立一个ModelBinderProvider来建立上述绑定了。只需要实现IModelBinderProvider接口即可。

public IModelBinder GetBinder(ModelBinderProviderContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (context.BindingInfo.BindingSource != null &&
                context.BindingInfo.BindingSource.CanAcceptDataFrom(BindingSource.Body))
            {

                return new QtBodyModelBinder(_options);
            }

            return null;
        }

增加过滤和绑定

services.AddMvc(options =>  {
    options.ModelBinderProviders.Insert(index, new QtBodyModelBinderProvider(options));
    options.Filters.Add(typeof(QtResultFilter));
}) ;

应用

控制器内的使用还是按照原有方式,简单明了,而传入的参数和输出的参数已经按照我们的json格式整理了,啊哈哈,终于不用纠结不优雅了~~~~

发布了112 篇原创文章 · 获赞 16 · 访问量 25万+

猜你喜欢

转载自blog.csdn.net/webmote/article/details/88963593