近期工作比较忙,确实没太多精力与时间写博文,博文写得少,但并不代表没有研究与总结,也不会停止我继续分享的节奏,最多有可能发博文间隔时间稍长一点。废话不多说,直接上干货,虽不是主流的ASP.NET CORE但ASP.NET WEB API仍然有很多地方在用【而且核心思路与.NET CORE其实都是一样的】,而且如下也适用于OWIN WEBAPI方式。

  1. 全局异常捕获处理几种方式

具体可参见: ASP.NET Web API 中的错误处理

  1. 自定义异常过滤器(ExceptionFilterAttribute) :当控制器方法引发带有 httpresponseexception异常的任何未经处理的异常时,将执行异常筛选器,仅处理与特定操作或控制器相关的子集未处理异常的最简单解决方案

        /// <summary>
    /// 统一全局异常过滤处理
    /// author:zuowenjun
    /// </summary>
    public class GlobalExceptionFilterAttribute : ExceptionFilterAttribute
    {
    private static readonly ILogger logger = LogManager.GetCurrentClassLogger(); public override void OnException(HttpActionExecutedContext actionExecutedContext)
    {
    var actionContext = actionExecutedContext.ActionContext;
    string actionInfo = $"{actionContext.ActionDescriptor.ControllerDescriptor.ControllerName}.{actionContext.ActionDescriptor.ActionName}";
    logger.Error(context.Exception, $"url:{actionContext.Request.RequestUri}({actionInfo}),request body:{actionContext.Request.Content.ReadAsStringAsync().Result},error:{context.Exception.Message}."); //为便于服务之间追踪分析,尝试把入参的TraceId继续传递给响应出参
    string traceId = null;
    if (actionContext.ActionArguments?.Count > 0)
    {
    var traceObj = actionContext.ActionArguments?.Values.Where(a => a is ITraceable).FirstOrDefault() as ITraceable;
    traceId = traceObj?.TraceId;
    } var apiResponse = new ApiResponse() { Code = 400, Msg = $"{actionInfo} Error:{context.Exception.Message}", TraceId = traceId }; actionExecutedContext.Response = actionExecutedContext.ActionContext.Request.CreateResponse(HttpStatusCode.OK, apiResponse, "application/json"); }
    }
  2. 自定义异常处理器(ExceptionHandler):自定义 Web API 捕获的未处理异常的所有可能响应的解决方案

        /// <summary>
    /// 全局异常处理器
    /// author:zuowenjun
    /// </summary>
    public class GlobalExceptionHandler : ExceptionHandler
    {
    private static readonly ILogger logger = LogManager.GetCurrentClassLogger(); public override void Handle(ExceptionHandlerContext context)
    {
    var actionContext = context.ExceptionContext.ActionContext;
    string actionInfo = $"{actionContext.ActionDescriptor.ControllerDescriptor.ControllerName}.{actionContext.ActionDescriptor.ActionName}";
    logger.Error(context.Exception, $"url:{actionContext.Request.RequestUri}({actionInfo}),request body:{actionContext.Request.Content.ReadAsStringAsync().Result},error:{context.Exception.Message}."); //为便于服务之间追踪分析,尝试把入参的TraceId继续传递给响应出参
    string traceId = null;
    if (actionContext.ActionArguments?.Count > 0)
    {
    var traceObj = actionContext.ActionArguments?.Values.Where(a => a is ITraceable).FirstOrDefault() as ITraceable;
    traceId = traceObj?.TraceId;
    }
    var apiResponse = new ApiResponse() { Code = 400, Msg = $"{actionInfo} Error:{context.Exception.Message}", TraceId = traceId };
    context.Result = new ResponseMessageResult(context.ExceptionContext.Request.CreateResponse(HttpStatusCode.OK, apiResponse));
    }
    }
  3. 自定义委托处理程序中间件(DelegatingHandler):在请求响应管道中进行拦截判断,用于捕获并处理HttpError类异常。【如果想记录完整的异常信息,可以自定义多个继承自ExceptionLogger】

    /// <summary>
    /// 全局异常处理管道中间件(将除GlobalExceptionHandler未能捕获的HttpError异常进行额外封装统一返回)
    /// author:zuowenjun
    /// </summary>
    public class GlobalExceptionDelegatingHandler : DelegatingHandler
    {
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
    return base.SendAsync(request, cancellationToken).ContinueWith<HttpResponseMessage>(respTask =>
    {
    HttpResponseMessage response = respTask.Result;
    HttpError httpError;
    if (response.TryGetContentValue(out httpError) && httpError != null)
    {
    var apiResponse = new ApiResponse<HttpError>()
    {
    Code = (int)response.StatusCode,
    Msg = $"{response.RequestMessage.RequestUri} Error:{httpError.Message}",
    Data = httpError,
    TraceId = GetTraceId(response.RequestMessage.Content)
    };
    response.StatusCode = HttpStatusCode.OK;
    response.Content = response.RequestMessage.CreateResponse(HttpStatusCode.OK, apiResponse).Content;
    } return response; });
    } private string GetTraceId(HttpContent httpContent)
    {
    string traceId = null;
    if (httpContent is ObjectContent)
    {
    var contentValue = (httpContent as ObjectContent).Value;
    if (contentValue != null && contentValue is ITraceable)
    {
    traceId = (contentValue as ITraceable).TraceId;
    }
    }
    else
    {
    string contentStr = httpContent.ReadAsStringAsync().Result;
    if (contentStr.IndexOf("traceId", StringComparison.OrdinalIgnoreCase) >= 0)
    {
    var traceObj = httpContent.ReadAsAsync<ITraceable>().Result;
    traceId = traceObj.TraceId;
    }
    } return traceId;
    }
    }

    Web Api Config入口方法(OWIN 是startup Configuration方法)注册上述相关的自定义的各类中间件:

    【注:建议一般只需要同时注册GlobalExceptionDelegatingHandler、GlobalExceptionHandler即可】

                config.MessageHandlers.Insert(0, new GlobalExceptionDelegatingHandler());
    config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler());
    config.Filters.Add(new GlobalExceptionFilterAttribute());

