1、打开VS2019,选择新建项目,选择【ASP.NET Web 应用程序(.NET Framework)】类型。

2、点击下一步,输入api项目名称,选择.NET Framework 4.5。

3、点击创建,选择WebAPI,https看情况,有需要可以勾选上,开发阶段影响不大。

4、点击创建,我们得到一个默认结构的WebApi项目。

其中有MVC的结构,不是我们需要的,比如fonts文件夹这些,删除一下。

编译下,发现报错,是因为我们删除了APP_Start文件夹下的一个cs文件,需要把Global.asax里面最后一句去掉。
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
//BundleConfig.RegisterBundles(BundleTable.Bundles);
}现在编译成功,得到最简洁的一个webapi站点。
5、项目上单击右键,选择【管理NuGet程序包】。搜索【Swashbuckle】,本项目安装的Swashbuckle 5.6

6、此时我们发现APP_Start中多了一个SwagerConfig.cs,此时编译并F5运行一下。
访问的是:https://localhost:44333/

无资源的原因是我们地址不对,正确的应该是:https://localhost:44333/swagger
重新回车一下,看到下面这个界面,说明没问题。

观察里面的接口内容,是控制器文件夹下ValuesController.cs这个里面的内容。
接口思路是一个控制器只干一件事,并且提供不同的协议类型的接口。
思路很好,但是不是我想要的,我要的是一个控制器里面多个接口,接口协议自定义。
7、修改一下api路由,打开APP_Start文件夹下WebApiConfig.cs文件。修改如下
public static class WebApiConfig{
public static void Register(HttpConfiguration config)
{
// Web API 配置和服务
// Web API 路由
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",//修改了这里
defaults: new { id = RouteParameter.Optional }
);
}
}再次编译刷新刚刚的页面。

现在接口路径中,已经多了Action名称,我们可以在控制器下开多个Action,并对外开放。
8、接下来可以创建一个新的控制器【LoginController.cs】,Controller文件夹名称上,单击右键-->选择添加-->控制器-->选择WebApi2控制器 空的-->确认-->输入LoginController名称-->确定

现在里面什么代码都没得,添加如下代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace WebApiToSwagger.Controllers
{
/// <summary>
/// 登录相关
/// </summary>
/// <seealso cref="System.Web.Http.ApiController" />
public class LoginController : ApiController
{
/// <summary>
/// 获取列表数据
/// </summary>
/// <param name="value">The value.</param>
public string GetListData([FromBody]string value)
{
return "数据";
}
}
}再次编译,刷新页面看效果。

看到我们新增的Login控制器,其中的GetListData接口也能看到,已经可以使用了。
但是我们看到这个界面上有两个问题,一个是界面是英文的,还有就是Login控制器是有注释的,但是没显示出来,一旦折叠,很难分辨这个控制器是干啥的。
接下来就是汉化和新增这个注释。
9、项目右键-->属性-->生成-->勾选XML文档文件选项-->关闭保存。后续我们将基于api的xml文件来生成接口。
10、修改APP_Start下SwaggerConfig.cs如下
using System.Web.Http;
using WebActivatorEx;
using WebApiToSwagger;
using Swashbuckle.Application;
[assembly: PreApplicationStartMethod(typeof(SwaggerConfig), "Register")]
namespace WebApiToSwagger
{
public class SwaggerConfig
{
public static void Register()
{
var thisAssembly = typeof(SwaggerConfig).Assembly;
GlobalConfiguration.Configuration
.EnableSwagger(c =>
{
c.SingleApiVersion("v1", "WebApiToSwagger");
c.IncludeXmlComments(GetXmlCommentsPath());//让swagger根据xml文档来解析
c.CustomProvider((defaultProvider) => new SwaggerControllerDescProvider(defaultProvider, GetXmlCommentsPath()));//获取控制器的注释方法类
})
.EnableSwaggerUi(c =>
{
c.DocumentTitle("WebApiToSwagger");
c.InjectJavaScript(thisAssembly, "WebApiToSwagger.Models.SwaggerCustom.js");//汉化js
});
}
/// <summary>
/// Gets the XML comments path.
/// </summary>
/// <returns>System.String.</returns>
private static string GetXmlCommentsPath()
{
return $"{System.AppDomain.CurrentDomain.BaseDirectory}/bin/WebApiToSwagger.XML";
}
}
}目前还缺少2个文件。
11、先添加SwaggerControllerDescProvider.cs,新建文件夹App_Filter-->新增类SwaggerControllerDescProvider.cs,添加代码如下:
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using Swashbuckle.Swagger;
namespace WebApiToSwagger.App_Filter
{
/// <summary>
/// swagger显示控制器的描述
/// </summary>
public class SwaggerControllerDescProvider : ISwaggerProvider
{
private readonly ISwaggerProvider _swaggerProvider;
private static ConcurrentDictionary<string, SwaggerDocument> _cache = new ConcurrentDictionary<string, SwaggerDocument>();
private readonly string _xml;
/// <summary>
///
/// </summary>
/// <param name="swaggerProvider"></param>
/// <param name="xml">xml文档路径</param>
public SwaggerControllerDescProvider(ISwaggerProvider swaggerProvider, string xml)
{
_swaggerProvider = swaggerProvider;
_xml = xml;
}
/// <summary>
/// Gets the swagger.
/// </summary>
/// <param name="rootUrl">The root URL.</param>
/// <param name="apiVersion">The API version.</param>
/// <returns>SwaggerDocument.</returns>
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<string, object> { { "ControllerDesc", GetControllerDesc() } };
_cache.TryAdd(cacheKey, srcDoc);
}
return srcDoc;
}
/// <summary>
/// 从API文档中读取控制器描述
/// </summary>
/// <returns>所有控制器描述</returns>
public ConcurrentDictionary<string, string> GetControllerDesc()
{
string xmlpath = _xml;
ConcurrentDictionary<string, string> controllerDescDict = new ConcurrentDictionary<string, string>();
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;
}
}
}12、在Model文件夹中添加SwaggerCustom.js,并且把这个js文件的属性,改一下,改成【嵌入的资源】

