You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
iot/labs/CSharpObjectJsonSchema/Controllers/ControllerExtensions.cs

208 lines
9.1 KiB

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Dynamic;
using System.Linq;
using System.Reflection;
namespace CSharpObjectJsonSchema.Controllers
{
public static class ControllerExtensions
{
public static object GetJsonSchema<T>(this ControllerBase controller)
{
if (controller is null)
{
throw new ArgumentNullException(nameof(controller));
}
var metadata = controller.HttpContext.RequestServices.GetRequiredService<IModelMetadataProvider>().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<IModelMetadataProvider>().GetMetadataForType(type);
return CreateJson(controller, metadata as DefaultModelMetadata);
}
public static object CreateJson(this ControllerBase controller, DefaultModelMetadata metadata)
{
if (metadata is null)
{
return null;
}
try
{
dynamic json = new ExpandoObject();
var dictionary = (IDictionary<string, object>)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;
}
foreach (var attribute in metadata.Attributes.Attributes)
{
if (attribute is DescriptionAttribute descriptionAttribute)
{//description
json.description = descriptionAttribute.Description;
}
else if (attribute is RegularExpressionAttribute regularExpressionAttribute)
{//pattern
json.pattern = regularExpressionAttribute.Pattern;
}
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;
}
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<string, object>)properties;
var requiredList = new List<string>();
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;
}
}
}
}