如下是自定义的一个可追踪接口,用于需要API入参透传TraceId(Model类实现该接口即代表可传TraceId),我这里是为了便于外部接口传TraceId方便,才放到报文中,其实也可以放在请求头中

    public interface ITraceable
{
string TraceId { get; set; }
}
  1. ACTION方法入参验证的几种方式

    1. 在Model类对应的属性上增加添加相关验证特性

      一般是指System.ComponentModel.DataAnnotations命名空间下的相关验证特性,如: PhoneAttribute、EmailAddressAttribute 、EnumDataTypeAttribute 、FileExtensionsAttribute 、MaxLengthAttribute 、MinLengthAttribute 、RangeAttribute 、RegularExpressionAttribute 、RequiredAttribute 、StringLengthAttribute 、UrlAttribute 、CompareAttribute 、CustomValidationAttribute【这个支持指定自定义类名及验证方法,采取约定验证方法签名,第一个参数为具体验证的对象类型(不限定,依实际情况指定),第二个则为ValidationContext类型】

    2. 让Model类实现IValidatableObject接口并实现Validate方法,同时需注册自定义的ValidatableObjectAdapter对象,以便实现对IValidatableObject接口的支持,这种方式的好处是可以实现集中验证(当然也可以使用上述方法1中的CustomValidationAttribute,并标注到要验证的Model类上,且指定当前Model类的某个符合验证方法即可

      public class CustomModelValidator : ModelValidator
      {
      public CustomModelValidator(IEnumerable<ModelValidatorProvider> modelValidatorProviders) : base(modelValidatorProviders)
      { } public override IEnumerable<ModelValidationResult> Validate(ModelMetadata metadata, object container)
      {
      if (metadata.IsComplexType && metadata.Model == null)
      {
      return new List<ModelValidationResult> { new ModelValidationResult { MemberName = metadata.GetDisplayName(), Message = "请求参数对象不能为空。" } };
      } if (typeof(IValidatableObject).IsAssignableFrom(metadata.ModelType))
      {
      var validationResult = (metadata.Model as IValidatableObject).Validate(new ValidationContext(metadata.Model));
      if (validationResult != null)
      {
      var modelValidationResults = new List<ModelValidationResult>();
      foreach (var result in validationResult)
      {
      modelValidationResults.Add(new ModelValidationResult
      {
      MemberName = string.Join(",", result.MemberNames),
      Message = result.ErrorMessage
      });
      } return modelValidationResults;
      }
      return null;
      } return GetModelValidator(ValidatorProviders).Validate(metadata, container);
      }
      }

      除了定义上述的CustomModelValidator以便支持对IValidatableObject的验证外,还必需注册加入到验证上下文集合中才能生效,仍然是在WEB API的config入口方法(OWIN 是startup Configuration方法),类似如下:

      RegisterDefaultValidatableObjectAdapter(config);
      
       private void RegisterDefaultValidatableObjectAdapter(HttpConfiguration config)
      {
      IEnumerable<ModelValidatorProvider> modelValidatorProviders = config.Services.GetModelValidatorProviders(); DataAnnotationsModelValidatorProvider provider = (DataAnnotationsModelValidatorProvider)
      modelValidatorProviders.Single(x => x is DataAnnotationsModelValidatorProvider); provider.RegisterDefaultValidatableObjectAdapter(typeof(CustomModelValidator));
      }
  2. 出入参JSON格式及日期处理

    1. 大小写及强制仅支持JSON交互方式(以便符合目前通行的JSON规范,若不加则是大写)

      config.Formatters.XmlFormatter.SupportedMediaTypes.Clear();
      
      config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
    2. 日期格式化(如果不设置,则默认序列化后为全球通用时,不便于阅读)

      config.Formatters.JsonFormatter.SerializerSettings.DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Local;
      config.Formatters.JsonFormatter.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
    3. 自定义类型转换(通过自定义实现JsonConverter抽象类,可灵活定义序列化与反序化的行为),例如:

      //在入口方法加入自定义的转换器类
      config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new BoolenJsonConverter()); public class BoolenJsonConverter : JsonConverter
      {
      public override bool CanConvert(Type objectType)
      {
      return typeof(bool).IsAssignableFrom(objectType);
      } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
      {
      if (reader.TokenType == JsonToken.Boolean)
      {
      return Convert.ToBoolean(reader.Value);
      } throw new JsonSerializationException($"{reader.Path} value: {reader.Value} can not convert to Boolean");
      } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
      {
      if (value == null)
      {
      writer.WriteNull();
      return;
      }
      if (value is bool)
      {
      writer.WriteValue(value.ToString().ToLower());
      return;
      } throw new JsonSerializationException($"Expected Boolean object value: {value}");
      }
      }