代码如下:
'use strict';
window.SwaggerTranslator = {
_words: [],
translate: function () {
var $this = this;
$('[data-sw-translate]').each(function () {
$(this).html($this._tryTranslate($(this).html()));
$(this).val($this._tryTranslate($(this).val()));
$(this).attr('title', $this._tryTranslate($(this).attr('title')));
});
},
setControllerSummary: function () {
$.ajax({
type: "get",
async: true,
url: $("#input_baseUrl").val(),
dataType: "json",
success: function (data) {
var summaryDict = data.ControllerDesc;
var id, controllerName, strSummary;
$("#resources_container .resource").each(function (i, item) {
id = $(item).attr("id");
if (id) {
controllerName = id.substring(9);
strSummary = summaryDict[controllerName];
if (strSummary) {
$(item).children(".heading").children(".options").first().prepend('<li class="controller-summary" title="' + strSummary + '">' + strSummary + '</li>');
}
}
});
}
});
},
_tryTranslate: function (word) {
return this._words[$.trim(word)] !== undefined ? this._words[$.trim(word)] : word;
},
learn: function (wordsMap) {
this._words = wordsMap;
}
};
/* jshint quotmark: double */
window.SwaggerTranslator.learn({
"Warning: Deprecated": "警告:已过时",
"Implementation Notes": "实现备注",
"Response Class": "响应类",
"Status": "状态",
"Parameters": "参数",
"Parameter": "参数",
"Value": "值",
"Example Value": "示例值",
"Description": "描述",
"Parameter Type": "参数类型",
"Data Type": "数据类型",
"Response Messages": "响应消息",
"HTTP Status Code": "HTTP状态码",
"Reason": "原因",
"Response Model": "响应模型",
"Request URL": "请求URL",
"Response Body": "响应体",
"Response Code": "响应码",
"Response Headers": "响应头",
"Hide Response": "隐藏响应",
"Headers": "头",
"Try it out!": "试一下!",
"Show/Hide": "显示/隐藏",
"List Operations": "显示操作",
"Expand Operations": "展开操作",
"Raw": "原始",
"can't parse JSON. Raw result": "无法解析JSON. 原始结果",
"Model Schema": "模型架构",
"Model": "模型",
"apply": "应用",
"Username": "用户名",
"Password": "密码",
"Terms of service": "服务条款",
"Created by": "创建者",
"See more at": "查看更多:",
"Contact the developer": "联系开发者",
"api version": "api版本",
"Response Content Type": "响应Content Type",
"fetching resource": "正在获取资源",
"fetching resource list": "正在获取资源列表",
"Explore": "浏览",
"Show Swagger Petstore Example Apis": "显示 Swagger Petstore 示例 Apis",
"Can't read from server. It may not have the appropriate access-control-origin settings.": "无法从服务器读取。可能没有正确设置access-control-origin。",
"Please specify the protocol for": "请指定协议:",
"Can't read swagger JSON from": "无法读取swagger JSON于",
"Finished Loading Resource Information. Rendering Swagger UI": "已加载资源信息。正在渲染Swagger UI",
"Unable to read api": "无法读取api",
"from path": "从路径",
"server returned": "服务器返回"
});
$(function () {
window.SwaggerTranslator.translate();
window.SwaggerTranslator.setControllerSummary();
});再次编译看效果

