http://www.cnblogs.com/idoudou/p/xmldocumentation-for-web-api-include-documentation-from-beyond-the-main.html

一、前言

上篇有提到在WebAPI项目内,通过在Nuget里安装(Microsoft.AspNet.WebApi.HelpPage)可以根据注释生成帮助文档,查看代码实现会发现是基于解析项目生成的xml文档来作为数据源从而展示出来的。在我们的项目帮助文档需要的类(特指定义的Request和Response)与项目在同一个项目时是没有问题的,但是我们实际工作中会因为其他项目也需要引用该(Request和Response)时,我们会将其抽出来单独作为一个项目供其它调用来引用,这时,查看帮助文档不会报错,但是注释以及附加信息将会丢失,因为这些信息是我们的代码注释和数据注释(如 [Required]标识为必填),也是生成到xml文档中的信息,但因不在同一项目内,将读取不到从而导致帮助文档无法显示我们的注释(对应的描述)和附加信息(是否必填、默认值、Range等).

二、帮助文档注释概要

我们的注释就是帮助文档的说明或者说是描述,那么这个功能是安装了HelpPage就直接具有的吗,这里分两种方式。

1:创建项目时是直接选择的Web API,那么这时在创建初始化项目时就配置好此功能的。

2:创建项目时选择的是Empty,选择的核心引用选择Web API是不具有此功能。

对于方式1来说生成的项目代码有一部分我们是不需要的,我们可以做减法来删掉不必要的文件。

对于方式2来说,需要在Nuget内安装HelpPage,需要将文件~/Areas/HelpPage/HelpPageConfig.cs内的配置注释取消,具体的可以根据需要。

并且设置项目的生成属性内的输出,勾选Xml文档文件,同时设置值与~/Areas/HelpPage/HelpPageConfig.cs

内的配置一致。

并在Global.asax文件Application_Start方法注册。

  1. AreaRegistration.RegisterAllAreas();

这时帮助文档已经可用,但却没有样式。你可以选择手动将需要的css及js拷入Areas文件夹内。并添加文件

  1. public class BundleConfig
  2. {
  3. // 有关绑定的详细信息,请访问 http://go.microsoft.com/fwlink/?LinkId=301862
  4. public static void RegisterBundles(BundleCollection bundles)
  5. {
  6. bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
  7. "~/Areas/HelpPage/Scripts/jquery-{version}.js"));
  8. // 使用要用于开发和学习的 Modernizr 的开发版本。然后,当你做好
  9. // 生产准备时,请使用 http://modernizr.com 上的生成工具来仅选择所需的测试。
  10. bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(
  11. "~/Areas/HelpPage/Scripts/modernizr-*"));
  12. bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include(
  13. "~/Areas/HelpPage/Scripts/bootstrap.js",
  14. "~/Areas/HelpPage/Scripts/respond.js"));
  15. bundles.Add(new StyleBundle("~/Content/css").Include(
  16. "~/Areas/HelpPage/Content/bootstrap.css",
  17. "~/Areas/HelpPage/Content/site.css"));
  18. }
  19. }

并在Global.asax文件Application_Start方法将其注册。

  1. BundleConfig.RegisterBundles(BundleTable.Bundles);

