您的当前位置:首页正文

ASP.NET MVC API 接口验证的示例代码

2020-11-27 来源:要发发教育

项目中有一个留言消息接口,接收其他系统的留言和展示留言,参考了网上的一些API验证方法,发现使用通用权限管理系统提供的验证方法最完美。

下面将实现的完整思路共享

1、WebApiConfig全局处理

/// <summary>
 /// WebApiConfig 
 /// 路由基础配置。
 /// 
 /// 
 /// 修改记录
 /// 
 /// 2016.11.01 版本:2.0 宋彪 对日期格式进行统一处理。
 /// 2016.10.30 版本:2.0 宋彪 解决json序列化时的循环引用问题。
 /// 2016.10.28 版本:2.0 宋彪 回传响应格式 $format 支持。
 /// 2016.09.01 版本:1.0 宋彪 创建。
 /// 
 /// 版本:1.0
 /// 
 /// <author>
 /// <name>宋彪</name>
 /// <date>2016.09.01</date>
 /// </author> 
 /// </summary>
 public static class WebApiConfig
 {
 /// <summary>
 /// 注册全局配置服务
 /// </summary>
 /// <param name="config"></param>
 public static void Register(HttpConfiguration config)
 {
 // Web API configuration and services

 //强制https访问
 //config.Filters.Add(new ForceHttpsAttribute());
 // 统一回传格式
 config.Filters.Add(new ApiResultAttribute());
 // 发生异常时处理
 config.Filters.Add(new ApiErrorHandleAttribute());
 // ToKen身份验证过滤器 更方便 不需要在这里了 具有改标签的就会自动检查
 //config.Filters.Add(new ApiAuthFilterAttribute());
 // 解决json序列化时的循环引用问题
 config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
 //对日期格式进行统一处理
 config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(
 new IsoDateTimeConverter()
 {
 DateTimeFormat = "yyyy-MM-dd hh:mm:ss"
 }
 );

 // Web API routes 路由
 config.MapHttpAttributeRoutes();

 config.Routes.MapHttpRoute(
 name: "DefaultApi",
 routeTemplate: "api/{controller}/{action}/{id}",
 defaults: new { id = RouteParameter.Optional }
 );

 // 干掉XML序列化器
 //config.Formatters.Remove(config.Formatters.XmlFormatter);
 //在请求的Url加上 ?$format=xml,便可以指定响应格式
 config.Formatters.XmlFormatter.AddQueryStringMapping("$format", "xml", "application/xml");
 config.Formatters.JsonFormatter.AddQueryStringMapping("$format", "json", "application/json");
 }
 }

2、身份验证过滤器

 using DotNet.Business;
 using DotNet.Utilities;
 using DotNet.Tracking.API.Common;

 /// <summary>
 /// ApiAuthFilterAttribute
 /// 身份验证过滤器,具有ApiAuthFilterAttribute标签属性的方法会自动检查
 /// 
 /// 
 /// 修改纪录
 /// 
 /// 2016-10-11 版本:1.0 SongBiao 创建文件。 
 /// 
 /// <author>
 /// <name>SongBiao</name>
 /// <date>2016-10-11</date>
 /// </author>
 /// </summary>
 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
 public class ApiAuthFilterAttribute : AuthorizationFilterAttribute
 {
 /// <summary>
 /// 未授权时的提示信息
 /// </summary>
 private const string UnauthorizedMessage = "请求未授权,拒绝访问。";

 /// <summary>
 /// 权限进入
 /// </summary>
 /// <param name="actionContext"></param>
 public override void OnAuthorization(HttpActionContext actionContext)
 {
 base.OnAuthorization(actionContext);
 // 允许匿名访问
 if (actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Count > 0) 
 {
 return;
 }

 string systemCode = APIOperateContext.Current.SystemCode;
 string permissionCode = APIOperateContext.Current.PermissionCode;
 string appKey = APIOperateContext.Current.AppKey;
 string appSecret = APIOperateContext.Current.AppSecret; 
 if (string.IsNullOrWhiteSpace(appKey) || string.IsNullOrWhiteSpace(appSecret))
 {
 //未验证(登录)的用户, 而且是非匿名访问,则转向登录页面 
 //actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
 //actionContext.Response.Content = new StringContent("<p>Unauthorized</p>", Encoding.UTF8, "text/html");
 var response = actionContext.Response= actionContext.Response?? new HttpResponseMessage();
 response.StatusCode = HttpStatusCode.Unauthorized;
 BaseResult result = new BaseResult
 {
 Status = false,
 StatusMessage = UnauthorizedMessage
 };
 response.Content = new StringContent(result.ToJson(), Encoding.UTF8, "application/json");
 }
 else
 {
 // 检查 AppKey 和 AppSecret
 BaseResult result = BaseServicesLicenseManager.CheckService(appKey, appSecret, false, 0, 0, systemCode, permissionCode);
 if (!result.Status)
 {
 var response = actionContext.Response = actionContext.Response?? new HttpResponseMessage();
 response.Content = new StringContent(result.ToJson(), Encoding.UTF8, "application/json");
 }
 }
 
 }
 }

