/// <summary> /// Create a resource listing /// </summary> /// <param name="actionContext">Current controller context</param> /// <param name="includeResourcePath">Should the resource path property be included in the response</param> /// <returns>A resrouce listing</returns> public static ResourceListing CreateResourceListing(HttpControllerContext controllerContext, bool includeResourcePath = false) { Uri uri = controllerContext.Request.RequestUri; var loadAssembly = System.Configuration.ConfigurationManager.AppSettings["swagger:APIAseembly"]; var version = Assembly.GetCallingAssembly().GetType().Assembly.GetName().Version.ToString(); if (!string.IsNullOrEmpty(loadAssembly)) { version = Assembly.Load(loadAssembly).GetName().Version.ToString(); } ResourceListing rl = new ResourceListing() { apiVersion = version, swaggerVersion = SWAGGER_VERSION, basePath = uri.GetLeftPart(UriPartial.Authority) + HttpRuntime.AppDomainAppVirtualPath.TrimEnd('/'), apis = new List <ResourceApi>() }; if (includeResourcePath) { rl.resourcePath = controllerContext.ControllerDescriptor.ControllerName; } return(rl); }
private static ResourceApiModel AddModelApiResponse(ResourceListing r, string targetTypeName) { if (!r.models.ContainsKey(targetTypeName)) { var model = new ResourceApiModel(); model.id = targetTypeName; model.properties = new Dictionary <string, ResourceApiModelProperty>(); var p = new ResourceApiModelProperty(); p.type = "bool"; p.description = "Indicates whether the API call is success or not"; model.properties.Add("Success", p); p = new ResourceApiModelProperty(); p.type = "string"; p.description = "Additional message for API result"; model.properties.Add("Message", p); p = new ResourceApiModelProperty(); p.type = "Exception"; p.description = "In case catch an exception"; model.properties.Add("Exception", p); r.models.TryAdd(targetTypeName, model); return(model); } else { return(null); } }
private static void AddModelDataObject(ResourceListing r, Type type, List <Type> queue) { if (!r.models.ContainsKey(type.Name)) { var model = new ResourceApiModel(); model.id = type.Name; model.properties = new Dictionary <string, ResourceApiModelProperty>(); foreach (var pi in type.GetProperties()) { var ignore = pi.GetCustomAttribute <System.Runtime.Serialization.IgnoreDataMemberAttribute>(); if (ignore != null) { continue; } var p = new ResourceApiModelProperty(); switch (pi.PropertyType.Name) { case "List`1": var dataObjType = pi.PropertyType.GetGenericArguments()[0]; AddTypeToProcessQueue(r, dataObjType, queue); p.type = "Array"; var items = new Dictionary <string, string>(); items.Add("$ref", dataObjType.Name); p.items = items; break; case "Nullable`1": var dd = pi.PropertyType.GetGenericArguments()[0]; p.type = dd.Name; AddTypeToProcessQueue(r, dd, queue); break; case "Dictionary`2": p.type = string.Format("Array[{0},{1}]" , pi.PropertyType.GetGenericArguments()[0].Name , pi.PropertyType.GetGenericArguments()[1].Name); break; default: p.type = pi.PropertyType.Name; AddTypeToProcessQueue(r, pi.PropertyType, queue); break; } //get description from xml p.description = GetDocumentation(pi); if (string.IsNullOrWhiteSpace(p.description)) { p.description = NODESCRIPTION; } if (!model.properties.ContainsKey(pi.Name)) { model.properties.Add(pi.Name, p); } //check if this is a nested data obj type, put into queue if yes } r.models.TryAdd(type.Name, model); } }
/// <summary> /// Get the resource description of the api for swagger documentation /// </summary> /// <remarks>It is very convenient to have this information available for generating clients. This is the entry point for the swagger UI /// </remarks> /// <returns>JSON document representing structure of API</returns> public HttpResponseMessage Get(int type = 1) { var docProvider = (XmlCommentDocumentationProvider)GlobalConfiguration.Configuration.Services.GetDocumentationProvider(); ResourceListing r = SwaggerGen.CreateResourceListing(ControllerContext); List <string> uniqueControllers = new List <string>(); foreach (var api in GlobalConfiguration.Configuration.Services.GetApiExplorer().ApiDescriptions) { string controllerName = api.ActionDescriptor.ControllerDescriptor.ControllerName; if (uniqueControllers.Contains(controllerName) || controllerName.ToUpper().Equals(SwaggerGen.SWAGGER.ToUpper()) || controllerName.ToLower().StartsWith("abp") ) { continue; } uniqueControllers.Add(controllerName); //添加API ResourceApi rApi = SwaggerGen.CreateResourceApi(api); r.apis.Add(rApi); } HttpResponseMessage resp = new HttpResponseMessage(); resp.Content = new ObjectContent <ResourceListing>(r, ControllerContext.Configuration.Formatters.JsonFormatter); return(resp); }
private ResourceListing getDocs(HttpActionContext actionContext) { var assemblyType = (actionContext.ActionDescriptor as ReflectedHttpActionDescriptor).MethodInfo.DeclaringType; var docProvider = new XmlCommentDocumentationProvider(); //(XmlCommentDocumentationProvider)GlobalConfiguration.Configuration.Services.GetDocumentationProvider(); ResourceListing r = SwaggerGen.CreateResourceListing(actionContext); foreach (var api in GlobalConfiguration.Configuration.Services.GetApiExplorer().ApiDescriptions) { if (api.ActionDescriptor.ActionName.EndsWith("API"))//Ignore each Default API action { continue; } string apiControllerName = api.ActionDescriptor.ControllerDescriptor.ControllerName; if (api.Route.Defaults.ContainsKey(SwaggerGen.SWAGGER) || apiControllerName.ToUpper().Equals(SwaggerGen.SWAGGER.ToUpper())) { continue; } // Make sure we only report the current controller docs if (!apiControllerName.Equals(actionContext.ControllerContext.ControllerDescriptor.ControllerName)) { continue; } ResourceApi rApi = SwaggerGen.CreateResourceApi(api); r.apis.Add(rApi); ResourceApiOperation rApiOperation = SwaggerGen.CreateResourceApiOperation(r, api, docProvider); rApi.operations.Add(rApiOperation); foreach (var param in api.ParameterDescriptions) { ResourceApiOperationParameter parameter = SwaggerGen.CreateResourceApiOperationParameter(r, api, param, docProvider); rApiOperation.parameters.Add(parameter); } if (System.Configuration.ConfigurationManager.AppSettings["swagger:APITOKEN"] != null && System.Configuration.ConfigurationManager.AppSettings["swagger:APITOKEN"].Equals("true") && !api.ActionDescriptor.ActionName.EndsWith("API")) { //添加Token ResourceApiOperationParameter p = new ResourceApiOperationParameter(); p.name = "ApiToken"; p.description = "Api Token"; p.paramType = "path"; p.required = true; p.dataType = "String"; rApiOperation.parameters.Insert(0, p); } SwaggerGen.CreateModel(r, api, docProvider); //r.models = new ResourceApiModel(); } return(r); }
private static void AddTypeToProcessQueue(ResourceListing r, Type type, List <Type> queue) { if (!queue.Contains(type) && //already in queue !r.models.ContainsKey(type.Name) &&//already handled !IsExceptType(type)) //only process Data Object { queue.Add(type); } }
/// <summary> /// Creates an api operation /// </summary> /// <param name="api">Description of the api via the ApiExplorer</param> /// <param name="docProvider">Access to the XML docs written in code</param> /// <returns>An api operation</returns> public static ResourceApiOperation CreateResourceApiOperation(ResourceListing r, ApiDescription api, XmlCommentDocumentationProvider docProvider) { ResourceApiOperation rApiOperation = new ResourceApiOperation() { httpMethod = api.HttpMethod.ToString(), nickname = docProvider.GetNickname(api.ActionDescriptor), responseClass = docProvider.GetResponseClass(api.ActionDescriptor), summary = docProvider.GetDocumentation(api.ActionDescriptor), notes = docProvider.GetNotes(api.ActionDescriptor), parameters = new List <ResourceApiOperationParameter>(), }; if (string.IsNullOrEmpty(rApiOperation.notes) || rApiOperation.notes.Equals("No Documentation Found.")) { rApiOperation.notes = rApiOperation.summary; } return(rApiOperation); }
/// <summary> /// Creates an operation parameter /// </summary> /// <param name="api">Description of the api via the ApiExplorer</param> /// <param name="param">Description of a parameter on an operation via the ApiExplorer</param> /// <param name="docProvider">Access to the XML docs written in code</param> /// <returns>An operation parameter</returns> public static ResourceApiOperationParameter CreateResourceApiOperationParameter(ResourceListing r, ApiDescription api, ApiParameterDescription param, XmlCommentDocumentationProvider docProvider) { string paramType = (param.Source.ToString().Equals(FROMURI)) ? QUERY : BODY; var dataType = param.ParameterDescriptor.ParameterType.Name; switch (dataType) { case "List`1": var dataObjType = param.ParameterDescriptor.ParameterType.GetGenericArguments()[0]; dataType = string.Format("Array[{0}]", dataObjType.Name); break; case "Nullable`1": var dd = param.ParameterDescriptor.ParameterType.GetGenericArguments()[0]; dataType = dd.Name; break; case "Dictionary`2": dataType = string.Format("Array[{0},{1}]", param.ParameterDescriptor.ParameterType.GetGenericArguments()[0].Name, param.ParameterDescriptor.ParameterType.GetGenericArguments()[1].Name); break; default: if (!IsExceptType(param.ParameterDescriptor.ParameterType)) { AddModelDataObject(r, param.ParameterDescriptor.ParameterType); } break; } ResourceApiOperationParameter parameter = new ResourceApiOperationParameter() { paramType = (paramType == "query" && api.RelativePath.IndexOf("{" + param.Name + "}") > -1) ? PATH : paramType, name = param.Name, description = param.Name.Equals("sessionKey") ? "Login session" : (string.IsNullOrWhiteSpace(param.Documentation) ? NODESCRIPTION : param.Documentation), dataType = dataType, required = docProvider.GetRequired(param.ParameterDescriptor) }; return(parameter); }
public static void CreateModel(ResourceListing r, ApiDescription api, XmlCommentDocumentationProvider docProvider) { if (r.models == null) { r.models = new ConcurrentDictionary <string, object>(); } ReflectedHttpActionDescriptor reflectedActionDescriptor = api.ActionDescriptor as ReflectedHttpActionDescriptor; if (reflectedActionDescriptor != null) { //this function will be called generating swagger API json //limitations //-only generating swagger models (API return type documentation) for return type is ApiResponse //--most calls fall into these 3 categories: //--ApiResponse<PageData<T>> //--ApiResponse<T> //--ApiResponse<T> where T is simple type such as object, string, int, etc. //-only generate detail when T is DataObject //implementation notes //-get the actual T //--when T is DataObject, add into a queue (i.e. a FIFO Stack) for next step processing //-loop until queue is empty //--process (add model details) for the first item in queue, remove it after processed //--during the processing, if found any property is type of DataObject, add the nested type into queue //-the reason to use a while loop with a queue is to avoid using recursive implementation which will throw a stack-overflow exception when it's too many nested types var returnType = reflectedActionDescriptor.MethodInfo.ReturnType; List <Type> queue = new List <Type>();//process queue AddTypeToProcessQueue(r, returnType, queue); //parameter handling var parameters = reflectedActionDescriptor.MethodInfo.GetParameters(); if (parameters != null && parameters.Length > 0) { foreach (var p in parameters) { if (p.ParameterType.IsGenericType) { var dataObjType = p.ParameterType.GetGenericArguments()[0]; AddTypeToProcessQueue(r, dataObjType, queue); } else { AddTypeToProcessQueue(r, p.ParameterType, queue); } } } if (returnType.IsGenericType) { Type[] types = returnType.GetGenericArguments(); if (types[0].Name.Equals("PageData`1")) { var dataObjType = types[0].GetGenericArguments()[0]; AddTypeToProcessQueue(r, dataObjType, queue); //ApiResponse_PageData_xxxxx var model = AddModelApiResponse(r, "ApiResponse_PageData_" + dataObjType.Name); if (model != null) { var p = new ResourceApiModelProperty(); p.type = "Array"; p.description = "Page data set"; var items = new Dictionary <string, string>(); items.Add("$ref", "PageData_" + dataObjType.Name); p.items = items; model.properties.Add("Results", p); } if (!r.models.ContainsKey("PageData_" + dataObjType.Name)) { model = new ResourceApiModel(); model.id = "PageData_" + dataObjType.Name; model.properties = new Dictionary <string, ResourceApiModelProperty>(); var p = new ResourceApiModelProperty(); p.type = "int"; p.description = "total no of qualified data"; model.properties.Add("TotalRecords", p); p = new ResourceApiModelProperty(); p.type = "bool"; p.description = "whether has next page"; model.properties.Add("HasNextPage", p); p = new ResourceApiModelProperty(); p.type = "int"; p.description = "curent page index, start from 0"; model.properties.Add("PageIndex", p); p = new ResourceApiModelProperty(); p.type = "int"; p.description = "how many records in one page"; model.properties.Add("PageSize", p); p = new ResourceApiModelProperty(); p.type = "Array"; p.description = "A page of data"; var items = new Dictionary <string, string>(); items.Add("$ref", dataObjType.Name); p.items = items; model.properties.Add("Results", p); r.models.TryAdd("PageData_" + dataObjType.Name, model); } } else if (returnType.Name.Equals("Page`1")) { var dataObjType = types[0]; AddTypeToProcessQueue(r, dataObjType, queue); var paging = returnType.GetProperty("Paging"); if (paging != null) {//add paging type manually AddTypeToProcessQueue(r, paging.PropertyType, queue); } //Page_xxxxx if (!r.models.ContainsKey("Page_" + dataObjType.Name)) { var model = new ResourceApiModel(); model.id = "Page_" + dataObjType.Name; model.properties = new Dictionary <string, ResourceApiModelProperty>(); var p = new ResourceApiModelProperty(); p.type = "Paging"; p.description = "Paging info"; model.properties.Add("Paging", p); p = new ResourceApiModelProperty(); p.type = "Array"; p.description = "A page of data"; var items = new Dictionary <string, string>(); items.Add("$ref", dataObjType.Name); p.items = items; model.properties.Add("Records", p); r.models.TryAdd("Page_" + dataObjType.Name, model); } } else { var dataObjType = types[0]; AddTypeToProcessQueue(r, dataObjType, queue); } } //process queue while (queue.Count > 0) { var type = queue[0]; AddModelDataObject(r, queue[0], queue); queue.Remove(type); } } }