C# 在Windform程序中搭建Webapi
1. 在NuGet引用owin
Microsoft.AspNet.WebApi.Owin
Microsoft.AspNet.WebApi.OwinSelfHost
Microsoft.Owin.StaticFiles
2. 添加服务启动配置类 Startup
1 using WebapiTest.App_Start; 2 using Microsoft.Owin.FileSystems; 3 using Microsoft.Owin.StaticFiles; 4 using Owin; 5 using System; 6 using System.Collections.Generic; 7 using System.Linq; 8 using System.Text; 9 using System.Threading.Tasks; 10 using System.Web.Http; 11 12 namespace WebapiTest 13 { 14 public class Startup 15 { 16 public void Configuration(IAppBuilder appBuilder) 17 { 18 // 创建 Web API 的配置 19 var config = new HttpConfiguration(); 20 // 启用标记路由 21 config.MapHttpAttributeRoutes(); 22 // 默认的 Web API 路由 23 config.Routes.MapHttpRoute( 24 name: "DefaultApi", 25 routeTemplate: "api/{controller}/{id}", 26 defaults: new { id = RouteParameter.Optional } 27 ); 28 29 FormatterConfig.Configure(config); 30 SwaggerConfig.Register(config); 31 FilterConfig.Register(config); 32 33 var physicalFileSystem = new PhysicalFileSystem(@".\Web"); //静态网站根目录 34 var options = new FileServerOptions 35 { 36 EnableDefaultFiles = true, 37 FileSystem = physicalFileSystem 38 }; 39 options.StaticFileOptions.FileSystem = physicalFileSystem; 40 options.StaticFileOptions.ServeUnknownFileTypes = true; 41 options.StaticFileOptions.DefaultContentType = "text/plain"; 42 options.DefaultFilesOptions.DefaultFileNames = new[] { "Index.html" }; //默认页面(填写与静态网站根目录的相对路径) 43 appBuilder.UseFileServer(options); 44 45 // 将路由配置附加到 appBuilder 46 appBuilder.UseWebApi(config); 47 } 48 } 49 }
3. 添加 Controllers 目录,创建 QueryController Web服务类
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.Web.Http; 7 8 namespace WebapiTest.Controllers 9 { 10 11 public class QueryController : ApiController 12 { 13 //// GET api 14 //[HttpGet] 15 //public IHttpActionResult Json(string id) 16 //{ 17 // return Json($"hello123:{id}"); 18 //} 19 20 // GET api 21 public string Get(string id) 22 { 23 return "hello:" + id; 24 } 25 // POST api 26 public string Post([FromBody] string value) 27 { 28 return value; 29 } 30 // PUT api 31 public void Put(int id, string value) 32 { 33 } 34 // DELETE api 35 public void Delete(int id) 36 { 37 } 38 } 39 40 41 }
4. 在程序中调用如下代码启动Web服务
// 打开Web服务
var server = WebApp.Start<Startup>(url: "http://localhost:9099/");
// 停止Web服务
server.Dispose();
server = null;
5. 在生成的文件目录,创建Web文件夹,放入静态Web资源(index.html)
6. 访问Web资源
浏览器访问静态资源 http://localhost:9099/
浏览器访问WebApi http://localhost:9099/api/Query/123
7.在项目中加入:swagger,安装Swashbuckle (补充如果是.net core api请安装Sawshbuckle aspnetcore)
8、打开项目App_Start文件夹,修改SwaggerConfig.cs配置文件
1 using System.Web.Http; 2 using WebActivatorEx; 3 using WebapiTest; 4 using Swashbuckle.Application; 5 using System.Reflection; 6 using System; 7 using WebapiTest.App_Start; 8 9 [assembly: PreApplicationStartMethod(typeof(SwaggerConfig), "Register")] 10 11 namespace WebapiTest.App_Start 12 { 13 public class SwaggerConfig 14 { 15 private static string GetXmlDocumentsPath() 16 { 17 var path = 18 $"{AppDomain.CurrentDomain.BaseDirectory}\\{Assembly.GetExecutingAssembly().GetName().Name}.XML"; 19 20 21 return path; 22 } 23 24 private static string GetXmlCommentsPath() 25 { 26 var path = $"{AppDomain.CurrentDomain.BaseDirectory}\\{Assembly.GetExecutingAssembly().GetName().Name}.XML"; 27 28 //$"{AppDomain.CurrentDomain.BaseDirectory}HellerDataManagerService.XML"; 29 30 return path; 31 } 32 33 public static void Register(HttpConfiguration configuration) 34 { 35 36 var thisAssembly = typeof(SwaggerConfig).Assembly; 37 38 configuration 39 .EnableSwagger(c => 40 { 41 // By default, the service root url is inferred from the request used to access the docs. 42 // However, there may be situations (e.g. proxy and load-balanced environments) where this does not 43 // resolve correctly. You can workaround this by providing your own code to determine the root URL. 44 // 45 //c.RootUrl(req => GetRootUrlFromAppConfig()); 46 47 // If schemes are not explicitly provided in a Swagger 2.0 document, then the scheme used to access 48 // the docs is taken as the default. If your API supports multiple schemes and you want to be explicit 49 // about them, you can use the "Schemes" option as shown below. 50 // 51 //c.Schemes(new[] { "http", "https" }); 52 53 // Use "SingleApiVersion" to describe a single version API. Swagger 2.0 includes an "Info" object to 54 // hold additional metadata for an API. Version and title are required but you can also provide 55 // additional fields by chaining methods off SingleApiVersion. 56 // 57 c.SingleApiVersion("v1", "我的在线说明"); 58 c.IncludeXmlComments(GetXmlDocumentsPath()); 59 c.IncludeXmlComments(GetXmlCommentsPath()); 60 // If you want the output Swagger docs to be indented properly, enable the "PrettyPrint" option. 61 // 62 //c.PrettyPrint(); 63 64 // If your API has multiple versions, use "MultipleApiVersions" instead of "SingleApiVersion". 65 // In this case, you must provide a lambda that tells Swashbuckle which actions should be 66 // included in the docs for a given API version. Like "SingleApiVersion", each call to "Version" 67 // returns an "Info" builder so you can provide additional metadata per API version. 68 // 69 //c.MultipleApiVersions( 70 // (apiDesc, targetApiVersion) => ResolveVersionSupportByRouteConstraint(apiDesc, targetApiVersion), 71 // (vc) => 72 // { 73 // vc.Version("v2", "Swashbuckle Dummy API V2"); 74 // vc.Version("v1", "Swashbuckle Dummy API V1"); 75 // }); 76 77 // You can use "BasicAuth", "ApiKey" or "OAuth2" options to describe security schemes for the API. 78 // See https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md for more details. 79 // NOTE: These only define the schemes and need to be coupled with a corresponding "security" property 80 // at the document or operation level to indicate which schemes are required for an operation. To do this, 81 // you'll need to implement a custom IDocumentFilter and/or IOperationFilter to set these properties 82 // according to your specific authorization implementation 83 // 84 //c.BasicAuth("basic") 85 // .Description("Basic HTTP Authentication"); 86 // 87 // NOTE: You must also configure 'EnableApiKeySupport' below in the SwaggerUI section 88 //c.ApiKey("apiKey") 89 // .Description("API Key Authentication") 90 // .Name("apiKey") 91 // .In("header"); 92 // 93 //c.OAuth2("oauth2") 94 // .Description("OAuth2 Implicit Grant") 95 // .Flow("implicit") 96 // .AuthorizationUrl("http://petstore.swagger.wordnik.com/api/oauth/dialog") 97 // //.TokenUrl("https://tempuri.org/token") 98 // .Scopes(scopes => 99 // { 100 // scopes.Add("read", "Read access to protected resources"); 101 // scopes.Add("write", "Write access to protected resources"); 102 // }); 103 104 // Set this flag to omit descriptions for any actions decorated with the Obsolete attribute 105 //c.IgnoreObsoleteActions(); 106 107 // Each operation be assigned one or more tags which are then used by consumers for various reasons. 108 // For example, the swagger-ui groups operations according to the first tag of each operation. 109 // By default, this will be controller name but you can use the "GroupActionsBy" option to 110 // override with any value. 111 // 112 //c.GroupActionsBy(apiDesc => apiDesc.HttpMethod.ToString()); 113 114 // You can also specify a custom sort order for groups (as defined by "GroupActionsBy") to dictate 115 // the order in which operations are listed. For example, if the default grouping is in place 116 // (controller name) and you specify a descending alphabetic sort order, then actions from a 117 // ProductsController will be listed before those from a CustomersController. This is typically 118 // used to customize the order of groupings in the swagger-ui. 119 // 120 //c.OrderActionGroupsBy(new DescendingAlphabeticComparer()); 121 122 // If you annotate Controllers and API Types with 123 // Xml comments (http://msdn.microsoft.com/en-us/library/b2s063f7(v=vs.110).aspx), you can incorporate 124 // those comments into the generated docs and UI. You can enable this by providing the path to one or 125 // more Xml comment files. 126 // 127 //c.IncludeXmlComments(GetXmlCommentsPath()); 128 129 // Swashbuckle makes a best attempt at generating Swagger compliant JSON schemas for the various types 130 // exposed in your API. However, there may be occasions when more control of the output is needed. 131 // This is supported through the "MapType" and "SchemaFilter" options: 132 // 133 // Use the "MapType" option to override the Schema generation for a specific type. 134 // It should be noted that the resulting Schema will be placed "inline" for any applicable Operations. 135 // While Swagger 2.0 supports inline definitions for "all" Schema types, the swagger-ui tool does not. 136 // It expects "complex" Schemas to be defined separately and referenced. For this reason, you should only 137 // use the "MapType" option when the resulting Schema is a primitive or array type. If you need to alter a 138 // complex Schema, use a Schema filter. 139 // 140 //c.MapType<ProductType>(() => new Schema { type = "integer", format = "int32" }); 141 142 // If you want to post-modify "complex" Schemas once they've been generated, across the board or for a 143 // specific type, you can wire up one or more Schema filters. 144 // 145 //c.SchemaFilter<ApplySchemaVendorExtensions>(); 146 147 // In a Swagger 2.0 document, complex types are typically declared globally and referenced by unique 148 // Schema Id. By default, Swashbuckle does NOT use the full type name in Schema Ids. In most cases, this 149 // works well because it prevents the "implementation detail" of type namespaces from leaking into your 150 // Swagger docs and UI. However, if you have multiple types in your API with the same class name, you'll 151 // need to opt out of this behavior to avoid Schema Id conflicts. 152 // 153 //c.UseFullTypeNameInSchemaIds(); 154 155 // Alternatively, you can provide your own custom strategy for inferring SchemaId's for 156 // describing "complex" types in your API. 157 // 158 //c.SchemaId(t => t.FullName.Contains('`') ? t.FullName.Substring(0, t.FullName.IndexOf('`')) : t.FullName); 159 160 // Set this flag to omit schema property descriptions for any type properties decorated with the 161 // Obsolete attribute 162 //c.IgnoreObsoleteProperties(); 163 164 // In accordance with the built in JsonSerializer, Swashbuckle will, by default, describe enums as integers. 165 // You can change the serializer behavior by configuring the StringToEnumConverter globally or for a given 166 // enum type. Swashbuckle will honor this change out-of-the-box. However, if you use a different 167 // approach to serialize enums as strings, you can also force Swashbuckle to describe them as strings. 168 // 169 //c.DescribeAllEnumsAsStrings(); 170 171 // Similar to Schema filters, Swashbuckle also supports Operation and Document filters: 172 // 173 // Post-modify Operation descriptions once they've been generated by wiring up one or more 174 // Operation filters. 175 // 176 //c.OperationFilter<AddDefaultResponse>(); 177 // 178 // If you've defined an OAuth2 flow as described above, you could use a custom filter 179 // to inspect some attribute on each action and infer which (if any) OAuth2 scopes are required 180 // to execute the operation 181 // 182 //c.OperationFilter<AssignOAuth2SecurityRequirements>(); 183 184 // Post-modify the entire Swagger document by wiring up one or more Document filters. 185 // This gives full control to modify the final SwaggerDocument. You should have a good understanding of 186 // the Swagger 2.0 spec. - https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md 187 // before using this option. 188 // 189 //c.DocumentFilter<ApplyDocumentVendorExtensions>(); 190 191 // In contrast to WebApi, Swagger 2.0 does not include the query string component when mapping a URL 192 // to an action. As a result, Swashbuckle will raise an exception if it encounters multiple actions 193 // with the same path (sans query string) and HTTP method. You can workaround this by providing a 194 // custom strategy to pick a winner or merge the descriptions for the purposes of the Swagger docs 195 // 196 //c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First()); 197 198 // Wrap the default SwaggerGenerator with additional behavior (e.g. caching) or provide an 199 // alternative implementation for ISwaggerProvider with the CustomProvider option. 200 // 201 //c.CustomProvider((defaultProvider) => new CachingSwaggerProvider(defaultProvider)); 202 }) 203 .EnableSwaggerUi(c => 204 { 205 // Use the "DocumentTitle" option to change the Document title. 206 // Very helpful when you have multiple Swagger pages open, to tell them apart. 207 // 208 //c.DocumentTitle("My Swagger UI"); 209 210 // Use the "InjectStylesheet" option to enrich the UI with one or more additional CSS stylesheets. 211 // The file must be included in your project as an "Embedded Resource", and then the resource's 212 // "Logical Name" is passed to the method as shown below. 213 // 214 //c.InjectStylesheet(containingAssembly, "Swashbuckle.Dummy.SwaggerExtensions.testStyles1.css"); 215 216 // Use the "InjectJavaScript" option to invoke one or more custom JavaScripts after the swagger-ui 217 // has loaded. The file must be included in your project as an "Embedded Resource", and then the resource's 218 // "Logical Name" is passed to the method as shown above. 219 // 220 //c.InjectJavaScript(thisAssembly, "Swashbuckle.Dummy.SwaggerExtensions.testScript1.js"); 221 222 // The swagger-ui renders boolean data types as a dropdown. By default, it provides "true" and "false" 223 // strings as the possible choices. You can use this option to change these to something else, 224 // for example 0 and 1. 225 // 226 //c.BooleanValues(new[] { "0", "1" }); 227 228 // By default, swagger-ui will validate specs against swagger.io's online validator and display the result 229 // in a badge at the bottom of the page. Use these options to set a different validator URL or to disable the 230 // feature entirely. 231 //c.SetValidatorUrl("http://localhost/validator"); 232 //c.DisableValidator(); 233 234 // Use this option to control how the Operation listing is displayed. 235 // It can be set to "None" (default), "List" (shows operations for each resource), 236 // or "Full" (fully expanded: shows operations and their details). 237 // 238 //c.DocExpansion(DocExpansion.List); 239 240 // Specify which HTTP operations will have the 'Try it out!' option. An empty paramter list disables 241 // it for all operations. 242 // 243 //c.SupportedSubmitMethods("GET", "HEAD"); 244 245 // Use the CustomAsset option to provide your own version of assets used in the swagger-ui. 246 // It's typically used to instruct Swashbuckle to return your version instead of the default 247 // when a request is made for "index.html". As with all custom content, the file must be included 248 // in your project as an "Embedded Resource", and then the resource's "Logical Name" is passed to 249 // the method as shown below. 250 // 251 //c.CustomAsset("index", containingAssembly, "YourWebApiProject.SwaggerExtensions.index.html"); 252 253 // If your API has multiple versions and you've applied the MultipleApiVersions setting 254 // as described above, you can also enable a select box in the swagger-ui, that displays 255 // a discovery URL for each version. This provides a convenient way for users to browse documentation 256 // for different API versions. 257 // 258 //c.EnableDiscoveryUrlSelector(); 259 260 // If your API supports the OAuth2 Implicit flow, and you've described it correctly, according to 261 // the Swagger 2.0 specification, you can enable UI support as shown below. 262 // 263 //c.EnableOAuth2Support( 264 // clientId: "test-client-id", 265 // clientSecret: null, 266 // realm: "test-realm", 267 // appName: "Swagger UI" 268 // //additionalQueryStringParams: new Dictionary<string, string>() { { "foo", "bar" } } 269 //); 270 271 // If your API supports ApiKey, you can override the default values. 272 // "apiKeyIn" can either be "query" or "header" 273 // 274 //c.EnableApiKeySupport("apiKey", "header"); 275 276 }); 277 } 278 } 279 }
9、创建项目xml注释文档
右键项目→属性→生成→选中下方的 "XML文档文件" 然后保存
10、启动webapi,然后在接口地址后面输入 /swagger (默认是英文的,如果需要中文显示 还需要做汉化处理)
11.汉化:(可以不做)
在App_Start文件夹中添加SwaggerControllerDescProvider.cs
1 using Swashbuckle.Swagger; 2 using System; 3 using System.Collections.Concurrent; 4 using System.Collections.Generic; 5 using System.IO; 6 using System.Linq; 7 using System.Text; 8 using System.Threading.Tasks; 9 using System.Xml; 10 11 namespace WebapiTest.App_Start 12 { 13 /// <summary> 14 /// swagger显示控制器的描述 15 /// </summary> 16 public class SwaggerControllerDescProvider : ISwaggerProvider 17 { 18 private readonly ISwaggerProvider _swaggerProvider; 19 private static ConcurrentDictionary<string, SwaggerDocument> _cache = new ConcurrentDictionary<string, SwaggerDocument>(); 20 private readonly string _xml; 21 /// <summary> 22 /// 23 /// </summary> 24 /// <param name="swaggerProvider"></param> 25 /// <param name="xml">xml文档路径</param> 26 public SwaggerControllerDescProvider(ISwaggerProvider swaggerProvider, string xml) 27 { 28 _swaggerProvider = swaggerProvider; 29 _xml = xml; 30 } 31 32 public SwaggerDocument GetSwagger(string rootUrl, string apiVersion) 33 { 34 var cacheKey = string.Format("{0}_{1}", rootUrl, apiVersion); 35 SwaggerDocument srcDoc = null; 36 //只读取一次 37 if (!_cache.TryGetValue(cacheKey, out srcDoc)) 38 { 39 srcDoc = _swaggerProvider.GetSwagger(rootUrl, apiVersion); 40 41 srcDoc.vendorExtensions = new Dictionary<string, object> { { "ControllerDesc", GetControllerDesc() } }; 42 _cache.TryAdd(cacheKey, srcDoc); 43 } 44 return srcDoc; 45 } 46 47 /// <summary> 48 /// 从API文档中读取控制器描述 49 /// </summary> 50 /// <returns>所有控制器描述</returns> 51 public ConcurrentDictionary<string, string> GetControllerDesc() 52 { 53 string xmlpath = _xml; 54 ConcurrentDictionary<string, string> controllerDescDict = new ConcurrentDictionary<string, string>(); 55 if (File.Exists(xmlpath)) 56 { 57 XmlDocument xmldoc = new XmlDocument(); 58 xmldoc.Load(xmlpath); 59 string type = string.Empty, path = string.Empty, controllerName = string.Empty; 60 61 string[] arrPath; 62 int length = -1, cCount = "Controller".Length; 63 XmlNode summaryNode = null; 64 foreach (XmlNode node in xmldoc.SelectNodes("//member")) 65 { 66 type = node.Attributes["name"].Value; 67 if (type.StartsWith("T:")) 68 { 69 //控制器 70 arrPath = type.Split('.'); 71 length = arrPath.Length; 72 controllerName = arrPath[length - 1]; 73 if (controllerName.EndsWith("Controller")) 74 { 75 //获取控制器注释 76 summaryNode = node.SelectSingleNode("summary"); 77 string key = controllerName.Remove(controllerName.Length - cCount, cCount); 78 if (summaryNode != null && !string.IsNullOrEmpty(summaryNode.InnerText) && !controllerDescDict.ContainsKey(key)) 79 { 80 controllerDescDict.TryAdd(key, summaryNode.InnerText.Trim()); 81 } 82 } 83 } 84 } 85 } 86 return controllerDescDict; 87 } 88 } 89 }
(2).添加Swagger.Net和Swagger.Net.UI 程序nuget引用
(3).如果没有SwaggerNet.cs文件则添加:
1 using Swagger.Net; 2 using System; 3 using System.IO; 4 using System.Web; 5 using System.Web.Http; 6 using System.Web.Http.Description; 7 using System.Web.Routing; 8 9 [assembly: WebActivator.PreApplicationStartMethod(typeof(WebapiTest.App_Start.SwaggerNet), "PreStart")] 10 [assembly: WebActivator.PostApplicationStartMethod(typeof(WebapiTest.App_Start.SwaggerNet), "PostStart")] 11 namespace WebapiTest.App_Start 12 { 13 public static class SwaggerNet 14 { 15 public static void PreStart() 16 { 17 RouteTable.Routes.MapHttpRoute( 18 name: "SwaggerApi", 19 routeTemplate: "api/docs/{controller}", 20 defaults: new { swagger = true } 21 ); 22 } 23 24 public static void PostStart() 25 { 26 var config = GlobalConfiguration.Configuration; 27 28 config.Filters.Add(new SwaggerActionFilter()); 29 30 try 31 { 32 config.Services.Replace(typeof(IDocumentationProvider), 33 new XmlCommentDocumentationProvider(HttpContext.Current.Server.MapPath("~/bin/WebapiTest.XML"))); 34 } 35 catch (FileNotFoundException) 36 { 37 throw new Exception("Please enable \"XML documentation file\" in project properties with default (bin\\WebapiTest.XML) value or edit value in App_Start\\SwaggerNet.cs"); 38 } 39 } 40 } 41 }