Startup.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Collections.Generic;
  4. using System.ComponentModel.DataAnnotations;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Net;
  8. using System.Net.Http;
  9. using System.Runtime.Remoting.Contexts;
  10. using System.Text;
  11. using System.Web.Http;
  12. using System.Web.Http.Description;
  13. using System.Web.Http.Filters;
  14. using System.Web.Http.Metadata;
  15. using System.Web.Http.Routing;
  16. using System.Web.Http.Validation;
  17. using System.Web.Http.Validation.Providers;
  18. using System.Xml;
  19. using Microsoft.Owin;
  20. using Microsoft.Owin.Cors;
  21. using Newtonsoft.Json;
  22. using Newtonsoft.Json.Serialization;
  23. using Owin;
  24. using Swashbuckle.Application;
  25. using Swashbuckle.Swagger;
  26. using XdCxRhDW.App.WebAPI;
  27. [assembly: OwinStartup(typeof(XdCxRhDW.App.WebAPI.Startup))]
  28. namespace XdCxRhDW.App.WebAPI
  29. {
  30. class Startup
  31. {
  32. public void Configuration(IAppBuilder app)
  33. {
  34. HttpConfiguration config = new HttpConfiguration();
  35. IEnumerable<ModelValidatorProvider> modelValidatorProviders = config.Services.GetModelValidatorProviders();
  36. DataAnnotationsModelValidatorProvider provider = (DataAnnotationsModelValidatorProvider)
  37. modelValidatorProviders.Single(x => x is DataAnnotationsModelValidatorProvider);
  38. provider.RegisterDefaultValidatableObjectAdapter(typeof(CustomModelValidator));
  39. JsonSerializerSettings setting = new JsonSerializerSettings()
  40. {
  41. //日期类型默认格式化处理
  42. DateFormatHandling = DateFormatHandling.MicrosoftDateFormat,
  43. DateFormatString = "yyyy-MM-dd HH:mm:ss",
  44. //驼峰样式
  45. ContractResolver = new CamelCasePropertyNamesContractResolver(),
  46. //空值处理
  47. //NullValueHandling = NullValueHandling.Ignore,
  48. //设置序列化的最大层数
  49. MaxDepth = 10,
  50. //解决json序列化时的循环引用问题
  51. ReferenceLoopHandling = ReferenceLoopHandling.Ignore
  52. };
  53. config.Formatters.JsonFormatter.SerializerSettings = setting;
  54. config.Formatters.Remove(config.Formatters.XmlFormatter);
  55. config.Filters.Add(new HandlerErrorAttribute());
  56. //config.Routes.MapHttpRoute("DefaultApiWithId", "Api/{controller}/{id}", new { id = RouteParameter.Optional }, new { id = @"\d+" });
  57. config.Routes.MapHttpRoute("DefaultApiWithAction", "Api/{controller}/{action}");
  58. //config.Routes.MapHttpRoute("DefaultApiGet", "Api/{controller}", new { action = "Get" }, new { httpMethod = new HttpMethodConstraint(HttpMethod.Get) });
  59. //config.Routes.MapHttpRoute("DefaultApiPost", "Api/{controller}", new { action = "Post" }, new { httpMethod = new HttpMethodConstraint(HttpMethod.Post) });
  60. ConfigureSwagger(config);
  61. //添加路由路径
  62. config.MapHttpAttributeRoutes();
  63. app.UseCors(CorsOptions.AllowAll);
  64. //app.Use<LoggingMiddleware>();
  65. app.UseWebApi(config);
  66. }
  67. private static void ConfigureSwagger(HttpConfiguration config)
  68. {
  69. var thisAssembly = typeof(Startup).Assembly;
  70. config.EnableSwagger(c =>
  71. {
  72. c.SingleApiVersion("v1", "多模式融合定位平台Http接口");
  73. //设置接口描述xml路径地址
  74. var webApiXmlPath1 = $"{AppDomain.CurrentDomain.BaseDirectory}{Path.GetFileNameWithoutExtension(System.AppDomain.CurrentDomain.FriendlyName)}.xml";
  75. c.IncludeXmlComments(webApiXmlPath1);
  76. var webApiXmlPath2 = $"{AppDomain.CurrentDomain.BaseDirectory}XdCxRhDw.Dto.xml";
  77. c.IncludeXmlComments(webApiXmlPath2);
  78. c.UseFullTypeNameInSchemaIds();
  79. //加入控制器描述
  80. c.CustomProvider((defaultProvider) => new SwaggerControllerDescProvider(defaultProvider, webApiXmlPath1));
  81. c.OperationFilter<FileUploadOperation>();
  82. })
  83. .EnableSwaggerUi(c =>
  84. {
  85. c.InjectJavaScript(thisAssembly, "XdCxRhDW.App.WebAPI.Swagger.js");
  86. c.DocumentTitle("");
  87. });
  88. }
  89. /// <summary>
  90. /// Swagger文件上传特性标注
  91. /// </summary>
  92. [AttributeUsage(AttributeTargets.Method)]
  93. public sealed class SwaggerFormAttribute : Attribute
  94. {
  95. public SwaggerFormAttribute()
  96. {
  97. this.Name = "文件";
  98. this.Description = "选择文件";
  99. }
  100. /// <summary>
  101. /// Swagger特性标注
  102. /// </summary>
  103. /// <param name="name"></param>
  104. /// <param name="description"></param>
  105. public SwaggerFormAttribute(string name, string description)
  106. {
  107. Name = name;
  108. Description = description;
  109. }
  110. /// <summary>
  111. /// 名称
  112. /// </summary>
  113. public string Name { get; private set; }
  114. /// <summary>
  115. /// 描述
  116. /// </summary>
  117. public string Description { get; private set; }
  118. }
  119. public class FileUploadOperation : IOperationFilter
  120. {
  121. public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
  122. {
  123. if (operation.parameters == null)
  124. {
  125. operation.parameters = new List<Parameter>();
  126. }
  127. var requestAttributes = apiDescription.GetControllerAndActionAttributes<SwaggerFormAttribute>();
  128. foreach (var attr in requestAttributes)
  129. {
  130. operation.parameters.Add(new Parameter
  131. {
  132. description = attr.Description,
  133. name = attr.Name,
  134. @in = "formData",
  135. required = true,
  136. type = "file",
  137. });
  138. operation.consumes.Add("multipart/form-data");
  139. }
  140. //if (operation.operationId.ToLower() == "apivaluesuploadpost")
  141. //{
  142. // operation.parameters.Clear();
  143. // operation.parameters.Add(new Parameter
  144. // {
  145. // name = "uploadedFile",
  146. // @in = "formData",
  147. // description = "Upload File",
  148. // required = true,
  149. // type = "file"
  150. // });
  151. // operation.consumes.Add("multipart/form-data");
  152. //}
  153. //判断上传文件的类型,只有上传的类型是IFormCollection的才进行重写。
  154. //var paras = apiDescription.ActionDescriptor.GetParameters();
  155. //if (paras.Any(w => w.ParameterType == typeof(IFormCollection)))
  156. //{
  157. // Dictionary<string, OpenApiSchema> schema = new Dictionary<string, OpenApiSchema>();
  158. // schema["fileName"] = new OpenApiSchema { Description = "Select file", Type = "string", Format = "binary" };
  159. // Dictionary<string, OpenApiMediaType> content = new Dictionary<string, OpenApiMediaType>();
  160. // content["multipart/form-data"] = new OpenApiMediaType { Schema = new OpenApiSchema { Type = "object", Properties = schema } };
  161. // operation.RequestBody = new OpenApiRequestBody() { Content = content };
  162. //}
  163. }
  164. }
  165. public class HandlerErrorAttribute : ExceptionFilterAttribute
  166. {
  167. /// <summary>
  168. /// 控制器方法中出现异常,会调用该方法捕获异常
  169. /// </summary>
  170. /// <param name="context">提供使用</param>
  171. public override void OnException(HttpActionExecutedContext context)
  172. {
  173. base.OnException(context);
  174. Serilog.Log.Error(context.Exception, context.Exception.Message);
  175. //LogFile.WriteError(context.Exception.Message);
  176. throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.OK)
  177. {
  178. Content = new StringContent(
  179. JsonConvert.SerializeObject(
  180. new
  181. {
  182. code = -1,
  183. data = "",
  184. msg = context.Exception.Message
  185. }), Encoding.UTF8, "text/json")
  186. });
  187. }
  188. };
  189. public class SwaggerControllerDescProvider : ISwaggerProvider
  190. {
  191. private readonly ISwaggerProvider _swaggerProvider;
  192. private static ConcurrentDictionary<string, SwaggerDocument> _cache = new ConcurrentDictionary<string, SwaggerDocument>();
  193. private readonly string _xml;
  194. /// <summary>
  195. ///
  196. /// </summary>
  197. /// <param name="swaggerProvider"></param>
  198. /// <param name="xml">xml文档路径</param>
  199. public SwaggerControllerDescProvider(ISwaggerProvider swaggerProvider, string xml)
  200. {
  201. _swaggerProvider = swaggerProvider;
  202. _xml = xml;
  203. }
  204. public SwaggerDocument GetSwagger(string rootUrl, string apiVersion)
  205. {
  206. var cacheKey = string.Format("{0}_{1}", rootUrl, apiVersion);
  207. SwaggerDocument srcDoc = null;
  208. //只读取一次
  209. if (!_cache.TryGetValue(cacheKey, out srcDoc))
  210. {
  211. srcDoc = _swaggerProvider.GetSwagger(rootUrl, apiVersion);
  212. srcDoc.vendorExtensions = new Dictionary<string, object> { { "ControllerDesc", GetControllerDesc() } };
  213. _cache.TryAdd(cacheKey, srcDoc);
  214. }
  215. return srcDoc;
  216. }
  217. /// <summary>
  218. /// 从API文档中读取控制器描述
  219. /// </summary>
  220. /// <returns>所有控制器描述</returns>
  221. public ConcurrentDictionary<string, string> GetControllerDesc()
  222. {
  223. string xmlpath = _xml;
  224. ConcurrentDictionary<string, string> controllerDescDict = new ConcurrentDictionary<string, string>();
  225. if (File.Exists(xmlpath))
  226. {
  227. XmlDocument xmldoc = new XmlDocument();
  228. xmldoc.Load(xmlpath);
  229. string type = string.Empty, path = string.Empty, controllerName = string.Empty;
  230. string[] arrPath;
  231. int length = -1, cCount = "Controller".Length;
  232. XmlNode summaryNode = null;
  233. foreach (XmlNode node in xmldoc.SelectNodes("//member"))
  234. {
  235. type = node.Attributes["name"].Value;
  236. if (type.StartsWith("T:"))
  237. {
  238. //控制器
  239. arrPath = type.Split('.');
  240. length = arrPath.Length;
  241. controllerName = arrPath[length - 1];
  242. if (controllerName.EndsWith("Controller"))
  243. {
  244. //获取控制器注释
  245. summaryNode = node.SelectSingleNode("summary");
  246. string key = controllerName.Remove(controllerName.Length - cCount, cCount);
  247. if (summaryNode != null && !string.IsNullOrEmpty(summaryNode.InnerText) && !controllerDescDict.ContainsKey(key))
  248. {
  249. controllerDescDict.TryAdd(key, summaryNode.InnerText.Trim());
  250. }
  251. }
  252. }
  253. }
  254. }
  255. return controllerDescDict;
  256. }
  257. }
  258. }
  259. public class CustomModelValidator : ModelValidator
  260. {
  261. public CustomModelValidator(IEnumerable<ModelValidatorProvider> modelValidatorProviders) : base(modelValidatorProviders)
  262. {
  263. }
  264. public override IEnumerable<ModelValidationResult> Validate(ModelMetadata metadata, object container)
  265. {
  266. if (metadata.IsComplexType && metadata.Model == null)
  267. {
  268. return new List<ModelValidationResult> { new ModelValidationResult { MemberName = metadata.GetDisplayName(), Message = "请求参数对象不能为空。" } };
  269. }
  270. if (typeof(IValidatableObject).IsAssignableFrom(metadata.ModelType))
  271. {
  272. var validationResult = (metadata.Model as IValidatableObject).Validate(new ValidationContext(metadata.Model));
  273. if (validationResult != null)
  274. {
  275. var modelValidationResults = new List<ModelValidationResult>();
  276. foreach (var result in validationResult)
  277. {
  278. modelValidationResults.Add(new ModelValidationResult
  279. {
  280. MemberName = string.Join(",", result.MemberNames),
  281. Message = result.ErrorMessage
  282. });
  283. }
  284. return modelValidationResults;
  285. }
  286. return null;
  287. }
  288. return GetModelValidator(ValidatorProviders).Validate(metadata, container);
  289. }
  290. }
  291. }