最后更改~/Areas/HelpPage/Views/Shared/_Layout.cshtml 为

  1. @using System
  2. @using System.Web.Optimization
  3. <!DOCTYPE html>
  4. <html>
  5. <head>
  6. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  7. <meta charset="utf-8" />
  8. <meta name="viewport" content="width=device-width" />
  9. <title>@ViewBag.Title</title>
  10. @Styles.Render("~/Content/css")
  11. @Scripts.Render("~/bundles/modernizr")
  12. </head>
  13. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  14. <body>
  15. <div class="navbar navbar-inverse navbar-fixed-top">
  16. <div class="container">
  17. <div class="navbar-header">
  18. <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
  19. <span class="icon-bar"></span>
  20. <span class="icon-bar"></span>
  21. <span class="icon-bar"></span>
  22. </button>
  23. </div>
  24. <div class="navbar-collapse collapse">
  25. <ul class="nav navbar-nav">
  26. <li>@Html.Raw("<a href='/Help'>首页</a>")</li>
  27. <li>@Html.Raw("<a href='/PostMan' target='_blank'>PostManFeture</a>")</li>
  28. </ul>
  29. </div>
  30. </div>
  31. </div>
  32. <div class="container body-content">
  33. @RenderBody()
  34. <hr />
  35. <footer>
  36. <p>&copy; @DateTime.Now.Year - 逗豆豆</p>
  37. </footer>
  38. </div>
  39. @Scripts.Render("~/bundles/jquery")
  40. @Scripts.Render("~/bundles/bootstrap")
  41. @RenderSection("scripts", required: false)
  42. </body>
  43. </html>

此时你看到的才会是如下的文档。

对应的路由结构如下

查看Route可以发现其 AllowMultiple = true 意味着我们可以对同一个Action定义多个不同的路由,但同时也意味着该Action只允许定义的路由访问。

比如这里的Get方法,这时在浏览器只能以这种方式访问 http://localhost:11488/api/product/{id}

用 http://localhost:11488/api/product?id={id} 则会抛出405,如下。

为了支持多种方式我们将路由增加,如下。

这时文档会将两种路由都生成出来。

这里有个原则是同类型的请求且响应的类型相同不允许定义相同的路由,如下,都是HttpGet 且响应类型相同。

  1. /// <summary>
  2. ///     获取所有产品
  3. /// </summary>
  4. [HttpGet, Route("")]
  5. public IEnumerable<Product> Get()
  6. {
  7. return _products;
  8. }
  9. /// <summary>
  10. ///     获取前三产品
  11. /// </summary>
  12. [HttpGet, Route("")]
  13. public IEnumerable<Product> GetTop3()
  14. {
  15. return _products.Take(3);
  16. }

此时访问 http://localhost:11488/api/product 会发现500错误,提示为匹配到多个Action,且这时候查看帮助文档也只会显示一个匹配的Action(前提是你没有指定Route的Order属性)。

路由内可以做一些基本的限制,我们将上面的Top3方法改造为可以根据传入参数来决定Top多少,并且最少是前三条。

  1. /// <summary>
  2. ///     获取前几产品
  3. /// </summary>
  4. [HttpGet, Route("Top/{count:min(3)}")]
  5. public IEnumerable<Product> GetTop(int count)
  6. {
  7. return _products.Take(3);
  8. }

这时访问 http://localhost:11488/api/product/Top/1 或 http://localhost:11488/api/product/Top/2 将会是抛出404

但是我希望直接访问 http://localhost:11488/api/product/Top 默认取前3条,这时直接访问会是405,因为并没有定义出Route(“Top”)的路由,我们改造下

  1. /// <summary>
  2. ///     获取前几产品
  3. /// </summary>
  4. [HttpGet, Route("Top/{count:min(3):int=3}")]
  5. public IEnumerable<Product> GetTop(int count)
  6. {
  7. return _products.Take(3);
  8. }

这时在访问 http://localhost:11488/api/product/Top 就会默认返回前3条了,除此之外还有一些定义包括正则可以 看这里 和 这里 。

路由的文档相关的基本就这些,有遗漏的地方欢迎指出。

接下来就是单个接口的Request和Response的文档,先来看看我们分别以Request和Response分开来看。

首先看下 api/Product/All 这个接口的显示,会发现分为两类。

api/Product 这个接口本身是就不需要任何参数的,因此都是None。

Put api/Product?id={id} 这接口确是都包含。他的定义如下。

  1. /// <summary>
  2. ///     编辑产品
  3. /// </summary>
  4. /// <param name="id">产品编号</param>
  5. /// <param name="request">编辑后的产品</param>
  6. [HttpPut, Route(""), Route("{id}")]
  7. public string Put(int id, Product request)
  8. {
  9. var model = _products.FirstOrDefault(x => x.Id.Equals(id));
  10. if (model == null) return "未找到该产品";
  11. model.Name = request.Name;
  12. model.Price = request.Price;
  13. model.Description = request.Description;
  14. return "ok";
  15. }

