基于.Net Framework 4.0 Web API开发(3):ASP.NET Web APIs 异常的统一处理Attribute 和统一写Log 的Attribute的实现
概述:
ASP.NET Web API 的好用使用过的都知道,没有复杂的配置文件,一个简单的ApiController加上需要的Action就能工作。但是项目,总有异常发生,本节就来谈谈API的异常的统一处理和写统一写log逻辑的解决方案。
问题:
在ASP.NET Web API编写时,如果每个API都写异常处理逻辑,不但加大了开发工作量,且每个开发人员处理异常返回的数据结构也不尽相同,在异常发生情况下,客户端处理异常的逻辑就不再通用,也同时加大了对接接口人员的工作量,好的API错误码和错误信息都是固定格式,并后台应该有相应的异常记录。
异常的统一处理的实现:
1. 首先定义异常处理Attribute,继承System.Web.Http.Filters.ExceptionAttribute, 重写OnException, 代码如下
public class ErrorHandleAttribute : System.Web.Http.Filters.ExceptionFilterAttribute
{
private string _msg = string.Empty; public ErrorHandleAttribute() { } public ErrorHandleAttribute(string msg)
{
this._msg = msg;
}
public override void OnException(System.Web.Http.Filters.HttpActionExecutedContext actionExecutedContext)
{
base.OnException(actionExecutedContext);
// 取得发生异常时的错误讯息
//var errorMessage = actionExecutedContext.Exception.Message;
// 标记log
var logAction = actionExecutedContext.ActionContext.ActionDescriptor.GetCustomAttributes<NoErrorHandlerAttribute>();
if (logAction.Any())
{
actionExecutedContext.Response = actionExecutedContext.Request.CreateResponse(System.Net.HttpStatusCode.InternalServerError, new ResultData(ResultType.SystemException, actionExecutedContext.Exception.Message));
return;
} var request = HttpContext.Current.Request;
var logDetail = new LogDetail
{
//获取action名称
ActionName = actionExecutedContext.ActionContext.ActionDescriptor.ActionName,
//获取Controller 名称
ControllerName = actionExecutedContext.ActionContext.ActionDescriptor.ControllerDescriptor.ControllerName,
Navigator = request.UserAgent,
//获取访问的ip
IP = request.UserHostAddress,
UserHostName = request.UserHostName,
UrlReferrer = request.UrlReferrer != null ? request.UrlReferrer.AbsoluteUri : "",
Browser = request.Browser.Browser + " - " + request.Browser.Version + " - " + request.Browser.Type,
//获取request提交的参数
Paramaters = GetRequestValues(actionExecutedContext),
//获取response响应的结果
//ExecuteResult = GetResponseValues(actionExecutedContext), //这句会报错,异常没有处理结果
AttrTitle = this._msg,
ErrorMsg = string.Format("错误信息:{0}, 异常跟踪:{1}", actionExecutedContext.Exception.Message, actionExecutedContext.Exception.StackTrace),
RequestUri = request.Url.AbsoluteUri
}; // 写log
var logRep = ContainerManager.Resolve<ISysLogRepository>();
var log = new Log()
{
Action = actionExecutedContext.ActionContext.ActionDescriptor.ControllerDescriptor.ControllerName + "/" + actionExecutedContext.ActionContext.ActionDescriptor.ActionName,
CreateDate = DateTime.Now,
CreatorLoginName = RISContext.Current.CurrentUserInfo.UserName,
IpAddress = request.UserHostAddress,
Detail = Utility.JsonSerialize<LogDetail>(logDetail)
}; logRep.Add(log);
actionExecutedContext.Response = actionExecutedContext.Request.CreateResponse(System.Net.HttpStatusCode.InternalServerError, new ResultData(ResultType.SystemException, actionExecutedContext.Exception.Message));
} /// <summary>
/// 读取request 的提交内容
/// </summary>
/// <param name="actionExecutedContext"></param>
/// <returns></returns>
public string GetRequestValues(HttpActionExecutedContext actionExecutedContext)
{ Stream stream = actionExecutedContext.Request.Content.ReadAsStreamAsync().Result;
Encoding encoding = Encoding.UTF8;
/*
这个StreamReader不能关闭,也不能dispose, 关了就傻逼了
因为你关掉后,后面的管道 或拦截器就没办法读取了
*/
var reader = new StreamReader(stream, encoding);
string result = reader.ReadToEnd();
/*
这里也要注意: stream.Position = 0;
当你读取完之后必须把stream的位置设为开始
因为request和response读取完以后Position到最后一个位置,交给下一个方法处理的时候就会读不到内容了。
*/
stream.Position = ;
return result;
}
}
2. 接下来定义不需要异常处理的Attribute,代码如下:
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true)]
public class NoErrorHandlerAttribute : Attribute
{
}
3. 在HttpConfiguration中注册使用 ErrorHandleAttribute, 注册代码如下:
config.Filters.Add(new ErrorHandleAttribute("错误处理"));
一般在项目的WebApiConfig.cs中注册此属性:
/// <summary>
/// WebApiConfig
/// </summary>
public static class WebApiConfig
{
/// <summary>
/// WebApiConfig Register
/// </summary>
/// <param name="config"></param>
public static void Register(HttpConfiguration config)
{
//config.Filters.Add(new TokenAuthorizeAttribute());
config.MessageHandlers.Add(new CrosHandler());
config.Filters.Add(new ApiAuthorizeAttribute());
config.Filters.Add(new ErrorHandleAttribute("错误处理"));
// Web API 路由
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "mobileapi/{controller}/{action}/{id}",
defaults: new { controller = "Test", action = "GetTestValue", id = RouteParameter.Optional }
);
}
}
这样就可以了,在每个Action中就不要写try catch了,否则不执行ErrorHandle中异常处理逻辑
4. 如果特殊的Controller或者Action不需要纪录和处理异常,可以在Controller或者Action上添加[NoErrorHandler],这样就不会执行ErrorHandle中异常处理逻辑
以上部分是异常的统一处理逻辑, 接下来实现统一写Log的 Attribute功能
统一写Log的 Attribute功能实现:
1. 首先定义写Log的Attribute,继承System.Web.Http.Filters.ActionFilterAttribute,重写OnActionExecuting和OnActionExecuted,代码如下:
public class LogAttribute : ActionFilterAttribute
{
private string _msg = string.Empty;
private string _token = string.Empty;
private string _remark = string.Empty;
public LogAttribute() { } public LogAttribute(string msg)
{
this._msg = msg;
} //http://www.cnblogs.com/shan333chao/p/5002054.html
private static readonly string key = "enterTime";
private const string UserToken = "token";
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
if (actionContext.Request.Method != HttpMethod.Options)
{
// 标记log
var logAction = actionContext.ActionDescriptor.GetCustomAttributes<NoLogAttribute>();
if (!logAction.Any())
{
actionContext.Request.Properties[key] = DateTime.Now.ToBinary();
this._token = GetToken(actionContext, out this._remark);
}
}
base.OnActionExecuting(actionContext);
} public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
if (actionExecutedContext.Request.Method != HttpMethod.Options)
{
object beginTime = null;
if (actionExecutedContext.Request.Properties.TryGetValue(key, out beginTime))
{
DateTime time = DateTime.FromBinary(Convert.ToInt64(beginTime));
var request = HttpContext.Current.Request;
var logDetail = new LogDetail
{
//获取action名称
ActionName = actionExecutedContext.ActionContext.ActionDescriptor.ActionName,
//获取Controller 名称
ControllerName = actionExecutedContext.ActionContext.ActionDescriptor.ControllerDescriptor.ControllerName,
//获取action开始执行的时间
EnterTime = time,
//获取执行action的耗时
CostTime = (DateTime.Now - time).TotalMilliseconds,
Navigator = request.UserAgent,
Token = this._token,
//获取用户ID
UId = UserTokenManager.GetUId(this._token),
//获取访问的ip
IP = request.UserHostAddress,
UserHostName = request.UserHostName,
UrlReferrer = request.UrlReferrer != null ? request.UrlReferrer.AbsoluteUri : "",
Browser = request.Browser.Browser + " - " + request.Browser.Version + " - " + request.Browser.Type,
//获取request提交的参数
Paramaters = GetRequestValues(actionExecutedContext),
//获取response响应的结果
ExecuteResult = GetResponseValues(actionExecutedContext),
AttrTitle = this._msg,
Remark = this._remark,
RequestUri = request.Url.AbsoluteUri
}; // 登录log
var logRep = ContainerManager.Resolve<ISysLogRepository>();
var log = new Log()
{
Action = actionExecutedContext.ActionContext.ActionDescriptor.ControllerDescriptor.ControllerName + "/" + actionExecutedContext.ActionContext.ActionDescriptor.ActionName,
CreateDate = DateTime.Now,
CreatorLoginName = RISContext.Current.CurrentUserInfo.UserName,
IpAddress = request.UserHostAddress,
Detail = Utility.JsonSerialize<LogDetail>(logDetail)
}; logRep.Add(log);
}
} base.OnActionExecuted(actionExecutedContext);
} private string GetToken(System.Web.Http.Controllers.HttpActionContext actionContext, out string msg)
{
Dictionary<string, object> actionArguments = actionContext.ActionArguments;
HttpMethod type = actionContext.Request.Method;
msg = "";
var token = "";
if (type == HttpMethod.Post)
{
if (actionArguments.ContainsKey(UserToken))
{
if (actionArguments[UserToken] != null)
token = actionArguments[UserToken].ToString();
}
else
{
foreach (var value in actionArguments.Values)
{
if (value != null && value.GetType().GetProperty(UserToken) != null)
token = value.GetType().GetProperty(UserToken).GetValue(value, null).ToString();
}
} if (string.IsNullOrEmpty(token))
msg = "匿名用户";
}
else if (type == HttpMethod.Get)
{
if (!actionArguments.ContainsKey(UserToken))
msg = "匿名用户";
// throw new HttpException(401, "还未登录"); if (actionArguments[UserToken] != null)
token = actionArguments[UserToken].ToString();
else
msg = "匿名用户";
}
else if (type == HttpMethod.Options)
{ }
else
{
throw new HttpException(, "暂未开放除POST,GET之外的访问方式!");
}
return token;
}
/// <summary>
/// 读取request 的提交内容
/// </summary>
/// <param name="actionExecutedContext"></param>
/// <returns></returns>
public string GetRequestValues(HttpActionExecutedContext actionExecutedContext)
{ Stream stream = actionExecutedContext.Request.Content.ReadAsStreamAsync().Result;
Encoding encoding = Encoding.UTF8;
/*
这个StreamReader不能关闭,也不能dispose, 关了就傻逼了
因为你关掉后,后面的管道 或拦截器就没办法读取了
*/
var reader = new StreamReader(stream, encoding);
string result = reader.ReadToEnd();
/*
这里也要注意: stream.Position = 0;
当你读取完之后必须把stream的位置设为开始
因为request和response读取完以后Position到最后一个位置,交给下一个方法处理的时候就会读不到内容了。
*/
stream.Position = ;
return result;
} /// <summary>
/// 读取action返回的result
/// </summary>
/// <param name="actionExecutedContext"></param>
/// <returns></returns>
public string GetResponseValues(HttpActionExecutedContext actionExecutedContext)
{
Stream stream = actionExecutedContext.Response.Content.ReadAsStreamAsync().Result;
Encoding encoding = Encoding.UTF8;
/*
这个StreamReader不能关闭,也不能dispose, 关了就傻逼了
因为你关掉后,后面的管道 或拦截器就没办法读取了
*/
var reader = new StreamReader(stream, encoding);
string result = reader.ReadToEnd();
/*
这里也要注意: stream.Position = 0;
当你读取完之后必须把stream的位置设为开始
因为request和response读取完以后Position到最后一个位置,交给下一个方法处理的时候就会读不到内容了。
*/
stream.Position = ;
return result;
}
}
2. 接下来定义不需要记录log的Attribute,代码如下:
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true)]
public class NoErrorHandlerAttribute : Attribute
{
}
3. 注意不要在HttpConfiguration中注册使用 LogAttribute,除非你想所有的请求都写log,在不需要写log的Action上添加[NoLog],否则只需要在需要记录log的Action添加[Log]就可以完成写log的功能。
此篇到此结束,相对比较简单,欢迎大家讨论!
基于.Net Framework 4.0 Web API开发(3):ASP.NET Web APIs 异常的统一处理Attribute 和统一写Log 的Attribute的实现的更多相关文章
- ASP.NET Web API 框架研究 ASP.NET Web API 路由
ASP.NET Web API 核心框架是一个独立的.抽象的消息处理管道,ASP.NET Web API有自己独立的路由系统,是消息处理管道的组成部分,其与ASP.NET路由系统有类似的设计,都能找到 ...
- web api :Routing in ASP.NET Web API
引 Web API 和SignalR都是在服务层. If you are familiar with ASP.NET MVC, Web API routing is very similar to M ...
- 水果项目第3集-asp.net web api开发入门
app后台开发,可以用asp.net webservice技术. 也有一种重量级一点的叫WCF,也可以用来做app后台开发. 现在可以用asp.net web api来开发app后台. Asp.net ...
- Asp.Net Web API 2第五课——Web API路由
Asp.Net Web API 导航 Asp.Net Web API第一课——入门 http://www.cnblogs.com/aehyok/p/3432158.html Asp.Net Web ...
- Implement JSON Web Tokens Authentication in ASP.NET Web API and Identity 2.1 Part 3 (by TAISEER)
http://bitoftech.net/2015/02/16/implement-oauth-json-web-tokens-authentication-in-asp-net-web-api-an ...
- 循序渐进学.Net Core Web Api开发系列【0】:序言与目录
一.序言 我大约在2003年时候开始接触到.NET,最初在.NET framework 1.1版本下写过代码,曾经做过WinForm和ASP.NET开发.大约在2010年的时候转型JAVA环境,这么多 ...
- ASP.NET Core Web API 开发-RESTful API实现
ASP.NET Core Web API 开发-RESTful API实现 REST 介绍: 符合REST设计风格的Web API称为RESTful API. 具象状态传输(英文:Representa ...
- 循序渐进学.Net Core Web Api开发系列【9】:常用的数据库操作
系列目录 循序渐进学.Net Core Web Api开发系列目录 本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi 一.概述 本篇描述一 ...
- 使用react全家桶制作博客后台管理系统 网站PWA升级 移动端常见问题处理 循序渐进学.Net Core Web Api开发系列【4】:前端访问WebApi [Abp 源码分析]四、模块配置 [Abp 源码分析]三、依赖注入
使用react全家桶制作博客后台管理系统 前面的话 笔者在做一个完整的博客上线项目,包括前台.后台.后端接口和服务器配置.本文将详细介绍使用react全家桶制作的博客后台管理系统 概述 该项目是基 ...
随机推荐
- [我给Unity官方视频教程做中文字幕]beginner Graphics – Lessons系列之摄像机介绍Cameras
[我给Unity官方视频教程做中文字幕]beginner Graphics – Lessons系列之摄像机介绍Cameras 最近得到一些Unity官方视频教程,一看全是纯英文的讲解,没有任何字幕或者 ...
- Lock,LockFree,MemoryBarrier,ConcurrentCollection
最近看并行编程书本的一些心得,简单记录下多线程和并行编程必知必会的几个概念,再次加深自己的理解. .NET Framework4提供了一个新的命名空间System.Collections.Concur ...
- PLoP(Pattern Languages of Programs,程序设计的模式语言)
2014/8/1 12:24:21潘加宇 http://www.umlchina.com/News/Content/340.htmPloP大会2014即将举行 PLoP(Pattern Languag ...
- PHP变量作用域
PHP 的全局变量和 C 语言有一点点不同,在 C 语言中,全局变量在函数中自动生效,除非被局部变量覆盖,这可能引起一些问题,有些人可能不小心就改变了一个全局变量.PHP 中全局变量在函数中使用时必须 ...
- 深入理解CSS变形transform(2d)
× 目录 [1]变形原点 [2]变形函数 [3]多值 前面的话 CSS变形transform是一些效果的集合,主要是移动.旋转.缩放和倾斜这四种基本操作,还可以通过设置matrix矩阵来实现更复杂的效 ...
- Uvaoj10054 - The Necklace
/* 题意:打印欧拉回路! 思路:开始时不明白,dfs为什么是后序遍历? 因为欧拉回路本身是一条回路,那么我们在dfs时,可能存在提前找到回路,这条回路可能不是欧拉回路, 因为没有遍历完成所有的边!如 ...
- 如何用Python实现目录遍历
1. 基本实现 [root@localhost ~]# cat dirfile.py import os path='/tmp' for dirpath,dirnames,filenames in o ...
- 使用Nginx配置NodeJs程序(Windows平台)
简介 Nginx("engine x") 是一个高性能的 HTTP 和 反向代理 服务器,也是一个 IMAP/POP3/SMTP 服务器. Nginx 是由 Igor Sysoev ...
- iHover – 30+ 纯 CSS 实现的超炫的图片悬停特效
iHover 是一个令人印象深刻的图片悬停效果集合,完全基于 CSS3 实现,无依赖,能够搭配 Bootstrap 3 很好地工作.基于 SCSS 技术构建,便于修改变量.有模块化的代码,无需包含整个 ...
- MySQL(Navicat)运行.sql文件时报错:[Err] 2006 - MySQL server has gone away 的解决方法
背景: 今天导入一个数据量很大的.sql文件时,报错: 原因: 可能是sql语句过长,超过mysql通信缓存区最大长度. 解决:1. 编辑 MySQL 安装目录下的 my.ini,在最后添加以下内容: ...