汉化也成功了,注释也加上了。
最后要解决两个问题,一个是我们目前的接口,无法跨域访问,一个是如果控制器中写了一个私有方法也会被显示,我们需要加一个特性,来屏蔽指定的方法或者action。
13、跨域解决方案,引入Microsoft.AspNet.WebApi.Cors 5.2.7的包。

修改App_Start下的WebApiConfig文件,修改如下
using System.Web.Http;
namespace WebApiToSwagger
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
var allowOrigins = "*";//最好来自配置文件夹
var allowHeaders = "*";//最好来自配置文件夹
var allowMethods = "*";//最好来自配置文件夹
var globalCors = new System.Web.Http.Cors.EnableCorsAttribute(allowOrigins, allowHeaders, allowMethods)
{
SupportsCredentials = true
};
config.EnableCors(globalCors);
// Web API 路由
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
}已经可以跨域访问接口了。
14、屏蔽某些接口不给界面展示,需要先修改一下App_Start文件夹下的SwaggerConfig文件。修改如下:
using System.Web.Http;
using WebActivatorEx;
using WebApiToSwagger;
using Swashbuckle.Application;
using WebApiToSwagger.App_Filter;
[assembly: PreApplicationStartMethod(typeof(SwaggerConfig), "Register")]
namespace WebApiToSwagger
{
public class SwaggerConfig
{
public static void Register()
{
var thisAssembly = typeof(SwaggerConfig).Assembly;
GlobalConfiguration.Configuration
.EnableSwagger(c =>
{
c.SingleApiVersion("v1", "WebApiToSwagger");
c.IncludeXmlComments(GetXmlCommentsPath());
c.DocumentFilter<HiddenApiFilter>();
c.CustomProvider((defaultProvider) => new SwaggerControllerDescProvider(defaultProvider, GetXmlCommentsPath()));
})
.EnableSwaggerUi(c =>
{
c.DocumentTitle("WebApiToSwagger");
c.InjectJavaScript(thisAssembly, "WebApiToSwagger.Models.SwaggerCustom.js");
});
}
/// <summary>
/// Gets the XML comments path.
/// </summary>
/// <returns>System.String.</returns>
private static string GetXmlCommentsPath()
{
return $"{System.AppDomain.CurrentDomain.BaseDirectory}/bin/WebApiToSwagger.XML";
}
}
}在App_Filter文件夹中,添加HidddenApiFilter.cs,代码如下:
using System;
using System.Linq;
using System.Web.Http.Description;
using Swashbuckle.Swagger;
namespace WebApiToSwagger.App_Filter
{
/// <summary>
/// 隐藏接口,不生成到swagger文档展示
/// </summary>
/// <seealso cref="System.Attribute" />
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public partial class HiddenApiAttribute : Attribute { }
/// <summary>
/// Class HiddenApiFilter.
/// </summary>
/// <seealso cref="Swashbuckle.Swagger.IDocumentFilter" />
public class HiddenApiFilter : IDocumentFilter
{
/// <summary>
/// 重写Apply方法,移除隐藏接口的生成
/// </summary>
/// <param name="swaggerDoc">swagger文档文件</param>
/// <param name="schemaRegistry"></param>
/// <param name="apiExplorer">api接口集合</param>
public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
{
foreach (ApiDescription apiDescription in apiExplorer.ApiDescriptions)
{
if (Enumerable
.OfType<HiddenApiAttribute>(apiDescription.GetControllerAndActionAttributes<HiddenApiAttribute>())
.Any())
{
string key = "/" + apiDescription.RelativePath;
if (key.Contains("?"))
{
int idx = key.IndexOf("?", StringComparison.Ordinal);
key = key.Substring(0, idx);
}
swaggerDoc.paths.Remove(key);
}
}
}
}
}使用如下:
加上这个特性,这个接口或者这个控制器里面的所有接口,都不会显示了。

到这里就搭建完成了,最终项目结构如下:

波浪线是因为有些地方没写注释导致的。
运行发现还有个小错误。

最后禁用validator验证即可解决,SwaggerConfig.cs加上这句 c.DisableValidator();
川公网安备 51010702003150号
留下您的脚步
最近评论