那其实,实际中我们可能只会使用Get和Post来完成我们所有的操作。因此,就会是Get只显示URI Parameters 而 Post只显示Body Parameters。

可以看到Description就是我们对属性的注释,Type就是属性的类型,而Additional information 则是“约束”的描述,如我们会约束请求的参数哪些为必填哪些为选填,哪些参数的值具有使用范围。

比如我们改造一下Product。

  1. /// <summary>
  2. ///     产品
  3. /// </summary>
  4. public class Product
  5. {
  6. /// <summary>
  7. ///     编号
  8. /// </summary>
  9. [Required]
  10. public int Id { get; set; }
  11. /// <summary>
  12. ///     名称
  13. /// </summary>
  14. [Required, MaxLength(36)]
  15. public string Name { get; set; }
  16. /// <summary>
  17. ///     价格
  18. /// </summary>
  19. [Required, Range(0, 99999999)]
  20. public decimal Price { get; set; }
  21. /// <summary>
  22. ///     描述
  23. /// </summary>
  24. public string Description { get; set; }
  25. }

可以看见对应的“约束信息”就改变了。

有人可能会说,我自定义了一些约束该怎么显示呢,接下来我们定义一个最小值约束MinAttrbute。

  1. /// <summary>
  2. ///     最小值特性
  3. /// </summary>
  4. [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
  5. public class MinAttribute : ValidationAttribute
  6. {
  7. /// <summary>
  8. ///     最小值
  9. /// </summary>
  10. public int MinimumValue { get; set; }
  11. /// <summary>
  12. ///     构造函数
  13. /// </summary>
  14. /// <param name="minimun"></param>
  15. public MinAttribute(int minimun)
  16. {
  17. MinimumValue = minimun;
  18. }
  19. /// <summary>
  20. ///     验证逻辑
  21. /// </summary>
  22. /// <param name="value">需验证的值</param>
  23. /// <returns>是否通过验证</returns>
  24. public override bool IsValid(object value)
  25. {
  26. int intValue;
  27. if (value != null && int.TryParse(value.ToString(), out intValue))
  28. {
  29. return (intValue >= MinimumValue);
  30. }
  31. return false;
  32. }
  33. /// <summary>
  34. ///     格式化错误信息
  35. /// </summary>
  36. /// <param name="name">属性名称</param>
  37. /// <returns>错误信息</returns>
  38. public override string FormatErrorMessage(string name)
  39. {
  40. return string.Format("{0} 最小值为 {1}", name, MinimumValue);
  41. }
  42. }

将其加在Price属性上,并将最小值设定为10。

  1. /// <summary>
  2. ///     价格
  3. /// </summary>
  4. [Required, Min(10)]
  5. public decimal Price { get; set; }

这时通过PostMan去请求,会发现验证是通过的,并没有预计的错误提示。那是因为我们没有启用验证属性的特性。

我们自定义一个ValidateModelAttribute,可用范围指定为Class和Method,且不允许多次,并将其加到刚才的Put接口上。

  1. /// <summary>
  2. ///     验证模型过滤器
  3. /// </summary>
  4. [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
  5. public class ValidateModelAttribute : ActionFilterAttribute
  6. {
  7. /// <summary>
  8. ///     Action执行前验证
  9. /// </summary>
  10. /// <param name="actionContext">The action context.</param>
  11. public override void OnActionExecuting(HttpActionContext actionContext)
  12. {
  13. if (actionContext.ActionArguments.Any(kv => kv.Value == null))
  14. {
  15. actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, "参数不能为空");
  16. }
  17. if (actionContext.ModelState.IsValid) return;
  18. actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState);
  19. }
  20. }
  1. /// <summary>
  2. ///     编辑产品
  3. /// </summary>
  4. /// <param name="id">产品编号</param>
  5. /// <param name="request">编辑后的产品</param>
  6. [HttpPut, Route(""), Route("{id}")]
  7. [ValidateModel]
  8. public string Put(int id, Product request)
  9. {
  10. var model = _products.FirstOrDefault(x => x.Id.Equals(id));
  11. if (model == null) return "未找到该产品";
  12. model.Name = request.Name;
  13. model.Price = request.Price;
  14. model.Description = request.Description;
  15. return "ok";
  16. }