3、统一回传格式

 /// <summary>
 /// ApiResultAttribute
 /// 统一回传格式
 /// 
 /// 修改纪录
 /// 
 /// 2016-10-31 版本:1.0 宋彪 创建文件。
 /// 
 /// <author>
 /// <name>宋彪</name>
 /// <date>2016-10-31</date>
 /// </author>
 /// </summary>
 public class ApiResultAttribute : ActionFilterAttribute
 {
 /// <summary>
 /// 重写回传的处理
 /// </summary>
 /// <param name="actionExecutedContext"></param>
 public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
 {
 // 快件跟踪接口传的是format,不用走这里
 if (actionExecutedContext.Request.Properties.ContainsKey("format"))
 {
 // 若发生例外则不在这边处理 在异常中处理 ApiErrorHandleAttribute
 if (actionExecutedContext.Exception != null)
 return;
 base.OnActionExecuted(actionExecutedContext);
 var result = new ApiResultModel();
 // 取得由 API 返回的状态码
 result.Status = actionExecutedContext.ActionContext.Response.StatusCode;
 // 取得由 API 返回的资料
 result.Data = actionExecutedContext.ActionContext.Response.Content.ReadAsAsync<object>().Result;
 // 重新封装回传格式
 actionExecutedContext.Response = actionExecutedContext.Request.CreateResponse(result.Status, result);
 }
 }
 }

4、全局异常处理

 using DotNet.Utilities;
 using DotNet.Tracking.API.Common;
 using DotNet.Tracking.API.Controllers;
 using DotNet.Tracking.API.Models;

 /// <summary>
 /// ApiErrorHandleAttribute
 /// 全局异常处理
 /// 
 /// 修改纪录
 /// 
 /// 2016-10-31 版本:1.0 宋彪 创建文件。
 /// 
 /// <author>
 /// <name>宋彪</name>
 /// <date>2016-10-31</date>
 /// </author>
 /// </summary>

 public class ApiErrorHandleAttribute : System.Web.Http.Filters.ExceptionFilterAttribute
 {
 /// <summary>
 /// 异常统一处理
 /// </summary>
 /// <param name="actionExecutedContext"></param>
 public override void OnException(System.Web.Http.Filters.HttpActionExecutedContext actionExecutedContext)
 {
 base.OnException(actionExecutedContext);
 // 取得发生例外时的错误讯息
 var errorMessage = actionExecutedContext.Exception.Message;

 // 异常记录
 string parameters = APIOperateContext.GetRequestParameters();
 NLogHelper.Trace(actionExecutedContext.Exception, BaseSystemInfo.SystemCode + " ApiErrorHandleAttribute OnException 完整的请求地址及参数 : " + parameters);
 // 2016-11-01 加入异常邮件提醒
 NLogHelper.InfoMail(actionExecutedContext.Exception, BaseSystemInfo.SystemCode + " ApiErrorHandleAttribute OnException 完整的请求地址及参数 : " + parameters);

 var result = new ApiResultModel()
 {
 Status = HttpStatusCode.BadRequest,
 ErrorMessage = errorMessage
 };
 // 重新打包回传的讯息
 actionExecutedContext.Response = actionExecutedContext.Request.CreateResponse(result.Status, result);
 }
 }

