"Go" webapi using multiple XML files to generate Help documents

Source: Internet
Author: User

From: http://www.it165.net/pro/html/201505/42504.html, preface

The previous article mentioned in the WEBAPI project, By installing in NuGet (Microsoft.AspNet.WebApi.HelpPage), you can generate a Help document based on comments, and see that the code implementation will show up as a data source based on the XML document generated by the parsing project. The classes needed for our project Help documentation (specifically defined request and response) are no problem with the project in the same project, but we actually work because other projects also need to reference the (Request and response), We'll pull it out. As a separate project for other references, when viewing the Help document does not give an error, but the comments and additional information will be lost because the information is our code comments and data annotations (such as [Required] is required), and is generated in the XML document information, However, because it is not in the same project, it will not be read, which causes the help document not to display our comments (the corresponding description) and additional information (whether required, default value, range, etc.).

Ii. Summary of Help documentation comments

Our comment is the description or description of the Help document, then this feature is installed helppage directly have, here in two ways.

1: When creating a project is a direct selection of the Web API, then this feature is configured when the initialization project is created.

2: When you create a project, you select Empty, the core reference chosen by the Web API is not available for this feature.

There is a part of the project code generated for mode 1 that we do not need, and we can do subtraction to delete unnecessary files.

For mode 2, you need to install helppage within NuGet, you need to cancel the configuration comments within the file ~/areas/helppage/helppageconfig.cs, depending on your needs.

and set the output in the project's generated properties, tick the XML document file, set the value and ~/areas/helppage/helppageconfig.cs

Consistent with the configuration within.

and register it in the Global.asax file Application_Start method.

Arearegistration.registerallareas ();

The Help document is now available, but it has no style. You can choose to manually copy the required CSS and JS into the areas folder. and add Files