这是我们使用PostMan请求,验证提示便出现了。

但这时候看我们的帮助文档,Price的“约束信息”就仅剩Required一个了。

那我要将自定义的MinAttribute的约束信息也显示出来该怎么办呢,观察文档的生成代码可以发现是在Areas.HelpPage.ModelDescriptions.ModelDescriptionGenerator类中的AnnotationTextGenerator内的定义生成的。

那既然如此就好办了,我将我自定义的也加进去。

  1. // Modify this to support more data annotation attributes.
  2. private readonly IDictionary<Type, Func<object, string>> AnnotationTextGenerator = new Dictionary<Type, Func<object, string>>
  3. {
  4. { typeof(RequiredAttribute), a => "Required" },
  5. { typeof(RangeAttribute), a =>
  6. {
  7. RangeAttribute range = (RangeAttribute)a;
  8. return String.Format(CultureInfo.CurrentCulture, "Range: inclusive between {0} and {1}", range.Minimum, range.Maximum);
  9. }
  10. },
  11. { typeof(MaxLengthAttribute), a =>
  12. {
  13. MaxLengthAttribute maxLength = (MaxLengthAttribute)a;
  14. return String.Format(CultureInfo.CurrentCulture, "Max length: {0}", maxLength.Length);
  15. }
  16. },
  17. { typeof(MinLengthAttribute), a =>
  18. {
  19. MinLengthAttribute minLength = (MinLengthAttribute)a;
  20. return String.Format(CultureInfo.CurrentCulture, "Min length: {0}", minLength.Length);
  21. }
  22. },
  23. { typeof(StringLengthAttribute), a =>
  24. {
  25. StringLengthAttribute strLength = (StringLengthAttribute)a;
  26. return String.Format(CultureInfo.CurrentCulture, "String length: inclusive between {0} and {1}", strLength.MinimumLength, strLength.MaximumLength);
  27. }
  28. },
  29. { typeof(DataTypeAttribute), a =>
  30. {
  31. DataTypeAttribute dataType = (DataTypeAttribute)a;
  32. return String.Format(CultureInfo.CurrentCulture, "Data type: {0}", dataType.CustomDataType ?? dataType.DataType.ToString());
  33. }
  34. },
  35. { typeof(RegularExpressionAttribute), a =>
  36. {
  37. RegularExpressionAttribute regularExpression = (RegularExpressionAttribute)a;
  38. return String.Format(CultureInfo.CurrentCulture, "Matching regular expression pattern: {0}", regularExpression.Pattern);
  39. }
  40. },
  41. { typeof(MinAttribute), a =>
  42. {
  43. MinAttribute minAttribute = (MinAttribute)a;
  44. return String.Format(CultureInfo.CurrentCulture, "最小值: {0}", minAttribute.MinimumValue);
  45. }
  46. },
  47. };

接着再看文档,我们的“约束信息”就出来了。

Request部分基本也就这些了。Response部分没太多内容,主要就是Sample的显示会有一个问题,你若是一步一步写到这里看到的帮助文档Sample会有三个,分别是

application/json,text/json  application/xml,text/xml   application/x-www-from-urlencoded

这里我们会发现它生成不了 application/x-www-form-urlencoded,是因为无法使用JqueryMvcFormUrlEncodeFomatter来格式我们的类。至于为什么,我没有去找,因为除了application/json是我需要的之外其余的我都不需要。

