如何使 WebAPI 自动生成漂亮又实用在线API文档

1.前言

1.1 SwaggerUI

SwaggerUI 是一个简单的Restful API 测试和文档工具。简单、漂亮、易用(官方demo)。通过读取JSON 配置显示API. 项目本身仅仅也只依赖一些 html,css.js静态文件. 你可以几乎放在任何Web容器上使用。

1.2 Swashbuckle

Swashbuckle 是.NET类库,可以将WebAPI所有开放的控制器方法生成对应SwaggerUI的JSON配置。再通过SwaggerUI 显示出来。类库中已经包含SwaggerUI 。所以不需要额外安装。

2.快速开始

  • 创建项目 OnlineAPI来封装百度音乐服务(示例下载) ,通过API可以搜索、获取音乐的信息和播放连接。

    我尽量删除一些我们demo中不会用到的一些文件,使其看上去比较简洁。

Swashbuckle配置文件

  • WebAPI 安装 Swashbuckle

    1
    Install-Package Swashbuckle
  • 代码注释生成文档说明。
    Swashbuckle 是通过生成的XML文件来读取注释的,生成 SwaggerUI,JSON 配置中的说明的。
    安装时会在项目目录 App_Start 文件夹下生成一个 SwaggerConfig.cs 配置文件,用于配置 SwaggerUI 相关展示行为的。如图:

Swashbuckle配置文件

  • 将配置文件大概99行注释去掉并修改为
1
c.IncludeXmlComments(GetXmlCommentsPath(thisAssembly.GetName().Name));
  • 并在当前类中添加一个方法

    1
    2
    3
    4
    5
    6
    7
    8
    /// <summary>
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    protected static string GetXmlCommentsPath(string name)
    {

    return string.Format(@"{0}\bin\{1}.XML", AppDomain.CurrentDomain.BaseDirectory, name);
    }
  • 紧接着你在此Web项目属性生成选卡中勾选 “XML 文档文件”,编译过程中生成类库的注释文件

生成XML

  • 添加百度音乐 3个API

WebAPI

  • 访问 http://<youhost>/swagger/ui/index,最终显示效果

WebAPI

  • 我们通过API 测试API 是否成功运行

WebAPI

3.添加自定义HTTP Header

在开发移动端 API时常常需要验证权限,验证参数放在Http请求头中是再好不过了。WebAPI配合过滤器验证权限即可

首先我们需要创建一个 IOperationFilter 接口的类。IOperationFilter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http;
using System.Web.Http.Description;
using System.Web.Http.Filters;
using Swashbuckle.Swagger;

namespace OnlineAPI.Utility
{
public class HttpHeaderFilter : IOperationFilter
{
public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{

if (operation.parameters == null) operation.parameters = new List<Parameter>();
var filterPipeline = apiDescription.ActionDescriptor.GetFilterPipeline();
//判断是否添加权限过滤器
var isAuthorized = filterPipeline.Select(filterInfo => filterInfo.Instance).Any(filter => filter is IAuthorizationFilter);
//判断是否允许匿名方法
var allowAnonymous = apiDescription.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Any();
if (isAuthorized && !allowAnonymous)
{
operation.parameters.Add(new Parameter
{
name = "access-key",
@in = "header",
description = "用户访问Key",
required = false,
type = "string"
});
}
}
}
}

在 SwaggerConfig.cs 的 EnableSwagger 配置匿名方法类添加一行注册代码

1
c.OperationFilter<HttpHeaderFilter>();

添加Web权限过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Web;
using System.Web.Http;
using System.Web.Http.Controllers;
using Newtonsoft.Json;

namespace OnlineAPI.Utility
{
/// <summary>
///
/// </summary>
public class AccessKeyAttribute : AuthorizeAttribute
{
/// <summary>
/// 权限验证
/// </summary>
/// <param name="actionContext"></param>
/// <returns></returns>
protected override bool IsAuthorized(HttpActionContext actionContext)
{

var request = actionContext.Request;
if (request.Headers.Contains("access-key"))
{
var accessKey = request.Headers.GetValues("access-key").SingleOrDefault();
//TODO 验证Key
return accessKey == "123456789";
}
return false;
}

/// <summary>
/// 处理未授权的请求
/// </summary>
/// <param name="actionContext"></param>
protected override void HandleUnauthorizedRequest(HttpActionContext actionContext)
{

var content = JsonConvert.SerializeObject(new {State = HttpStatusCode.Unauthorized});
actionContext.Response = new HttpResponseMessage
{
Content = new StringContent(content, Encoding.UTF8, "application/json"),
StatusCode = HttpStatusCode.Unauthorized
};
}
}
}

在你想要的ApiController 或者是 Action 添加过滤器

1
[AccessKey]

最终显示效果

WebAPI

4.显示上传文件参数

SwaggerUI 有上传文件的功能和添加自定义HTTP Header 做法类似,只是我们通过特殊的设置来标示API具有上传文件的功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Http.Description;
using Swashbuckle.Swagger;