public class bundleconfig{    //For more information about bindings, visit http://go.microsoft.com/fwlink/? linkid=301862 public    static void Registerbundles (Bundlecollection bundles)    {        bundles. ADD (New Scriptbundle (' ~/bundles/jquery '). Include (                    ' ~/areas/helppage/scripts/jquery-{version}.js '));        Use the development version of the MODERNIZR to be used for development and learning. Then, when you're        ready for//production, use the build tool on http://modernizr.com to select only the tests you want.        bundles. ADD (New Scriptbundle (' ~/bundles/modernizr '). Include (                    ' ~/areas/helppage/scripts/modernizr-* '));        Bundles. ADD (New Scriptbundle (' ~/bundles/bootstrap '). Include (                  ' ~/areas/helppage/scripts/bootstrap.js ',                  ' ~/areas/helppage/scripts/respond.js '));        Bundles. ADD (New Stylebundle (' ~/content/css '). Include (                  ' ~/areas/helppage/content/bootstrap.css ',                  ' ~/areas/helppage/content/site.css '));}    }

and register it in the Global.asax file Application_Start method.

Bundleconfig.registerbundles (Bundletable.bundles);

Finally, change ~/areas/helppage/views/shared/_layout.cshtml to

@using system@using system.web.optimization<! DOCTYPE html>
  

At this point, you will see the following document.

The corresponding routing structure is as follows

Viewing the route can find its AllowMultiple = True means that we can define multiple different routes for the same action, but it also means that the action only allows defined routing access.

such as the Get method here, when the browser can only access Http://localhost:11488/api/product/{id} in this way

Use Http://localhost:11488/api/product?id={id} will throw 405, as follows.

In order to support multiple ways we will increase the number of routes as follows.

The document then generates both routes.

There is a principle here that the same type of request and the same type of response do not allow the same route to be defined, such as httpget and the same response type.

<summary>///     Get all products///</summary>[httpget, Route (')]public ienumerable<product> get () {    return _products;} <summary>///     Gets the first three products//</summary>[httpget, Route (')]public ienumerable<product> GetTop3 ( ) {    return _products. Take (3);}

Accessing http://localhost:11488/api/product at this point will find a 500 error, prompting for matching to multiple actions, and viewing the Help document at this time will only show a matching action if you do not specify the route's Order property )。

There are some basic restrictions that can be made within a route, and we'll transform the Top3 method above to determine the top number based on the parameters passed in, and at least the first three bars.

<summary>///     Get the first few products//</summary>[httpget, Route (' Top/{count:min (3)} ')]public ienumerable< product> GetTop (int count) {    return _products. Take (3);}

At this point the access to HTTP://LOCALHOST:11488/API/PRODUCT/TOP/1 or HTTP://LOCALHOST:11488/API/PRODUCT/TOP/2 will be thrown 404

But I want direct access to the HTTP://LOCALHOST:11488/API/PRODUCT/TOP default of the first 3, then the direct access will be 405, because there is no route ("Top") to define the routing, we modified the next

<summary>///     Get the first few products//</summary>[httpget, Route (' Top/{count:min (3): int=3} ')]public ienumerable<product> GetTop (int count) {    return _products. Take (3);}

The first 3 are returned by default when accessing Http://localhost:11488/api/product/Top, but there are some definitions including the regular can see here and here.

Routed documents related to the basic of these, where there are omissions are welcomed to point out.

The next step is the request and response documentation for the single interface, and let's take a look at our separate request and response.

First look at the display of Api/product/all this interface, will be found in two categories.

Api/product This interface itself does not require any parameters, so it is none.

Put Api/product?id={id} This interface is actually all inclusive. His definition is as follows.

<summary>///     Editing products///</summary>///<param name= ' ID ' > Product code </param>///<param name= ' Request ' > edited product </param>[httpput, Route ('), route (' {ID} ')]public string Put (int ID, product request) {    var model = _products. FirstOrDefault (x = x.id.equals (Id));    if (model = null) return ' did not find the product ';    Model. Name = Request. Name;    Model. Price = Request. Price;    Model. Description = Request. Description;    return ' OK ';}

In fact, in practice we may only use get and post to do all of our operations. Therefore, it will be get only the URI Parameters and the post only shows the body Parameters.

You can see that description is our comment on the property, type is the property, and additional information is the description of the "constraint", as we will constrain the requested parameters which are required which are optional and which values have a scope of use.

Let's say we change product.

<summary>///     Products//</summary>public class product{    //<summary>///number/ </summary>    [Required] public    int Id {get; set;}    <summary>    //////</summary> [Required, MaxLength] public    string name {get; set;}    <summary>    ///Price///</summary> [Required, Range (0, 99999999)]    public Decimal price {get; set;}    <summary>//description////     </summary> public    string Description {get; set;}}

You can see that the corresponding "constraint information" is changed.

One might say that I have customized some of the constraints to be displayed, and then we define a minimum constraint minattrbute.

<summary>///min characteristic///</summary>[attributeusage (Attributetargets.property, AllowMultiple = False)] public class minattribute:validationattribute{//<summary>//min//</summary> public I    NT MinimumValue {get; set;} <summary>///constructors///</summary>//<param name= ' minimun ' ></param> public Mi    Nattribute (int minimun) {minimumvalue = Minimun; }///<summary>///Authentication logic///</summary>//<param name= ' value ' > value to be validated </param>/        <returns> whether to pass validation </returns> public override bool IsValid (object value) {int intvalue; if (value! = null && int. TryParse (value.        ToString (), out Intvalue) {return (intvalue >= minimumvalue);    } return false;    }///<summary>///Format error message///</summary>//<param name= ' name ' > property name </param> <retuRns> error message </returns> public override string Formaterrormessage (string name) {return string.    Format (' {0} minimum value {1} ', name, MinimumValue); }}

Add it to the price property and set the minimum value to 10.

<summary>///     Price//</summary>[required, Min (Ten)]public decimal prices {get; set;}

At this time through the postman to request, you will find that the validation is passed, and there is no expected error prompt. That's because we don't have the attribute to enable the validation attribute.

We customize a validatemodelattribute with the available range specified as class and method, and do not allow multiple times and add it to just the put interface.

///<summary>///Validate Model Filter///</summary>[attributeusage ( AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = False, inherited = True)]public class Validatemodelattribute: actionfilterattribute{//<summary>//action Pre-validation///</summary>//<param name= ' action    Context ' >the action context.</param> public override void OnActionExecuting (Httpactioncontext actioncontext) {if (ActionContext.ActionArguments.Any (kv = kv). Value = = null)) {actioncontext.response = ActionContext.Request.CreateErrorResponse (Httpstatuscode.badr        Equest, ' parameter cannot be null ');        } if (ActionContext.ModelState.IsValid) return; Actioncontext.response = ActionContext.Request.CreateErrorResponse (Httpstatuscode.badrequest,    Actioncontext.modelstate); }}
<summary>///     Editing products///</summary>///<param name= ' ID ' > Product code </param>///<param name= ' Request ' > edited product </param>[httpput, Route ('), route (' {ID} ')][validatemodel]public string Put (int ID, product Request) {    var model = _products. FirstOrDefault (x = x.id.equals (Id));    if (model = null) return ' did not find the product ';    Model. Name = Request. Name;    Model. Price = Request. Price;    Model. Description = Request. Description;    return ' OK ';}

This is where we use the postman request and the validation prompt appears.

But by the time we look at our help document, Price's "constraint information" leaves only required.

So I'm going to show the constraint information for the custom Minattribute. The generated code of the observation document can be found to be generated within the definition of Annotationtextgenerator in the Areas.HelpPage.ModelDescriptions.ModelDescriptionGenerator class.

That would be a good job, and I would add my own custom.

Modify this to support more data annotation attributes.private readonly idictionary<type, Func<object, string> > annotationtextgenerator = new Dictionary<type, Func<object, string>>{{typeof (RequiredAttribute), a =            > ' Required '}, {typeof (Rangeattribute), a = = {Rangeattribute range = (rangeattribute) A; Return String.Format (CultureInfo.CurrentCulture, ' range:inclusive between {0} and {1} ', Range. Minimum, Range.        Maximum);             }}, {typeof (Maxlengthattribute), a = = {Maxlengthattribute MaxLength = (Maxlengthattribute) A;        Return String.Format (CultureInfo.CurrentCulture, ' Max length: {0} ', maxlength.length);             }}, {typeof (Minlengthattribute), a = = {Minlengthattribute MinLength = (Minlengthattribute) A;        Return String.Format (CultureInfo.CurrentCulture, ' Min length: {0} ', minlength.length); }}, {typeof (Stringlengthattribute),A = {Stringlengthattribute strlength = (Stringlengthattribute) A; Return String.Format (CultureInfo.CurrentCulture, ' String length:inclusive between {0} and {1} ',        Strlength.minimumlength, strlength.maximumlength);            }}, {typeof (Datatypeattribute), a = = {Datatypeattribute DataType = (Datatypeattribute) A; Return String.Format (CultureInfo.CurrentCulture, ' Data type: {0} ', Datatype.customdatatype?? datatype.datatype.t        Ostring ()); }}, {typeof (Regularexpressionattribute), a = = {Regularexpressionattribute RegularExpression            = (Regularexpressionattribute) A; Return String.Format (CultureInfo.CurrentCulture, ' Matching regular expression pattern: {0} ',        Regularexpression.pattern);            }}, {typeof (Minattribute), a = = {Minattribute Minattribute = (Minattribute) A; Return String.Format (cultureinfo.currentculture, ' minimum value: {0} ', MinattribuTe.        MinimumValue); }    },};

Then we look at the document and our "constraint information" comes out.

This is basically what the request section is all about. Response part of the content is not much, mainly is the display of sample will have a problem, if you write a step-by-step here to see the Help document sample will have three, respectively, is

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

Here we will find that it cannot generate application/x-www-form-urlencoded because we cannot use Jquerymvcformurlencodefomatter to format our class. As for why, I did not go to look for, because besides Application/json is I need, the rest I do not need.

Interested friends can find out why. Then let's tell you what we don't need to remove here, as follows.

public static class webapiconfig{public    static void Register (httpconfiguration config)    {        //Web API Configuration and services C3/>config. Formatters.remove (config. Formatters.xmlformatter);        Web API Routing        config. Maphttpattributeroutes ();    }}

This only removes xmlformatter, because application/x-www-form-urlencoded we need it at the request, but I don't want him to show up in the document, so ...

Within the Getsample method in the Areas.HelpPage.SampleGeneration.HelpPageSampleGenerator class, the

foreach (Var formatter in formatters)

Change to

foreach (Var formatter in formatters. Where (x = X.gettype ()! = typeof (Jquerymvcformurlencodedformatter)))

Then, the document is clean, is it a neat freak ...

Iii. using multiple projects to generate an XML file to display the Help document

It's finally here, we first webapi2postman.webmodel the product as a project and referencing him, and see the documentation below.

You will find that your comment is not a description of the attribute. Before you open app_data/xmldocument.xml file comparison p did not move the roduct XML file indeed the description of the product class did not, because the Xmldocument.xml file here is the project's build profile, not in this project

The defined files are not generated within this file, and the real requirement is that we really need to define all requests and response separately in one project for other project references, perhaps unit tests may also be our encapsulated WEBAPI client (as described in the next article).

With this question found this article http://stackoverflow.com/questions/21895257/ How-can-xml-documentation-for-web-api-include-documentation-from-beyond-the-main

This article provides 3 ways to only describe what I think is reasonable, that is, we need to webapi2postman.webmodel the generated properties of the XML document file, but also to generate an XML document, while expanding a new XML document loading method

Add a class named MultiXmlDocumentationProvider.cs under Directory ~/areas/helppage/.

Using system;using system.linq;using system.reflection;using system.web.http.controllers;using System.web.http.description;using Xlobo.rechargeservice.areas.helppage.modeldescriptions;namespace xlobo.rechargeservice.areas.helppage{//<summary>a custom <see cref= ' Idocumentationprovider '/> That Reads the API documentation from a collection of XML documentation files.</summary> public class Multixmldocument Ationprovider:idocumentationprovider, Imodeldocumentationprovider {/********* * * Properties * * * //////<summary>the internal documentation providers for specific files.</summary> private        ReadOnly xmldocumentationprovider[] Providers;        /********* * * Public methods *********///<summary>construct an instance.</summary> <param name= ' paths ' >the physical paths to the XML documents.</param> public Multixmldocumentati Onprovider (params string[] paths) {this. Providers = paths. Select (p = new Xmldocumentationprovider (p)).        ToArray (); }//<summary>gets the documentation for a subject.</summary>//<param name= ' subject ' >t  He subject to document.</param> public string getdocumentation (MemberInfo subject) {return This.        Getfirstmatch (P = p.getdocumentation (subject)); }//<summary>gets the documentation for a subject.</summary>//<param name= ' subject ' >t He subject to document.</param> public string getdocumentation (Type subject) {return this.        Getfirstmatch (P = p.getdocumentation (subject)); }//<summary>gets the documentation for a subject.</summary>//<param name= ' subject ' >t            He subject to document.</param> public string getdocumentation (httpcontrollerdescriptor subject) { return this. GetfirStmatch (P = p.getdocumentation (subject)); }//<summary>gets the documentation for a subject.</summary>//<param name= ' subject ' >t            He subject to document.</param> public string getdocumentation (httpactiondescriptor subject) { return this.        Getfirstmatch (P = p.getdocumentation (subject)); }//<summary>gets the documentation for a subject.</summary>//<param name= ' subject ' >t            He subject to document.</param> public string getdocumentation (httpparameterdescriptor subject) { return this.        Getfirstmatch (P = p.getdocumentation (subject)); }//<summary>gets the documentation for a subject.</summary>//<param name= ' subject ' >t             He subject to document.</param> public string getresponsedocumentation (httpactiondescriptor subject) { return this. Getfirstmatch (p = P.getdocumentatiOn (subject)); }/********* * * Private methods *********///<summary>get The first valid result from The collection of XML documentation providers.</summary>///<param name= ' expr ' >the method to INVOKE.&L T;/param> private String Getfirstmatch (Func<xmldocumentationprovider, string> expr) {RE Turn this. Providers. Select (expr). FirstOrDefault (p =!)        String.isnullorwhitespace (p)); }    }}

Then replace the configuration within the original ~/areas/helppage/helppageconfig.cs.

Config. Setdocumentationprovider (New Xmldocumentationprovider (HttpContext.Current.Server.MapPath (' ~/app_data/ Xmldocument.xml ')); config. Setdocumentationprovider (New Multixmldocumentationprovider (HttpContext.Current.Server.MapPath (' ~/app_data/ Xmldocument.xml '), HttpContext.Current.Server.MapPath (' ~/app_data/webapi2postman.webmodel.xmldocument.xml '));

So here you can select multiple document XML to be placed in different locations or under App_Data that are placed under the WEBAPI project.

To facilitate our WEBAPI project, this refers to Webapi2postman, which adds a build event to it

Copy $ (SolutionDir) WebAPI2PostMan.WebModelApp_DataXmlDocument.xml $ (ProjectDir) App_ DataWebAPI2PostMan.WebModel.XmlDocument.xml

After each build succeeds, copy the WebAPI2PostMan.WebModel.XmlDocument.xml file to the App_Data directory of the Webapi2postman project and rename it to WebAPI2PostMan.WebModel.XmlDocument.xml.

Now, rebuild the project, and our description is back again ~

This article if the patient will see the changes in fact, there is no need to spend so much space to explain, but for the people who need a little help.

For convenience, the source code is still in: Https://github.com/yanghongjie/WebAPI2PostMan, if you have seen here, easy to click "Recommended" bar ('? ') ).

"Go" webapi using multiple XML files to generate Help documents

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.