有兴趣的朋友可以找找为什么。然后告知一下~那这里我们将不需要的移除,如下。

  1. public static class WebApiConfig
  2. {
  3. public static void Register(HttpConfiguration config)
  4. {
  5. // Web API 配置和服务
  6. config.Formatters.Remove(config.Formatters.XmlFormatter);
  7. // Web API 路由
  8. config.MapHttpAttributeRoutes();
  9. }
  10. }

这里只移除了XmlFormatter,因为application/x-www-form-urlencoded我们在请求的时候还需要,但我不想让他显示在文档中,于是…

在Areas.HelpPage.SampleGeneration.HelpPageSampleGenerator类中的 GetSample 方法内将

foreach (var formatter in formatters)

更改为

foreach (var formatter in formatters.Where(x => x.GetType() != typeof(JQueryMvcFormUrlEncodedFormatter)))

然后,文档就干净了,这难道是洁癖么…

三、使用多个项目生成Xml文件来显示帮助文档

终于到这了,我们首先将Product单独作为一个项目 WebAPI2PostMan.WebModel 并引用他,查看文档如下。

你会发现,你的注释也就是属性的描述没有了。打开App_Data/XmlDocument.xml文件对比之前P没移动roduct的xml文件确实Product类的描述确实没有了,因为此处的XmlDocument.xml文件是项目的生成描述文件,不在此项目

内定义的文件是不会生成在这个文件内的,那真实的需求是我们确确实实需要将所有Request和Response单独定义在一个项目内供其它项目引用,可能是单元测试也可能是我们封装的WebAPI客户端(此处下篇文章介绍)。

带着这个疑问找到了这样一篇文章 http://stackoverflow.com/questions/21895257/how-can-xml-documentation-for-web-api-include-documentation-from-beyond-the-main

该文章提供了3种办法,这里只介绍我认为合理的方法,那那就是我们就需要将 WebAPI2PostMan.WebModel 的生成属性也勾选XML文档文件,就是也生成一个xml文档,同时拓展出一个新的Xml文档加载方式

在目录 ~/Areas/HelpPage/ 下新增一个名为 MultiXmlDocumentationProvider.cs 的类。

  1. using System;
  2. using System.Linq;
  3. using System.Reflection;
  4. using System.Web.Http.Controllers;
  5. using System.Web.Http.Description;
  6. using Xlobo.RechargeService.Areas.HelpPage.ModelDescriptions;
  7. namespace Xlobo.RechargeService.Areas.HelpPage
  8. {
  9. /// <summary>A custom <see cref="IDocumentationProvider"/> that reads the API documentation from a collection of XML documentation files.</summary>
  10. public class MultiXmlDocumentationProvider : IDocumentationProvider, IModelDocumentationProvider
  11. {
  12. /*********
  13. ** Properties
  14. *********/
  15. /// <summary>The internal documentation providers for specific files.</summary>
  16. private readonly XmlDocumentationProvider[] Providers;
  17. /*********
  18. ** Public methods
  19. *********/
  20. /// <summary>Construct an instance.</summary>
  21. /// <param name="paths">The physical paths to the XML documents.</param>
  22. public MultiXmlDocumentationProvider(params string[] paths)
  23. {
  24. this.Providers = paths.Select(p => new XmlDocumentationProvider(p)).ToArray();
  25. }
  26. /// <summary>Gets the documentation for a subject.</summary>
  27. /// <param name="subject">The subject to document.</param>
  28. public string GetDocumentation(MemberInfo subject)
  29. {
  30. return this.GetFirstMatch(p => p.GetDocumentation(subject));
  31. }
  32. /// <summary>Gets the documentation for a subject.</summary>
  33. /// <param name="subject">The subject to document.</param>
  34. public string GetDocumentation(Type subject)
  35. {
  36. return this.GetFirstMatch(p => p.GetDocumentation(subject));
  37. }
  38. /// <summary>Gets the documentation for a subject.</summary>
  39. /// <param name="subject">The subject to document.</param>
  40. public string GetDocumentation(HttpControllerDescriptor subject)
  41. {
  42. return this.GetFirstMatch(p => p.GetDocumentation(subject));
  43. }
  44. /// <summary>Gets the documentation for a subject.</summary>
  45. /// <param name="subject">The subject to document.</param>
  46. public string GetDocumentation(HttpActionDescriptor subject)
  47. {
  48. return this.GetFirstMatch(p => p.GetDocumentation(subject));
  49. }
  50. /// <summary>Gets the documentation for a subject.</summary>
  51. /// <param name="subject">The subject to document.</param>
  52. public string GetDocumentation(HttpParameterDescriptor subject)
  53. {
  54. return this.GetFirstMatch(p => p.GetDocumentation(subject));
  55. }
  56. /// <summary>Gets the documentation for a subject.</summary>
  57. /// <param name="subject">The subject to document.</param>
  58. public string GetResponseDocumentation(HttpActionDescriptor subject)
  59. {
  60. return this.GetFirstMatch(p => p.GetDocumentation(subject));
  61. }
  62. /*********
  63. ** Private methods
  64. *********/
  65. /// <summary>Get the first valid result from the collection of XML documentation providers.</summary>
  66. /// <param name="expr">The method to invoke.</param>
  67. private string GetFirstMatch(Func<XmlDocumentationProvider, string> expr)
  68. {
  69. return this.Providers
  70. .Select(expr)
  71. .FirstOrDefault(p => !String.IsNullOrWhiteSpace(p));
  72. }
  73. }
  74. }

