using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata; using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Localization; using System; using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.Dynamic; using System.Linq; using System.Reflection; namespace Infrastructure.Extensions { public static class ControllerExtensions { public static ModelMetadata GetModelMetadata(this ControllerBase controller) { if (controller is null) { throw new ArgumentNullException(nameof(controller)); } return controller.HttpContext.RequestServices.GetRequiredService().GetMetadataForType(typeof(T)); } public static object GetJsonSchema(this ControllerBase controller) { if (controller is null) { throw new ArgumentNullException(nameof(controller)); } var metadata = controller.HttpContext.RequestServices.GetRequiredService().GetMetadataForType(typeof(T)); return CreateJson(controller, metadata as DefaultModelMetadata); } public static object GetJsonSchema(this ControllerBase controller, Type type) { if (controller is null) { throw new ArgumentNullException(nameof(controller)); } var metadata = controller.HttpContext.RequestServices.GetRequiredService().GetMetadataForType(type); return CreateJson(controller, metadata as DefaultModelMetadata); } public static SelectList GetSelectList(this Controller controller, object selectedValue) { var values = from Enum e in Enum.GetValues(typeof(T)) select new { Id = e, Name = e.GetDisplayName() }; return new SelectList(values, "Id", "Name", selectedValue); } public static object CreateJson(this ControllerBase controller, DefaultModelMetadata metadata, DefaultModelMetadata parent = null) { if (metadata is null) { return null; } try { var factory = controller.HttpContext.RequestServices.GetService(); var localizer = factory.Create("Resources.Resource", Assembly.GetEntryAssembly().GetName().Name); dynamic json = new ExpandoObject(); var dictionary = (IDictionary)json; //type if (metadata.IsComplexType && metadata.IsCollectionType) { json.type = "array"; json.items = controller.GetJsonSchema(metadata.ModelType.GenericTypeArguments[0]); } else if (metadata.IsEnum) { json.type = "string"; dictionary["enum"] = metadata.IsNullableValueType ? Enum.GetNames(metadata.ModelType.GenericTypeArguments[0]) : Enum.GetNames(metadata.ModelType); } else { var modelType = metadata.IsNullableValueType ? metadata.ModelType.GenericTypeArguments[0] : metadata.ModelType; var underlyingType = Nullable.GetUnderlyingType(modelType); if (underlyingType != null) { json.nullable = true; modelType = underlyingType; } if (modelType == typeof(string)) { json.type = "string"; } else if (modelType == typeof(int) || modelType == typeof(long)) { json.type = "integer"; json.ui = "string"; json.digits = new { message = "整数格式错误" }; } else if (modelType == typeof(float) || modelType == typeof(double) || modelType == typeof(decimal)) { json.type = "number"; json.ui = "string"; json.number = new { message = "数值格式错误" }; } else if (modelType == typeof(bool)) { json.type = "boolean"; } else if (modelType == typeof(Guid)) { json.type = "guid"; } else if (modelType == typeof(DateTime)) { json.type = "string"; } else { json.type = "object"; } } //title if (metadata.DisplayName != null) { json.title = metadata.DisplayName; } foreach (var attribute in metadata.Attributes.Attributes) { if (attribute is DescriptionAttribute descriptionAttribute) {//description json.description = descriptionAttribute.Description; } else if (attribute is RequiredAttribute requiredAttribute) { json.required = new { message = requiredAttribute.GetErrorMessage(localizer, metadata.DisplayName) }; } else if (attribute is RegularExpressionAttribute regularExpressionAttribute) {//pattern json.pattern = new { regex = regularExpressionAttribute.Pattern, message = regularExpressionAttribute.GetErrorMessage(localizer, metadata.DisplayName, regularExpressionAttribute.Pattern) }; } else if (attribute is DataTypeAttribute dataTypeAttribute) {//format if (dataTypeAttribute.DataType == DataType.EmailAddress) { json.format = "email"; json.ui = "string"; json.email = new { message = "email格式错误" }; } else if (dataTypeAttribute.DataType == DataType.Url) { json.format = "url"; json.ui = "string"; json.url = new { message = "url格式错误" }; } else if (dataTypeAttribute.DataType == DataType.DateTime) { json.format = "date"; json.ui = "string"; json.date = new { message = "日期格式错误" }; } else if (dataTypeAttribute.DataType == DataType.CreditCard) { json.format = "creditcard"; json.ui = "string"; json.url = new { message = "信用卡格式错误" }; } else if (dataTypeAttribute.DataType == DataType.Custom) {//自定义 json.format = dataTypeAttribute.GetDataTypeName().ToLower(); } else { json.format = dataTypeAttribute.DataType.ToString().ToLower(); } } else if (attribute is ReadOnlyAttribute readOnlyAttribute) {//readOnly json.readOnly = readOnlyAttribute.IsReadOnly; } else if (attribute is MinLengthAttribute minLengthAttribute) {//minLength json.minLength = new { min = minLengthAttribute.Length, message = minLengthAttribute.GetErrorMessage(localizer, metadata.DisplayName, minLengthAttribute.Length.ToString()) }; } else if (attribute is MaxLengthAttribute maxLengthAttribute) {//maxLength json.maxLength = new { max = maxLengthAttribute.Length, message = maxLengthAttribute.GetErrorMessage(localizer, metadata.DisplayName, maxLengthAttribute.Length.ToString()) }; } else if (attribute is StringLengthAttribute stringLengthAttribute) {//minLength,maxLength json.rangeLength = new { min = stringLengthAttribute.MinimumLength, max = stringLengthAttribute.MaximumLength, message = stringLengthAttribute.GetErrorMessage(localizer, metadata.DisplayName, stringLengthAttribute.MinimumLength.ToString(), stringLengthAttribute.MaximumLength.ToString()) }; } else if (attribute is RangeAttribute rangeAttribute) {//minimum,maximum json.range = new { min = rangeAttribute.Minimum, max = rangeAttribute.Maximum, message = rangeAttribute.GetErrorMessage(localizer, metadata.DisplayName, rangeAttribute.Minimum.ToString(), rangeAttribute.Maximum.ToString()) }; } else if (attribute is CompareAttribute compareAttribute) {//compare 自定义 var compareName = parent.Properties.FirstOrDefault(o => o.PropertyName == compareAttribute.OtherProperty)?.DisplayName; json.equalTo = new { other = compareAttribute.OtherProperty, message = compareAttribute.GetErrorMessage(localizer, metadata.DisplayName, compareName) }; } else if (attribute is RemoteAttribute remoteAttribute) {//remote 自定义 var routeData = attribute.GetType().GetProperty("RouteData", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(remoteAttribute) as RouteValueDictionary; json.remote = new { url = controller.Url.Action(routeData["action"].ToString(), routeData["controller"].ToString(), new { Area = routeData["area"].ToString() }), remoteAttribute.AdditionalFields, message = remoteAttribute.GetErrorMessage(localizer, metadata.DisplayName) }; } else if (attribute is ScaffoldColumnAttribute scaffoldColumnAttribute) {//scaffold 自定义 json.scaffold = scaffoldColumnAttribute.Scaffold; } else if (attribute is HiddenInputAttribute hiddenInputAttribute) {//hidden 自定义 json.hidden = hiddenInputAttribute.DisplayValue; } else if (attribute is UIHintAttribute uIHintAttribute) {//ui 自定义 json.ui = uIHintAttribute.UIHint.ToLower(); } } dynamic properties = new ExpandoObject(); var propertiesDictionary = (IDictionary)properties; if (metadata.IsComplexType && !metadata.IsCollectionType && metadata.Properties.Any()) { foreach (var item in metadata.Properties) { var modelMetadataItem = item as DefaultModelMetadata; propertiesDictionary[item.PropertyName] = CreateJson(controller, modelMetadataItem, metadata); } json.properties = properties; } return json; } catch (Exception ex) { ex.PrintStack(); return null; } } public static string GetErrorMessage(this ValidationAttribute attribute, IStringLocalizer localizer, params string[] args) { var localizedString = localizer.GetString(attribute.GetType().Name); //return localizedString.ResourceNotFound ? attribute.FormatErrorMessage(args[0]) : string.Format(localizedString.Value, args); var format = attribute.ErrorMessage; if (localizedString.ResourceNotFound) { if (string.IsNullOrEmpty(format)) { format = attribute.GetType().GetProperty("ErrorMessageString", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(attribute) as string; } } else { format = localizedString.Value; } try { return string.Format(format, args); } catch (Exception ex) { ex.PrintStack(); } return format; } } }