5、接口操作的上下文

 using DotNet.Business;
 using DotNet.Model;
 using DotNet.Utilities;

 /// <summary>
 /// APIOperateContext
 /// 接口操作的上下文
 /// 跟上下文有关的一些通用的东西放在这里处理
 /// 
 /// 修改纪录
 /// 
 /// 2016-10-31 版本:1.0 宋彪 创建文件。
 /// 
 /// <author>
 /// <name>宋彪</name>
 /// <date>2016-10-31</date>
 /// </author>
 /// </summary>
 public class APIOperateContext
 {
 /// <summary>
 /// 获取当前 操作上下文 (为每个处理浏览器请求的服务器线程 单独创建 操作上下文)
 /// </summary>
 public static APIOperateContext Current
 {
 get
 {
 APIOperateContext oContext = CallContext.GetData(typeof(APIOperateContext).Name) as APIOperateContext;
 if (oContext == null)
 {
 oContext = new APIOperateContext();
 CallContext.SetData(typeof(APIOperateContext).Name, oContext);
 }
 return oContext;
 }
 }

 #region Http上下文 及 相关属性
 /// <summary>
 /// Http上下文
 /// </summary>
 public HttpContext ContextHttp
 {
 get
 {
 return HttpContext.Current;
 }
 }

 /// <summary>
 /// 
输出对象 /// </summary> public HttpResponse Response { get { return ContextHttp.Response; } } /// <summary> /// 请求对象 /// </summary> public HttpRequest Request { get { return ContextHttp.Request; } } /// <summary> /// Session对象 /// </summary> System.Web.SessionState.HttpSessionState Session { get { return ContextHttp.Session; } } #endregion /// <summary> /// 获取全部请求参数,get和post的 简化版 /// </summary> public static string GetRequestParameters() { string query = HttpContext.Current.Request.Url.Query; NameValueCollection nvc; string baseUrl; ParseUrl(query, out baseUrl, out nvc); List<string> list = new List<string>() { }; foreach (var key in nvc.AllKeys) { list.Add(key + "=" + nvc[key]); } var form = HttpContext.Current.Request.Form; foreach (var key in form.AllKeys) { list.Add(key + "=" + form[key]); } string result = HttpContext.Current.Request.Url.AbsoluteUri + "?" + string.Join("&", list); return result; } /// <summary> /// 分析 url 字符串中的参数信息 /// 针对get请求的 /// </summary> /// <param name="url">输入的 URL</param> /// <param name="baseUrl">输出 URL 的基础部分</param> /// <param name="nvc">输出分析后得到的 (参数名,参数值) 的集合</param> public static void ParseUrl(string url, out string baseUrl, out NameValueCollection nvc) { if (url == null) { throw new ArgumentNullException("url"); } nvc = new NameValueCollection(); baseUrl = ""; if (url == "") { return; } int questionMarkIndex = url.IndexOf('?'); if (questionMarkIndex == -1) { baseUrl = url; return; } baseUrl = url.Substring(0, questionMarkIndex); if (questionMarkIndex == url.Length - 1) { return; } string ps = url.Substring(questionMarkIndex + 1); // 开始分析参数对 Regex re = new Regex(@"(^|&)?(\w+)=([^&]+)(&|$)?", RegexOptions.Compiled); MatchCollection mc = re.Matches(ps); foreach (Match m in mc) { nvc.Add(m.Result("$2").ToLower(), m.Result("$3")); } } /// <summary> /// 系统编号 /// </summary> public string SystemCode { get { return Request["systemCode"] ?? "Base"; } } /// <summary> /// 权限编号 /// </summary> public string PermissionCode { get { return Request["permissionCode"]; } } /// <summary> /// 访问接口的应用传来AppKey /// </summary> public string AppKey { get { return Request["appKey"]; } } /// <summary> /// 访问接口的应用传来AppSecret /// </summary> public string AppSecret { get { return Request["appSecret"]; } } private BaseUserInfo _userInfo = null; /// <summary> /// 获取当前用户 /// 通过接口AppKey和AppSecret获取的用户 /// </summary> /// <returns></returns> public BaseUserInfo UserInfo { get { BaseUserInfo userInfo = null; BaseUserEntity userEntity = BaseUserManager.GetObjectByCodeByCache(AppKey); if (userEntity != null) { if (BaseServicesLicenseManager.CheckServiceByCache(userEntity.Id, AppSecret)) { userInfo = new BaseUserInfo(); userInfo.Id = userEntity.Id; userInfo.RealName = userEntity.RealName; userInfo.UserName = userEntity.UserName; userInfo.IPAddress = Utilities.GetIPAddress(true); } } return userInfo; } } #region 业务库连接 /// <summary> /// 业务库连接 /// </summary> public static IDbHelper BusinessDbHelper { get { return DbHelperFactory.GetHelper(BaseSystemInfo.BusinessDbType, BaseSystemInfo.BusinessDbConnection); } } #endregion #region 用户中心库连接 /// <summary> /// 用户中心库连接 /// </summary> public static IDbHelper UserCenterDbHelper { get { return DbHelperFactory.GetHelper(BaseSystemInfo.UserCenterDbType, BaseSystemInfo.UserCenterDbConnection); } } #endregion }

7、统一回传格式实体

 /// <summary>
 /// ApiResultModel
 /// 统一回传格式实体
 /// 
 /// 修改纪录
 /// 
 /// 2016-10-31 版本:1.0 宋彪 创建文件。
 /// 
 /// <author>
 /// <name>宋彪</name>
 /// <date>2016-10-31</date>
 /// </author>
 /// </summary>
 public class ApiResultModel
 {
 public HttpStatusCode Status { get; set; }

 //public JsonResult<T> Data { get; set; }
 public object Data { get; set; }
 public string ErrorMessage { get; set; }
 }

8、留言相关接口

 /// <summary>
 /// MessageBookController
 /// 留言相关接口
 /// 
 /// 修改纪录
 /// 
 /// 2016-10-31 版本:1.0 宋彪 创建文件。
 /// 
 /// <author>
 /// <name>宋彪</name>
 /// <date>2016-10-31</date>
 /// </author>
 /// </summary>
 [ApiAuthFilter]
 public class CustomerMessageController : ApiController
 {
 /// <summary>
 /// 保存单号留言信息
 /// </summary>
 /// <param name="messageBook"></param>
 /// <returns></returns>
 [HttpPost]
 //[AllowAnonymous] 不需要验证的就加这个标签
 public IHttpActionResult Add([FromBody]MsgbookCusEntity messageBook)
 {
 BaseResult baseResult = new BaseResult();
 if (string.IsNullOrWhiteSpace(messageBook.SystemFrom))
 {
 baseResult.Status = false;
 baseResult.StatusMessage = "SystemFrom参数不可为空";
 }
 else
 {
 try
 {
 MsgbookCusManager manager = new MsgbookCusManager(APIOperateContext.BusinessDbHelper, APIOperateContext.Current.UserInfo);
 MsgbookCusEntity model = new MsgbookCusEntity();
 model.Id = Guid.NewGuid().ToString("N");
 model.Message = messageBook.Message;
 model.SendEmail = messageBook.SendEmail;
 model.SendTelephone = messageBook.SendTelephone;
 model.Message = messageBook.Message;
 model.BillCode = messageBook.BillCode;
 model.SystemFrom = messageBook.SystemFrom;
 model.DeletionStateCode = 0;
 manager.Add(model, false, false);

 baseResult.Status = true;
 baseResult.StatusMessage = "添加成功。";
 }
 catch (Exception ex)
 {
 NLogHelper.Warn(ex, "CustomerMessageController AddBillMessage 异常");
 baseResult.Status = false;
 baseResult.StatusMessage = "异常:" + ex.Message;
 }
 }

 return Ok(baseResult);
 }

 /// <summary>
 /// 获取某个单号的留言
 /// </summary>
 /// <param name="billCode"></param>
 /// <returns></returns>
 [HttpGet]
 public IHttpActionResult GetList(string billCode)
 {
 JsonResult<List<MsgbookCusEntity>> jsonResult = new JsonResult<List<MsgbookCusEntity>>();
 try
 {
 MsgbookCusManager manager = new MsgbookCusManager(APIOperateContext.BusinessDbHelper, APIOperateContext.Current.UserInfo);
 List<MsgbookCusEntity> list = new List<MsgbookCusEntity>();
 list = manager.GetList<MsgbookCusEntity>(new KeyValuePair<string, object>(MsgbookCusEntity.FieldBillCode, billCode)
 , new KeyValuePair<string, object>(MsgbookCusEntity.FieldDeletionStateCode, 0));

 jsonResult.Status = true;
 jsonResult.RecordCount = list.Count;
 jsonResult.Data = list;
 jsonResult.StatusMessage = "获取成功";
 }
 catch (Exception ex)
 {
 NLogHelper.Warn(ex, "CustomerMessageController AddBillMessage 异常");
 jsonResult.Status = false;
 jsonResult.StatusMessage = "异常:" + ex.Message;
 }

 return Ok(jsonResult);
 }
 }

9、接口调用方法

 /// <summary>
 /// 测试留言接口调用
 /// </summary>
 /// <returns></returns>
 public ActionResult AddCustomerMessage()
 {
 string url = "http://192.168.1.88:808/api/CustomerMessage/Add?";
 WebClient webClient = new WebClient();
 NameValueCollection postValues = new NameValueCollection();

 postValues.Add("Message", "填写您的留言内容吧");
 postValues.Add("SendEmail", "youemail@qq.com");
 postValues.Add("SendTelephone", "021-60375335");
 postValues.Add("Code", "661137858");
 postValues.Add("AppKey", "wssavbcn");
 postValues.Add("AppSecret", "350e66b1e6564b0a817163erwwwwe8");
 postValues.Add("SystemFrom", "官网");

 byte[] responseArray = webClient.UploadValues(url, postValues);
 string response = Encoding.UTF8.GetString(responseArray);

 return Content(response);
 }