namespace OnlineAPI.Utility
{
/// <summary>
///
/// </summary>
public class UploadFilter : IOperationFilter
{

/// <summary>
/// 文件上传
/// </summary>
/// <param name="operation"></param>
/// <param name="schemaRegistry"></param>
/// <param name="apiDescription"></param>
public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{

if (!string.IsNullOrWhiteSpace(operation.summary) && operation.summary.Contains("upload"))
{
operation.consumes.Add("application/form-data");
operation.parameters.Add(new Parameter
{
name = "file",
@in = "formData",
required = true,
type = "file"
});
}
}
}
}

在 SwaggerConfig.cs 的 EnableSwagger 配置匿名方法类添加一行注册代码

1
c.OperationFilter<UploadFilter>();

WebAPI

API 文档展示效果

WebAPI

5.版本和资源

你可以通过下列连接获取相关说明。
OnlineAPI Demo
http://files.cnblogs.com/files/Arrays/OnlineAPI.rar
Swashbuckle 项目地址:
https://github.com/domaindrivendev/Swashbuckle
swagger-ui 项目地址:
https://github.com/swagger-api/swagger-ui
swagger-ui 官网地址:
http://swagger.io/swagger-ui/

Elasticsearch,kibana 搭建日志集中分析系统

1.前言

日志收集分析非常重要,而且日志到达一定数量后分析无法进行。本篇文章讲解如何使用 Elasticsearch+kibana 在 Ubuntu系统下快速搭建一个日志分析系统。

Elasticsearch:
Elasticsearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。

Kibana
Kibana是一个使用 Apache 开源协议,基于浏览器的 Elasticsearch 分析和搜索仪表板。Kibana 非常容易安装和使用。整个项目都是用 HTML 和 Javascript 写的,所以 Kibana 不需要任何服务器端组件,一个纯文本发布服务器就够了。Kibana 和Elasticsearch 一样,力争成为极易上手,但同样灵活而强大的软件。

2.安装 Java 8,Elasticsearch 全文检索系统

你可以在此查看 ElasticSearch.sh 自动安装脚本。当前脚本会安装网站最新版本 elasticsearch-2.2.1

下载自动安装脚本

1
cd ~ && wget https://gist.githubusercontent.com/seamys/c08a6ccc7c34cb19cf05/raw/f12379cf54438630decf776089f76fb62aff9282/ElasticSearch.sh

为脚本添加执行权限

1
chmod a+r ./ElasticSearch.sh

执行当前安装脚本

1
sudo ./ElasticSearch.sh

绑定IP

如果上述脚本安装成功。我们紧接着修改elasticsearch http访问地址

5.WebAPI与log4net,log4net.Elasticsearch 扩展

6.完成

参考链接:
Kibana User Guide 4.3
Elasticsearch Reference
how-to-install-elasticsearch-logstash-and-kibana-elk-stack-on-ubuntu-14-04
log4net.ElasticSearch

CsQuery .Net下的jQuery实现

CsQuery Github 作者已经不再积极维护此项目。

CsQuery 简介

CsQuery实现了所有CSS2和CSS3选择器。实现jQuery的DOM操作方法和工具方法。jQuery 1.6.2 的多数测试已经在C#上成功运行。

为什么需要 CsQuery?

jQuery使用CSS选择器非常容易在客户端上操作HTML。没有理由在服务端使用更复杂的方式来处理这样的事情。它在eb项目上做HTML页面后处理,比如网页抓取后做页面分析等。

符合标准的HTML解析

CsQuery使用validator.nu HTML解析器的C#实现和Gecko 浏览器引擎使用相同的代码。CsQuery将创建从同一来源任何基于Gecko的浏览器相同的DOM。你应该想到它处理有效和无效的标记的优异的成绩。

CSS3选择器和jQuery方法

CsQuery实现了所有CSS2和CSS3选择器和过滤器,以及全面的DOM模型。您可以使用所有相同的jQuery你熟悉遍历和操纵DOM方法。

快速索引CSS选择器

该CSS选择器引擎完全索引Html文档上的tagName,ID,class和属性。该指数是子选择能力,这意味着复杂的选择仍然能够采取指数(用于编入索引选择的任何部分)的优势。选择的性能相对于其他现有的C#HTML解析库是快几个数量级。
更重要的是,从灒(jQuery的CSS选择器引擎)和jQuery整个测试套件(1.6.2)已经从Javascript移植到C#来弥补这一项目。

难以置信的简单

CQ 对象包含你所有需要的功能,CQ对象可以像jQuery一样使用,你可以把字符串赋值到CQ 对象上进行解析。通过 [...] 来运行jQuery选择器,并返回新的CQ对象。像 jQuery中 $('..')一样。最后通过Render返回 DOM 字符串。使用CQ 你可以完整的使用jQuery API 遍历和操作HTML文档。
下面有一些HTML解析,选择,修改和操作后返回字符串的例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

CQ dom = "<div>Hello world! <b>I am feeling bold!</b> What about <b>you?</b></div>";


CQ bold = dom["b"]; /// find all "b" nodes (在例子中有两个)
     
> bold.ToList()
> Count = 2
> [0]: {<b>...</b>}
> [1]: {<b>...</b>}