后面计划将分享关于OAuth2.0原生实现及借助相关框架实现(JAVA与C#语言),敬请期待!

温馨提示:我的分享不局限于某一种编程语言,更多的是分享经验、技巧及原理,让更多的人受益,少走弯路!

关于ASP.NET WEB API(OWIN WEBAPI)的几个编码最佳实践总结的更多相关文章

  1. 使用 OWIN Self-Host ASP.NET Web API 2

    Open Web Interface for .NET (OWIN)在Web服务器和Web应用程序之间建立一个抽象层.OWIN将网页应用程序从网页服务器分离出来,然后将应用程序托管于OWIN的程序而离 ...

  2. 使用 OWIN 作为 ASP.NET Web API 的宿主

    使用 OWIN 作为 ASP.NET Web API 的宿主 ASP.NET Web API 是一种框架,用于轻松构建可以访问多种客户端(包括浏览器和移动 设备)的 HTTP 服务. ASP.NET ...

  3. Use OWIN to Self-Host ASP.NET Web API 2

      Open Web Interface for .NET (OWIN)在Web服务器和Web应用程序之间建立一个抽象层.OWIN将网页应用程序从网页服务器分离出来,然后将应用程序托管于OWIN的程序 ...

  4. [转] JSON Web Token in ASP.NET Web API 2 using Owin

    本文转自:http://bitoftech.net/2014/10/27/json-web-token-asp-net-web-api-2-jwt-owin-authorization-server/ ...

  5. JSON Web Token in ASP.NET Web API 2 using Owin

    In the previous post Decouple OWIN Authorization Server from Resource Server we saw how we can separ ...

  6. 在ASP.NET Web API 2中使用Owin OAuth 刷新令牌(示例代码)

    在上篇文章介绍了Web Api中使用令牌进行授权的后端实现方法,基于WebApi2和OWIN OAuth实现了获取access token,使用token访问需授权的资源信息.本文将介绍在Web Ap ...

  7. 在ASP.NET Web API 2中使用Owin基于Token令牌的身份验证

    基于令牌的身份验证 基于令牌的身份验证主要区别于以前常用的常用的基于cookie的身份验证,基于cookie的身份验证在B/S架构中使用比较多,但是在Web Api中因其特殊性,基于cookie的身份 ...

  8. ASP.NET Web API与Owin OAuth:使用Access Toke调用受保护的API

    在前一篇博文中,我们使用OAuth的Client Credential Grant授权方式,在服务端通过CNBlogsAuthorizationServerProvider(Authorization ...

  9. Exception Handling in ASP.NET Web API webapi异常处理

    原文:http://www.asp.net/web-api/overview/error-handling/exception-handling This article describes erro ...

  10. (转)【ASP.NET Web API】Authentication with OWIN

    概述 本文说明了如何使用 OWIN 来实现 ASP.NET Web API 的验证功能,以及在客户端与服务器的交互过程中,避免重复提交用户名和密码的机制. 客户端可以分为两类: JavaScript: ...

随机推荐

  1. vue2+高德地图web端开发

    https://blog.csdn.net/qq_51553982/article/details/123014412

  2. python global函数的使用

    1.在全局变量与局部变量均存在时自定义的函数优先使用局部变量,自定义函数并不能改变全局变量的值. 查看运行结果:  2.在没有局部变量时,使用全局变量,且函数内部不能改变全局变量的值  查看运行结果: ...

  3. java进阶(15)--DecimalFormat、BigDecimal

    一.DecimalFormat(数字格式化) 1.数字格式化的表示: (#) 代表任意数字 (,)代表千分位 (.)代表小数 (0)代表不够时补0 2.示例

  4. Error adding module to project: null I(IDEA创建maven项目时遇到此提示)

    1.问题 在已创建一个springboot项目的时候,想要再增添一个Maven项目,发生报错 2.解决 根本原因是创建maven模块时选择的sdk版本过高(选用了openjdk19).选用jdk1.8 ...

  5. Spring Boot+Vue实现汽车租赁系统(毕设)

    一.前言 汽车租赁系统,很常见的一个系统,但是网上还是以前的老框架实现的,于是我打算从设计到开发都用现在比较流行的新框架.想学习或者做毕设的可以私信联系哦!! 二.技术栈 - 后端技术 Spring ...

  6. Java进程内线程数量限制的相关学习

    Java进程内线程数量限制的相关学习 背景 还是之前出现 cannot create native thread 的问题的后续 周末在家学习了下如何在容器外抓取dump. 也验证了下能否开启超过宿主机 ...

  7. Oracle process/session/cursor/tx/tm的简单学习

    Oracle process/session/cursor/tx/tm的简单学习 Oracle的部署模式 Oracle安装时有专用模式和共享模式的区别 共享模式(Shared mode): 在共享模式 ...

  8. [转帖]s3fs把 s3-like 对象存储挂载到本地

    s3fs把 s3-like 对象存储挂载到本地 s3fs把 s3-like 对象存储挂载到本地 s3fs-fuse 是一个采用 c++ 开发的开源应用,它的作用是可以将 AWS S3 以及兼容 S3 ...

  9. [转帖]Web性能优化工具WebPageTest(一)——总览与配置

    https://www.cnblogs.com/strick/p/6677836.html 网站性能优化工具大致分为两类:综合类和RUM类(实时监控用户类),WebPageTest属于综合类. Web ...

  10. [转帖]认识目标文件的格式——a.out COFF PE ELF

    https://cloud.tencent.com/developer/article/1446849   1.目标文件的常用格式 目标文件是源代码编译后未进行链接的中间文件(Windows的.obj ...