接着替换掉原始 ~/Areas/HelpPage/HelpPageConfig.cs 内的配置。

  1. //config.SetDocumentationProvider(new XmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/XmlDocument.xml")));
  2. config.SetDocumentationProvider(new MultiXmlDocumentationProvider(HttpContext.Current.Server.MapPath("~/App_Data/XmlDocument.xml"), HttpContext.Current.Server.MapPath("~/App_Data/WebAPI2PostMan.WebModel.XmlDocument.xml")));

那这里你可以选择多个文档xml放置于不同位置也可以采用将其都放置于WebAPI项目下的App_Data下。

为了方便我们在WebAPI项目下,这里指 WebAPI2PostMan,对其添加生成事件

  1. copy $(SolutionDir)WebAPI2PostMan.WebModel\App_Data\XmlDocument.xml $(ProjectDir)\App_Data\WebAPI2PostMan.WebModel.XmlDocument.xml

每次生成成功后将 WebAPI2PostMan.WebModel.XmlDocument.xml 文件拷贝到 WebAPI2PostMan项目的App_Data目录下,并更名为 WebAPI2PostMan.WebModel.XmlDocument.xml。

至此,重新生成项目,我们的描述就又回来了~

这篇文章若耐心看完会发现其实就改动几处而已,没必要花这么大篇幅来说明,但是对需要的人来说还是有一点帮助的。

为了方便,源代码依然在:https://github.com/yanghongjie/WebAPI2PostMan ,若你都看到这里了,顺手点下【推荐】吧(●'◡'●)。

