using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Reflection; using System.Runtime.Remoting.Contexts; using System.Text; using System.Threading; using System.Web.Http; using System.Web.Http.Description; using System.Web.Http.Filters; using System.Web.Http.Metadata; using System.Web.Http.Routing; using System.Web.Http.Validation; using System.Web.Http.Validation.Providers; using System.Xml; using System.Xml.Linq; using Microsoft.Owin; using Microsoft.Owin.Cors; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using Owin; using Swashbuckle.Application; using Swashbuckle.Swagger; using XdCxRhDW.App.WebAPI; [assembly: OwinStartup(typeof(XdCxRhDW.App.WebAPI.Startup))] namespace XdCxRhDW.App.WebAPI { class Startup { public void Configuration(IAppBuilder app) { HttpConfiguration config = new HttpConfiguration(); IEnumerable modelValidatorProviders = config.Services.GetModelValidatorProviders(); DataAnnotationsModelValidatorProvider provider = (DataAnnotationsModelValidatorProvider) modelValidatorProviders.Single(x => x is DataAnnotationsModelValidatorProvider); provider.RegisterDefaultValidatableObjectAdapter(typeof(CustomModelValidator)); JsonSerializerSettings setting = new JsonSerializerSettings() { //日期类型默认格式化处理 DateFormatHandling = DateFormatHandling.MicrosoftDateFormat, DateFormatString = "yyyy-MM-dd HH:mm:ss", //驼峰样式 //ContractResolver = new CamelCasePropertyNamesContractResolver(), //空值处理 //NullValueHandling = NullValueHandling.Ignore, //设置序列化的最大层数 MaxDepth = 10, //解决json序列化时的循环引用问题 ReferenceLoopHandling = ReferenceLoopHandling.Ignore }; config.Formatters.JsonFormatter.SerializerSettings = setting; config.Formatters.Remove(config.Formatters.XmlFormatter); config.Filters.Add(new HandlerErrorAttribute()); //config.Routes.MapHttpRoute("DefaultApiWithId", "Api/{controller}/{id}", new { id = RouteParameter.Optional }, new { id = @"\d+" }); config.Routes.MapHttpRoute("DefaultApiWithAction", "Api/{controller}/{action}"); //config.Routes.MapHttpRoute("DefaultApiGet", "Api/{controller}", new { action = "Get" }, new { httpMethod = new HttpMethodConstraint(HttpMethod.Get) }); //config.Routes.MapHttpRoute("DefaultApiPost", "Api/{controller}", new { action = "Post" }, new { httpMethod = new HttpMethodConstraint(HttpMethod.Post) }); ConfigureSwagger(config); //添加路由路径 config.MapHttpAttributeRoutes(); app.UseCors(CorsOptions.AllowAll); app.UseWebApi(config); } private static void ConfigureSwagger(HttpConfiguration config) { var thisAssembly = typeof(Startup).Assembly; config.EnableSwagger(c => { c.IgnoreObsoleteActions();//忽略过时的方法 c.IgnoreObsoleteProperties();//忽略过时的属性 c.PrettyPrint();//漂亮缩进 c.SingleApiVersion("v1", "多模式融合定位平台Http接口"); c.ApiKey("123456"); //设置接口描述xml路径地址 var webApiXmlPath1 = $"{AppDomain.CurrentDomain.BaseDirectory}{Path.GetFileNameWithoutExtension(System.AppDomain.CurrentDomain.FriendlyName)}.xml"; c.IncludeXmlComments(webApiXmlPath1); var webApiXmlPath2 = $"{AppDomain.CurrentDomain.BaseDirectory}XdCxRhDw.Dto.xml"; c.IncludeXmlComments(webApiXmlPath2); //c.UseFullTypeNameInSchemaIds();//使用完整类型名称 //加入控制器描述 c.CustomProvider((defaultProvider) => new SwaggerControllerDescProvider(defaultProvider, webApiXmlPath1)); c.OperationFilter(); c.SchemaFilter(); }) .EnableSwaggerUi(c => { c.InjectJavaScript(thisAssembly, "XdCxRhDW.App.WebAPI.Swagger.js"); c.DocumentTitle("多模式融合定位平台Http接口"); }); } /// /// Swagger文件上传特性标注 /// [AttributeUsage(AttributeTargets.Method)] public sealed class SwaggerFormAttribute : Attribute { public SwaggerFormAttribute() { this.Name = "文件"; this.Description = "选择文件"; } /// /// Swagger特性标注 /// /// /// public SwaggerFormAttribute(string name, string description) { Name = name; Description = description; } /// /// 名称 /// public string Name { get; private set; } /// /// 描述 /// public string Description { get; private set; } } public class FileUploadOperation : IOperationFilter { public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription) { if (operation.parameters == null) { operation.parameters = new List(); } var requestAttributes = apiDescription.GetControllerAndActionAttributes(); foreach (var attr in requestAttributes) { operation.parameters.Add(new Parameter { description = attr.Description, name = attr.Name, @in = "formData", required = true, type = "file", }); operation.consumes.Add("multipart/form-data"); } //if (operation.operationId.ToLower() == "apivaluesuploadpost") //{ // operation.parameters.Clear(); // operation.parameters.Add(new Parameter // { // name = "uploadedFile", // @in = "formData", // description = "Upload File", // required = true, // type = "file" // }); // operation.consumes.Add("multipart/form-data"); //} //判断上传文件的类型,只有上传的类型是IFormCollection的才进行重写。 //var paras = apiDescription.ActionDescriptor.GetParameters(); //if (paras.Any(w => w.ParameterType == typeof(IFormCollection))) //{ // Dictionary schema = new Dictionary(); // schema["fileName"] = new OpenApiSchema { Description = "Select file", Type = "string", Format = "binary" }; // Dictionary content = new Dictionary(); // content["multipart/form-data"] = new OpenApiMediaType { Schema = new OpenApiSchema { Type = "object", Properties = schema } }; // operation.RequestBody = new OpenApiRequestBody() { Content = content }; //} } } public class HandlerErrorAttribute : ExceptionFilterAttribute { /// /// 控制器方法中出现异常,会调用该方法捕获异常 /// /// 提供使用 public override void OnException(HttpActionExecutedContext context) { base.OnException(context); Serilog.Log.Error(context.Exception, context.Exception.Message); //LogFile.WriteError(context.Exception.Message); throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent( JsonConvert.SerializeObject( new { code = -1, data = "", msg = context.Exception.Message }), Encoding.UTF8, "text/json") }); } }; public class SwaggerControllerDescProvider : ISwaggerProvider { private readonly ISwaggerProvider _swaggerProvider; private static ConcurrentDictionary _cache = new ConcurrentDictionary(); private readonly string _xml; /// /// /// /// /// xml文档路径 public SwaggerControllerDescProvider(ISwaggerProvider swaggerProvider, string xml) { _swaggerProvider = swaggerProvider; _xml = xml; } public SwaggerDocument GetSwagger(string rootUrl, string apiVersion) { var cacheKey = string.Format("{0}_{1}", rootUrl, apiVersion); SwaggerDocument srcDoc = null; //只读取一次 if (!_cache.TryGetValue(cacheKey, out srcDoc)) { srcDoc = _swaggerProvider.GetSwagger(rootUrl, apiVersion); srcDoc.vendorExtensions = new Dictionary { { "ControllerDesc", GetControllerDesc() } }; _cache.TryAdd(cacheKey, srcDoc); } return srcDoc; } /// /// 从API文档中读取控制器描述 /// /// 所有控制器描述 public ConcurrentDictionary GetControllerDesc() { string xmlpath = _xml; ConcurrentDictionary controllerDescDict = new ConcurrentDictionary(); if (File.Exists(xmlpath)) { XmlDocument xmldoc = new XmlDocument(); xmldoc.Load(xmlpath); string type = string.Empty, path = string.Empty, controllerName = string.Empty; string[] arrPath; int length = -1, cCount = "Controller".Length; XmlNode summaryNode = null; foreach (XmlNode node in xmldoc.SelectNodes("//member")) { type = node.Attributes["name"].Value; if (type.StartsWith("T:")) { //控制器 arrPath = type.Split('.'); length = arrPath.Length; controllerName = arrPath[length - 1]; if (controllerName.EndsWith("Controller")) { //获取控制器注释 summaryNode = node.SelectSingleNode("summary"); string key = controllerName.Remove(controllerName.Length - cCount, cCount); if (summaryNode != null && !string.IsNullOrEmpty(summaryNode.InnerText) && !controllerDescDict.ContainsKey(key)) { controllerDescDict.TryAdd(key, summaryNode.InnerText.Trim()); } } } } } return controllerDescDict; } } private sealed class ApiComparer : IComparer { public int Compare(string x, string y) { return x.CompareTo(y); } } } public class SwaggerEnumFilter : ISchemaFilter { public void Apply(Schema schema, SchemaRegistry schemaRegistry, Type type) { UpdateSchemaDescription(schema, type); } private void UpdateSchemaDescription(Schema schema, Type type) { if (type.IsEnum)//枚举直接应用在controller接口中 { var items = GetEnumInfo(type); if (items.Length > 0) { var description = GetEnumInfo(type); schema.description = string.IsNullOrEmpty(schema.description) ? description : $"{schema.description}:{description}"; } } else if (type.IsClass && type != typeof(string))//枚举在类的属性中 { if (schema.properties == null) return; var props = type.GetProperties(); foreach (var prop in props) { var propScheama = schema.properties[prop.Name]; if (prop.PropertyType.IsClass && prop.PropertyType != typeof(string)) { UpdateSchemaDescription(propScheama, prop.PropertyType); } else { if (prop.PropertyType.IsEnum) { var description = GetEnumInfo(prop.PropertyType); propScheama.description = string.IsNullOrWhiteSpace(propScheama.description) ? description : $"{propScheama.description}:{description}"; propScheama.@enum = null; } } } } } /// /// 获取枚举值+描述 /// /// /// private string GetEnumInfo(Type enumType) { var fields = enumType.GetFields(); List list = new List(); foreach (var field in fields) { if (!field.FieldType.IsEnum) continue; string description = null; if (description == null)//取DescriptionAttribute的值 { var descriptionAttr = field.GetCustomAttribute(); if (descriptionAttr != null && !string.IsNullOrWhiteSpace(descriptionAttr.Description)) { description = descriptionAttr.Description; } } if (description == null)//取DisplayAttribute的值 { var dispalyAttr = field.GetCustomAttribute(); if (dispalyAttr != null && !string.IsNullOrWhiteSpace(dispalyAttr.Name)) { description = dispalyAttr.Name; } } if (description == null)//取DisplayNameAttribute的值 { var dispalyNameAttr = field.GetCustomAttribute(); if (dispalyNameAttr != null && !string.IsNullOrWhiteSpace(dispalyNameAttr.DisplayName)) { description = dispalyNameAttr.DisplayName; } } if (description == null)//取字段名 { description = field.Name; } var value = field.GetValue(null); list.Add($"{description}={(int)value}"); } return string.Join(",", list); } } public class CustomModelValidator : ModelValidator { public CustomModelValidator(IEnumerable modelValidatorProviders) : base(modelValidatorProviders) { } public override IEnumerable Validate(ModelMetadata metadata, object container) { if (metadata.IsComplexType && metadata.Model == null) { return new List { 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(); 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); } } }