using Infrastructure.Resources; 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) { 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; if (modelType == typeof(string)) { json.type = "string"; } else if (modelType == typeof(int) || modelType == typeof(long)) { json.type = "integer"; } else if (modelType == typeof(float) || modelType == typeof(double) || modelType == typeof(decimal)) { json.type = "number"; } else if (modelType == typeof(bool)) { json.type = "boolean"; } else if (modelType == typeof(DateTime)) { json.type = "string"; } else { json.type = "object"; } } //title if (metadata.DisplayName != null) { json.title = metadata.DisplayName; } //if (metadata.IsRequired) //{ // if (metadata.ModelType != typeof(bool)) // { // json.required = true; // } //} foreach (var attribute in metadata.Attributes.Attributes) { if (attribute is DescriptionAttribute descriptionAttribute) {//description json.description = descriptionAttribute.Description; } else if (attribute is RequiredAttribute requiredAttribute) { var localizedString = localizer.GetString(nameof(RequiredAttribute)); var message = requiredAttribute.ErrorMessage; if (!localizedString.ResourceNotFound) { message = string.Format(localizedString.Value, metadata.DisplayName); } json.required = new { message = localizedString.ResourceNotFound ? requiredAttribute.FormatErrorMessage(metadata.DisplayName) : string.Format(localizedString.Value, metadata.DisplayName) }; } else if (attribute is RegularExpressionAttribute regularExpressionAttribute) {//pattern json.pattern = new { regex = regularExpressionAttribute.Pattern, message = regularExpressionAttribute.ErrorMessage }; } else if (attribute is DataTypeAttribute dataTypeAttribute) {//format if (dataTypeAttribute.DataType == DataType.DateTime) { json.format = "date-time"; } else if (dataTypeAttribute.DataType == DataType.Time) { json.format = "time"; } else if (dataTypeAttribute.DataType == DataType.EmailAddress) { json.format = "email"; } else if (dataTypeAttribute.DataType == DataType.Url) { json.format = "uri"; } 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 = minLengthAttribute.Length; } else if (attribute is MaxLengthAttribute maxLengthAttribute) {//maxLength json.maxLength = maxLengthAttribute.Length; } else if (attribute is StringLengthAttribute stringLengthAttribute) {//minLength,maxLength json.minLength = stringLengthAttribute.MinimumLength; json.maxLength = stringLengthAttribute.MaximumLength; } else if (attribute is RangeAttribute rangeAttribute) {//minimum,maximum json.minimum = rangeAttribute.Minimum; json.maximum = rangeAttribute.Maximum; } 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(); } else if (attribute is CompareAttribute compareAttribute) {//compare 自定义 json.compare = compareAttribute.OtherProperty; } 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, //remoteAttribute. }; } } dynamic properties = new ExpandoObject(); var propertiesDictionary = (IDictionary)properties; //var requiredList = new List(); 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); //if (modelMetadataItem.IsRequired) //{ // requiredList.Add(item.PropertyName); //} } json.properties = properties; // if (requiredList.Any()) // { // json.required = requiredList; // } } return json; } catch (Exception ex) { return null; } } } }