> bold.First().RenderSelection()
> "<b>I am feeling bold!</b>"

string boldText = bold.Text(); /// jQuery text 方法;

> boldText
> "I am feeling bold! you?"

bold.Remove(); /// jQuery remove 方法
string html = dom.Render();

> html
> "<div>Hello world! What about </div>"

资源列表

CsQuery Github: https://github.com/jamietre/CsQuery

JQuery 无限级下拉选择插件

插件主要应对无限级树型结构的数据。比如说国家、省、市、区、县级市、村、街道的选择,还有一些B2B之类的网站无限极分类。无论本地数据还是异步获取这些在后端系统对内容进行归类是常常用到。jQuery.category.js 就是为了解决此问题的。

image

快速开始

下载压缩版本 或者 开发版本.

依赖文件

1
2
3
4
<!--引入jquery 1.7 以上的版本-->
<script src="jquery.js"></script>
<!--引入插件-->
<script src="jquery.category.js"></script>

2. 数据脚本格式

我们简单使用一些数据来填充使用(并非一定需要使用当前格式后面详细介绍使用)。

1
2
3
4
5
6
7
var data = {
id_0: [{ Id: 1, Name: '服装' }, { Id: 2, Name: '鞋包' }],
id_1: [{ Id: 3, Name: '男装' }, { Id: 4, Name: '女装' }],
id_2: [{ Id: 5, Name: '男鞋' }, { Id: 6, Name: '女鞋' }, { Id: 7, Name: '箱包' }],
id_3: [{ Id: 8, Name: '男式上装' }, { Id: 9, Name: '男式裤子' }],
id_4: [{ Id: 7, Name: '女式裙子' }, { Id: 8, Name: '女式裤子' }]
}

数据格式,id_0 节点是所有节点的根分类。根分类下有 服装鞋包。服装的 Id为 1 则服装的子分类key为 id_1,服装下有 男装女装。以此类推。

3. 调用插件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- 用一个div 存储结果-->
<div id="show"></div>
<div class="category"></div>
<script type="text/javascript">
$(".category").category({
//select 值修改后触发此回调方法
change: function (v) {
$("#show").text(v.join());
},
//数据提供回调方法
provider: function (params, url, callback) {
var id = params[params.length - 1];
callback(data["id_" + id]);
}
});
</script>

查看demo

点击查看 DEMO

image

参数说明

url(string) : 在异步工作下,插件通过此URL获取相应分类子分类

handel(function) : 创建select内option时将调用此方法,当您无法控制服务器或者数据源格式的时可以重写此方法。

path(string) : 在页面初始化时,指定此参数可以初始化选中的select值。例如:0,2,38,65,

sChar(string) : 用于指定拆分 path的字符。 默认 “,

create(function) : 创建 select 时调用,回调方法必须返回一个jQuery select 对象。

filter(function) : 方法在 provider方法执行后调用,负责对数据进行二次过滤。

param(function) : 如果你无法控制服务器提供的参数名称时可以调用此方法重写服务器参数。

provider(function): 数据提供者,您可以通过此方法来重写数据提供的方式。

默认参数如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
url: "/category/list",
handel: function (v) {
return "<option value=" + v.Id + ">" + v.Name + "</option>";
},
path: 0,
sChar: ',',
change: function (arrays) { return arrays; },
create: function () {
return $("<select>");
},
filter: function (arrays) {
return arrays;
},
param: function (arrays) {
return { ids: arrays.join(defaults.sChar) };
},
provider: function (arrays, callback) {
$.ajax(defaults.url, {
type: 'GET',
dataType: "json",
cache: true,
data: defaults.param(arrays || [0]),
success: callback
});
},
defOption: '请选择..'
}

开发文档

Examples

Examples

Release History

2016年1月30日 0.1.0 Release

哈利波特电影的一些偏见

陪对象看“哈利波特”系列电影时我突然觉得“德拉科·马尔福”是个货真价实的帅哥,也毫无保留的说了出来。女朋友面无表情的对我说

“你真有当坏人的潜质”。

暂且不谈我是不是一个坏人,因为我不知道如何评价我自己,既然说了“有当坏人的潜质”,那说明迄今为止我还是一个好人。
当然能说出“你有当坏人的潜质”。她还是非常厌恶这个角色的,我喜欢他也并不是我价值观扭曲。
高中时代唯一最多的是时间(听了这个话你就知道我是个“学渣”)每个月回家,都会租大量的电影碟片回家。所以从“火焰杯” 之前的多部电影基本每部3遍以上。

“马尔福”更像是一个巫师界的贵族。对“麻瓜”厌恶至极,傲慢和极端偏见,与哈利第一次见面时候主动结交朋友便遭到拒绝。“混血王子”之前的多部,
“马尔福”更多也是和哈利一些言语上的冲突并没有干什么十恶不赦的事情,而之后当了“食死徒”,接受“伏地魔”的任务去杀死霍格沃兹学院校长经历内心的极度挣扎。
尚若这样定性一个人是坏人那样未免觉得草率。