WebAPI使用多个xml文件生成帮助文档(转)的更多相关文章

  1. WebAPI使用多个xml文件生成帮助文档

    一.前言 上篇有提到在WebAPI项目内,通过在Nuget里安装(Microsoft.AspNet.WebApi.HelpPage)可以根据注释生成帮助文档,查看代码实现会发现是基于解析项目生成的xm ...

  2. 【转】WebAPI使用多个xml文件生成帮助文档

    来自:http://www.it165.net/pro/html/201505/42504.html 一.前言 上篇有提到在WebAPI项目内,通过在Nuget里安装(Microsoft.AspNet ...

  3. Java 读取txt文件生成Word文档

    本文将以Java程序代码为例介绍如何读取txt文件中的内容,生成Word文档.在编辑代码前,可参考如下代码环境进行配置: IntelliJ IDEA Free Spire.Doc for Java T ...

  4. C# 读取txt文件生成Word文档

    本文将以C#程序代码为例介绍如何来读取txt文件中的内容,生成Word文档.在编辑代码前,可参考如下代码环境进行配置: Visual Studio 2017 .Net Framework 4.6.1 ...

  5. 使用apidoc根据JS文件生成接口文档

    1.安装nodejs.下载网址:http://www.nodejs.org: 2.安装apidoc.运行cmd,切换到nodejs的安装目录,在命令行输入: 1 npm install apidoc ...

  6. Chimm.Excel —— 使用Java 操作 excel 模板文件生成 excel 文档

    Chimm.Excel -- 设置模板,填充数据,就完事儿了~ _____ _ _ _____ _ / __ \ | (_) | ___| | | | / \/ |__ _ _ __ ___ _ __ ...

  7. [转]java 根据模板文件生成word文档

    链接地址:https://blog.csdn.net/ai_0922/article/details/82773466

  8. webAPI 自动生成帮助文档

    之前在项目中有用到webapi对外提供接口,发现在项目中有根据webapi的方法和注释自动生成帮助文档,还可以测试webapi方法,功能很是强大,现拿出来与大家分享一下. 先看一下生成的webapi文 ...

  9. asp.net webAPI 自动生成帮助文档并测试

    之前在项目中有用到webapi对外提供接口,发现在项目中有根据webapi的方法和注释自动生成帮助文档,还可以测试webapi方法,功能很是强大,现拿出来与大家分享一下. 先看一下生成的webapi文 ...

随机推荐

  1. Android开发笔记——常见BUG类型之内存泄露与线程安全

    本文内容来源于最近一次内部分享的总结,没来得及详细整理,见谅. 本次分享主要对内存泄露和线程安全这两个问题进行一些说明,内部代码扫描发现的BUG大致分为四类:1)空指针:2)除0:3)内存.资源泄露: ...

  2. win+Nginx+php+mysql 环境配置

     1.准备工作 (1)PHP 版本5.6.17 下载地址  PHP官网 (2)Nginx 版本1.8.0 下载地址  Nginx官网 (3)MySQL 版本5.7.10 MySQL官网 2.php的安 ...

  3. Effective Java 50 Avoid strings where other types are more appropriate

    Principle Strings are poor substitutes for other value types. Such as int, float or BigInteger. Stri ...

  4. js日期时间函数

    日期时间脚本库方法列表 Date.prototype.isLeapYear 判断闰年Date.prototype.Format 日期格式化Date.prototype.DateAdd 日期计算Date ...

  5. index merge的一次优化

    手机微博4040端口SQL优化 现象 某端口常态化延迟,通过使用pt-query-digest发现主要由于一条count(*)语句引发,具体如下: # .5s .58M rss, .84M vsz # ...

  6. 6天的巴厘岛旅行 I love Bali

    6天的巴厘岛旅游今天结束了,从第一天的踏进异国之域的新奇,到最后一天的将且回程的恋恋不舍,要记下的.不愿忘记的东西太多太多. 1.下午5点半抵达巴厘岛登巴萨国际机场,7点半才出机场,让Ricky导游和 ...

  7. Python开发之【简单计算器】

    开发一个简单的python计算器 1.实现加减乘除及括号优先级解析 2.用户输入 1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * ...

  8. 获取tomcat上properties文件的内容——方便文件存储位置的修改,解耦和

    在java web开发的时候经常会用到读取读取或存放文件,这个文件的默认路径在哪里呢?写死在程序里面显然是可以的,但这样子不利于位于,假如有一天项目从window移植到linux,或者保存文件的路径变 ...

  9. linux timezone

    首先需要了解下“UTC时间”与“本地时间”UTC时间:世界协调时间(又称世界标准时间.世界统一时间),在一般精度要求下,它与GMT(Greenwich Mean Time,格林威治标准时间)是一样的, ...

  10. [麦先生]初学Laravel框架与ThinkPHP的不同(1)

    作为一个PHP菜鸟初学Laravel框架 在学习过程中我发现了其与TP框架的不同点,由于时间问题和认识还不够完善我先写出其中几点,有错误的地方希望各位大牛斧正... 1.渲染模版方式的不同:在Lara ...