新建Web API工程 選Empty,勾選Web API,不要選擇Web API,那樣會把MVC勾上,這裡不需要MVC Web API工程屬性 XML文件用於生成線上文檔 新建Windows服務作為Web API的宿主 WebApiHost工程屬性 控制台應用程式方便調試 Windows服務安裝Mi ...
新建Web API工程
選Empty,勾選Web API,不要選擇Web API,那樣會把MVC勾上,這裡不需要MVC
Web API工程屬性
XML文件用於生成線上文檔
新建Windows服務作為Web API的宿主
WebApiHost工程屬性
控制台應用程式方便調試
Windows服務安裝Microsoft.AspNet.WebApi.OwinSelfHost
工程WebApiDemo需要引用Microsoft.Owin.dll
WebApiDemo安裝Swashbuckle
應用程式入口

using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.ServiceProcess; using System.Text; using System.Threading.Tasks; namespace WebApiHost { static class Program { /// <summary> /// 應用程式的主入口點。 /// </summary> static void Main(string[] args) { RunDebug(); StartService(); } private static void StartService() { ServiceBase[] ServicesToRun; ServicesToRun = new ServiceBase[] { new WebApiHostService() }; ServiceBase.Run(ServicesToRun); } [Conditional("DEBUG")] private static void RunDebug() { new WebApiHostService().Start(); Console.WriteLine("啟動成功"); Console.ReadLine(); } } }View Code
啟動Web API服務

using Microsoft.Owin.Hosting; using System; using System.Collections.Generic; using System.ComponentModel; using System.Configuration; using System.Data; using System.Diagnostics; using System.Linq; using System.ServiceProcess; using System.Text; using System.Threading; using System.Threading.Tasks; using Utils; namespace WebApiHost { public partial class WebApiHostService : ServiceBase { #region 構造函數 public WebApiHostService() { InitializeComponent(); } #endregion #region OnStart 啟動服務 protected override void OnStart(string[] args) { int port = int.Parse(ConfigurationManager.AppSettings["WebApiServicePort"]); StartOptions options = new StartOptions(); options.Urls.Add("http://127.0.0.1:" + port); options.Urls.Add("http://localhost:" + port); options.Urls.Add("http://+:" + port); WebApp.Start<Startup>(options); LogUtil.Log("Web API 服務 啟動成功"); } #endregion #region OnStop 停止服務 protected override void OnStop() { LogUtil.Log("Web API 服務 停止成功"); Thread.Sleep(100); //等待一會,待日誌寫入文件 } #endregion #region Start 啟動服務 public void Start() { OnStart(null); } #endregion } }View Code
配置Web API路由、攔截器以及初始化Swagger線上文檔

using Owin; using WebApiDemo; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Web.Http; namespace WebApiHost { class Startup { public void Configuration(IAppBuilder appBuilder) { HttpConfiguration config = new HttpConfiguration(); config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); config.Filters.Add(new MyExceptionFilter()); config.Filters.Add(new MyActionFilter()); SwaggerConfig.Register(config); appBuilder.UseWebApi(config); } } }View Code
介面實現
1、繼承ApiController
2、RoutePrefix設置路由首碼
3、SwaggerResponse用於生成線上文檔描述

using Models; using Swashbuckle.Swagger.Annotations; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Net; using System.Net.Http; using System.Web.Http; using Utils; namespace WebApiDemo.Controllers { /// <summary> /// 測試介面 /// </summary> [RoutePrefix("api/test")] public class TestController : ApiController { #region TestGet 測試GET請求 /// <summary> /// 測試GET請求 /// </summary> /// <param name="val">測試參數</param> [HttpGet] [Route("TestGet")] [SwaggerResponse(HttpStatusCode.OK, "返回JSON", typeof(JsonListResult<TestGetResult>))] public HttpResponseMessage TestGet(string val) { List<TestGetResult> list = new List<TestGetResult>(); for (int i = 1; i <= 10; i++) { TestGetResult item = new TestGetResult(); item.testValue1 = i.ToString(); item.testValue2 = i; item.testValue3 = "這是傳入參數:" + val; list.Add(item); } var jsonResult = new JsonListResult<TestGetResult>(list, list.Count); return ApiHelper.ToJson(jsonResult); } #endregion #region TestPost 測試POST請求 /// <summary> /// 測試POST請求 /// </summary> /// <param name="data">POST數據</param> [HttpPost] [Route("TestPost")] [SwaggerResponse(HttpStatusCode.OK, "返回JSON", typeof(JsonResult<CommonSubmitResult>))] public HttpResponseMessage TestPost([FromBody] TestPostData data) { JsonResult jsonResult = null; if (data == null) return ApiHelper.ToJson(new JsonResult("請檢查參數格式", ResultCode.參數不正確)); string msg = "操作成功,這是您傳入的參數:" + data.testArg; jsonResult = new JsonResult<CommonSubmitResult>(new CommonSubmitResult() { msg = msg, id = "1" }); return ApiHelper.ToJson(jsonResult); } #endregion } #region 數據類 /// <summary> /// TestGet介面返回結果 /// </summary> public class TestGetResult { /// <summary> /// 測試數據1 /// </summary> public string testValue1 { get; set; } /// <summary> /// 測試數據2 /// </summary> public int testValue2 { get; set; } /// <summary> /// 測試數據3 /// </summary> public string testValue3 { get; set; } } /// <summary> /// TestPost介面參數 /// </summary> [MyValidate] public class TestPostData { /// <summary> /// 測試參數1 /// </summary> [Required] public string testArg { get; set; } /// <summary> /// 測試日期參數 /// </summary> [Required] [DateTime(Format = "yyyyMMddHHmmss")] public string testTime { get; set; } } /// <summary> /// TestPost介面返回結果 /// </summary> public class TestPostResult { /// <summary> /// 測試數據1 /// </summary> public string testValue1 { get; set; } } #endregion }View Code
MyValidate屬性表示該數據需要校驗
Required必填校驗
DateTime日期輸入格式校驗
輔助類ApiHelper.cs

using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using System.Text; using System.Web; namespace Utils { public class ApiHelper { public static HttpResponseMessage ToJson(object obj) { string str = JsonConvert.SerializeObject(obj); HttpResponseMessage result = new HttpResponseMessage { Content = new StringContent(str, Encoding.UTF8, "application/json") }; return result; } } }View Code
輔助類ServiceHelper.cs

using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Web; namespace Utils { /// <summary> /// 服務幫助類 /// </summary> public class ServiceHelper { public static ConcurrentDictionary<Type, object> _dict = new ConcurrentDictionary<Type, object>(); /// <summary> /// 獲取對象 /// </summary> public static T Get<T>() where T : new() { Type type = typeof(T); object obj = _dict.GetOrAdd(type, (key) => new T()); return (T)obj; } /// <summary> /// 獲取對象 /// </summary> public static T Get<T>(Func<T> func) where T : new() { Type type = typeof(T); object obj = _dict.GetOrAdd(type, (key) => func()); return (T)obj; } } }View Code
JsonResult類

using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Net; using System.Web; namespace Models { /// <summary> /// Json返回 /// </summary> public class JsonResult { /// <summary> /// 介面是否成功 /// </summary> [Required] public virtual bool success { get; set; } /// <summary> /// 結果編碼 /// </summary> [Required] public virtual ResultCode resultCode { get; set; } /// <summary> /// 介面錯誤信息 /// </summary> public virtual string errorMsg { get; set; } /// <summary> /// 記錄總數(可空類型) /// </summary> public virtual int? total { get; set; } /// <summary> /// 預設構造函數 /// </summary> public JsonResult() { } /// <summary> /// 介面失敗返回數據 /// </summary> public JsonResult(string errorMsg, ResultCode resultCode) { this.success = false; this.resultCode = resultCode; this.errorMsg = errorMsg; } } /// <summary> /// Json返回 /// </summary> public class JsonResult<T> : JsonResult { /* 子類重寫屬性解決JSON序列化屬性順序問題 */ /// <summary> /// 介面是否成功 /// </summary> [Required] public override bool success { get; set; } /// <summary> /// 結果編碼 /// </summary> [Required] public override ResultCode resultCode { get; set; } /// <summary> /// 介面錯誤信息 /// </summary> public override string errorMsg { get; set; } /// <summary> /// 記錄總數(可空類型) /// </summary> public override int? total { get; set; } /// <summary> /// 數據 /// </summary> public T info { get; set; } /// <summary> /// 介面成功返回數據 /// </summary> public JsonResult(T info) { this.success = true; this.resultCode = ResultCode.OK; this.info = info; this.total = null; } } /// <summary> /// Json返回 /// </summary> public class JsonListResult<T> : JsonResult { /* 子類重寫屬性解決JSON序列化屬性順序問題 */ /// <summary> /// 介面是否成功 /// </summary> [Required] public override bool success { get; set; } /// <summary> /// 結果編碼 /// </summary> [Required] public override ResultCode resultCode { get; set; } /// <summary> /// 介面錯誤信息 /// </summary> public override string errorMsg { get; set; } /// <summary> /// 記錄總數(可空類型) /// </summary> public override int? total { get; set; } /// <summary> /// 數據 /// </summary> public List<T> info { get; set; } /// <summary> /// 介面成功返回數據 /// </summary> public JsonListResult(List<T> list, int total) { this.success = true; this.resultCode = ResultCode.OK; this.info = list; this.total = total; } /// <summary> /// 介面成功返回數據 /// </summary> public JsonListResult(List<T> list, PagerModel pager) { this.success = true; this.resultCode = ResultCode.OK; this.info = list; this.total = pager.totalRows; } } /// <summary> /// 結果編碼 /// </summary> public enum ResultCode { OK = 200, token不匹配或已過期 = 1001, 請求頭中不存在token = 1002, 用戶不存在 = 1101, 密碼不正確 = 1102, 參數不正確 = 1201, 操作失敗 = 1301, 資源不存在 = 1302, 其他錯誤 = 1401, 伺服器內部錯誤 = 1501 } /// <summary> /// 通用返回數據 /// </summary> public class CommonSubmitResult { /// <summary> /// 提示信息 /// </summary> public string msg { get; set; } /// <summary> /// 記錄ID /// </summary> public string id { get; set; } } /// <summary> /// 通用返回數據 /// </summary> public class CommonMsgResult { /// <summary> /// 提示信息 /// </summary> public string msg { get; set; } } }View Code
異常攔截器
異常在這裡統一處理,介面方法中不需要再加try catch

using Models; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Text; using System.Web; using System.Web.Http.Filters; using Utils; namespace WebApiDemo { public class MyExceptionFilter : ExceptionFilterAttribute { //重寫基類的異常處理方法 public override void OnException(HttpActionExecutedContext actionExecutedContext) { var result = new JsonResult("攔截到異常:" + actionExecutedContext.Exception.Message, ResultCode.伺服器內部錯誤); LogUtil.Error(actionExecutedContext.Exception); actionExecutedContext.Response = ApiHelper.ToJson(result); base.OnException(actionExecutedContext); } } }View Code
方法攔截器
1、在攔截器中校驗證token
2、在攔截器中校驗POST和GET參數
3、在攔截器中寫操作日誌

using Microsoft.Owin; using Models; using Newtonsoft.Json; using Swashbuckle.Swagger; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Net; using System.Net.Http; using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Web; using System.Web.Http; using System.Web.Http.Controllers; using System.Web.Http.Filters; using Utils; namespace WebApiDemo { /// <summary> /// 攔截器 /// </summary> public class MyActionFilter : ActionFilterAttribute { #region 變數 private Dictionary<string, string> _dictActionDesc = ServiceHelper.Get<Dictionary<string, string>>(() => XmlUtil.GetActionDesc()); #endregion #region OnActionExecuting 執行方法前 /// <summary> /// 執行方法前 /// </summary> public override void OnActionExecuting(HttpActionContext actionContext) { base.OnActionExecuting(actionContext); //token驗證 Collection<AllowAnonymousAttribute> attributes = actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>(); if (attributes.Count == 0) { IEnumerable<string> value; if (actionContext.Request.Headers.TryGetValues("token", out value)) { string token = value.ToArray()[0]; if (false) //todo:token驗證 { actionContext.Response = ApiHelper.ToJson(new JsonResult("token不匹配或已過期", ResultCode.token不匹配或已過期)); return; } } else { actionContext.Response = ApiHelper.ToJson(new JsonResult("請求頭中不存在token", ResultCode.請求頭中不存在token)); return; } } //post參數驗證 if (actionContext.Request.Method == HttpMethod.Post) { foreach (string key in actionContext.ActionArguments.Keys) { object value = actionContext.ActionArguments[key]; if (value != null) { if (value.GetType().GetCustomAttributes(typeof(MyValidateAttribute), false).Length > 0) { string errMsg = null; if (!ValidatePropertyUtil.Validate(value, out errMsg)) { JsonResult jsonResult = new JsonResult(errMsg, ResultCode.參數不正確); actionContext.Response = ApiHelper.ToJson(jsonResult);