GVKun编程网logo

asp.net-web-api – asp.net web api self hosting / owin / katana

6

在这里,我们将给大家分享关于asp.net-web-api–asp.netwebapiselfhosting/owin/katana的知识,同时也会涉及到如何更有效地ASP.NETWebAPIDemo

在这里,我们将给大家分享关于asp.net-web-api – asp.net web api self hosting / owin / katana的知识,同时也会涉及到如何更有效地ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 在线文档、ASP.NET Web API与Owin OAuth:调用与用户相关的Web API、asp.net web-api – ASP.net Web API RESTful Web服务基本身份验证、asp.net – Webapi,Webhost和Owin之间的关系的内容。

本文目录一览:

asp.net-web-api – asp.net web api self hosting / owin / katana

asp.net-web-api – asp.net web api self hosting / owin / katana

我自己主持有多个问题

自我主播Nuget

有2个nuget提供自我托管:Microsoft.AspNet.WebApi.OwinSelfHost和Microsoft.AspNet.WebApi.SelfHost,微软有没有2个自主托管的实现?或者他们是一样的?

Owin或Kitana

nuget的名字是Microsoft.AspNet.WebApi.OwinSelfHost有OWIN,但是据我看到Owin是一个接口和Kitana一个实现,实现的nuget的名称是什么?

托管在生产

我设法通过创建控制台来运行示例.但是当部署到prod时,如何部署?运行exe并继续运行控制台,不能这样做.如果有人关闭那个控制台怎么办?所以应该作为Windows服务的一部分托管?还是有其他办法吗?

解决方法

NuGet包 here清楚地说明了这一点.

Microsoft ASP.NET Web API 2.2 Self Host 5.2.2 This is a legacy package
for hosting ASP.NET Web API within your own process (outside of IIS).
Please use the Microsoft.AspNet.WebApi.OwinSelfHost package for new
projects.

无论如何,SelfHost是旧的,基于WCF堆栈. OwinSelfHost是新的,基于Katana(名称是Katana而不是Kitana,BTW).

对于生产托管,控制台应用程序不实用.您将需要创建Windows服务.看看this.

ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 在线文档

ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 在线文档

新建Web API工程

 

选Empty,勾选Web API,不要选择Web API,那样会把MVC勾上,这里不需要MVC

Web API工程属性

 XML文件用于生成在线文档

  新建Windows服务作为Web API的宿主

 

WebApiHost工程属性

 控制台应用程序方便调试

 Windows服务安装Microsoft.AspNet.WebApi.OwinSelfHost

 

工程WebApiDemo需要引用Microsoft.Owin.dll

 WebApiDemo安装Swashbuckle

 应用程序入口

using System;
 System.Collections.Generic;
 System.Diagnostics;
 System.Linq;
 System.ServiceProcess;
 System.Text;
 System.Threading.Tasks;

namespace WebApiHost
{
    static class Program
    {
        /// <summary>
        /// 应用程序的主入口点。
        </summary>
        void Main(string[] args)
        {
            RunDebug();
            StartService();
        }

        private void StartService()
        {
            ServiceBase[] ServicesToRun;
            ServicesToRun = new ServiceBase[]
            {
                 WebApiHostService()
            };
            ServiceBase.Run(ServicesToRun);
        }

        [Conditional("DEBUG")]
         RunDebug()
        {
             WebApiHostService().Start();
            Console.WriteLine(启动成功);
            Console.ReadLine();
        }
    }
}
View Code

 启动Web API服务

 Microsoft.Owin.Hosting;
 System.ComponentModel;
 System.Configuration;
 System.Data;
 System.Threading;
 System.Threading.Tasks;
 Utils;

public partial  WebApiHostService : ServiceBase
    {
        #region 构造函数
        public WebApiHostService()
        {
            InitializeComponent();
        }
        #endregion

        #region OnStart 启动服务
        protected override void OnStart([] args)
        {
            int port = int.Parse(ConfigurationManager.AppSettings[WebApiServicePort]);
            StartOptions options =  StartOptions();
            options.Urls.Add(http://127.0.0.1:" + port);
            options.Urls.Add(http://localhost:http://+: port);
            WebApp.Start<Startup>(options);
            LogUtil.Log(Web API 服务 启动成功);
        }
        #region OnStop 停止服务
         OnStop()
        {
            LogUtil.Log(Web API 服务 停止成功);
            Thread.Sleep(100); //等待一会,待日志写入文件
        }
        #region Start 启动服务
         Start()
        {
            OnStart(null#endregion

    }
}
View Code

 配置Web API路由、拦截器以及初始化Swagger在线文档

 Owin;
 WebApiDemo;
 System.Web.Http;

 Startup
    {
         Configuration(IAppBuilder appBuilder)
        {
            HttpConfiguration config =  HttpConfiguration();

            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: DefaultApi,routeTemplate: api/{controller}/{id}new { id = RouteParameter.Optional }
            );

            config.Filters.Add( MyExceptionFilter());
            config.Filters.Add( MyActionFilter());

            SwaggerConfig.Register(config);

            appBuilder.UseWebApi(config);
        }
    }
}
View Code

接口实现

1、继承ApiController

2、RoutePrefix设置路由前缀

3、SwaggerResponse用于生成在线文档描述

 Models;
 Swashbuckle.Swagger.Annotations;
 System.ComponentModel.DataAnnotations;
 System.Net;
 System.Net.Http;
 System.Web.Http;
 WebApiDemo.Controllers
{
    <summary>
     测试接口
    </summary>
    [RoutePrefix(api/test)]
     TestController : ApiController
    {
        #region TestGet 测试GET请求
         测试GET请求
        </summary>
        <param name="val">测试参数</param>
        [HttpGet]
        [Route(TestGet)]
        [SwaggerResponse(HttpStatusCode.OK,返回JSON",typeof(JsonListResult<TestGetResult>))]
        public HttpResponseMessage TestGet( val)
        {
            List<TestGetResult> list = new List<TestGetResult>();

            for (int i = 1; i <= 10; i++)
            {
                TestGetResult item =  TestGetResult();
                item.testValue1 = i.ToString();
                item.testValue2 = i;
                item.testValue3 = 这是传入参数: val;
                list.Add(item);
            }

            var jsonResult = new JsonListResult<TestGetResult>(list,list.Count);

            return ApiHelper.ToJson(jsonResult);
        }
        #region TestPost 测试POST请求
         测试POST请求
        <param name="data">POST数据        [HttpPost]
        [Route(TestPosttypeof(JsonResult<CommonSubmitResult> HttpResponseMessage TestPost([FromBody] TestPostData data)
        {
            JsonResult jsonResult = ;

            if (data == null) return ApiHelper.ToJson(new JsonResult(请检查参数格式string msg = 操作成功,这是您传入的参数: data.testArg;

            jsonResult = new JsonResult<CommonSubmitResult>( CommonSubmitResult()
            {
                msg = msg,id = 1
            });

            

    }

    #region 数据类
     TestGet接口返回结果
    </summary>
     TestGetResult
    {
         测试数据1
        string testValue1 { get; set; }

         测试数据2
        int testValue2 {  测试数据3
        string testValue3 { ; }
    }

     TestPost接口参数
    </summary>
    [MyValidate]
     TestPostData
    {
         测试参数1
                [required]
        string testArg {  测试日期参数
                [required]
        [DateTime(Format = yyyyMMddHHmmssstring testTime {  TestPost接口返回结果
     TestPostResult
    {
        ; }
    }
    

}
View Code

MyValidate属性表示该数据需要校验

required必填校验
DateTime日期输入格式校验

辅助类ApiHelper.cs

 Newtonsoft.Json;
 System.Web;

 Utils
{
     ApiHelper
    {
        static HttpResponseMessage ToJson(object obj)
        {
            string str = JsonConvert.SerializeObject(obj);
            HttpResponseMessage result = new HttpResponseMessage { Content = new StringContent(str,Encoding.UTF8,application/json) };
             result;
        }

    }
}
View Code

 辅助类ServiceHelper.cs

 System.Collections.Concurrent;
 Utils
{
     服务帮助类
     ServiceHelper
    {
        static ConcurrentDictionary<Type,1)">object> _dict = new ConcurrentDictionary<Type,1)">object>();

         获取对象
        static T Get<T>() where T : ()
        {
            Type type = typeof(T);
            object obj = _dict.GetorAdd(type,(key) =>  T());

             (T)obj;
        }

        static T Get<T>(Func<T> func)  func());

             (T)obj;
        }

    }
}
View Code

 JsonResult类

 Models
{
     Json返回
     JsonResult
    {
         接口是否成功
        virtual bool success {  结果编码
        virtual ResultCode resultCode {  接口错误信息
        string errorMsg {  记录总数(可空类型)
        int? total {  默认构造函数
         JsonResult() { }

         接口失败返回数据
        public JsonResult( errorMsg,ResultCode resultCode)
        {
            this.success = false;
            this.resultCode = resultCode;
            this.errorMsg = errorMsg;
        }

    }

    class JsonResult<T> : JsonResult
    {
        /* 子类重写属性解决JSON序列化属性顺序问题 */

        override ResultCode resultCode {  数据
        public T info {  接口成功返回数据
         JsonResult(T info)
        {
            true ResultCode.OK;
            this.info = info;
            this.total = ;
        }

    }

    class JsonListResult<T>public List<T> info { public JsonListResult(List<T> list,1)">int total)
        {
             list;
            this.total = total;
        }

        public JsonListResult(List<T> list,PagerModel pager)
        {
             pager.totalRows;
        }

    }

     结果编码
    enum ResultCode
    {
        OK = 20010011002用户不存在 = 11011102120113011302错误 = 1401错误 = 1501
    }

     通用返回数据
     CommonSubmitResult
    {
         提示信息
        string msg {  记录ID
        string id {  CommonMsgResult
    {
        ; }
    }
}
View Code

异常拦截器

异常在这里统一处理,接口方法中不需要再加try catch

 System.Web;
 System.Web.Http.Filters;
 WebApiDemo
{
     MyExceptionFilter : ExceptionFilterattribute
    {
        重写基类的异常处理方法
         OnException(HttpActionExecutedContext actionExecutedContext)
        {
            var result = 拦截到异常: actionExecutedContext.Exception.Message,ResultCode.服务器内部错误);

            LogUtil.Error(actionExecutedContext.Exception);

            actionExecutedContext.Response = ApiHelper.ToJson(result);

            base.OnException(actionExecutedContext);
        }
    }
}
View Code

方法拦截器

1、在拦截器中校验证token
2、在拦截器中校验POST和GET参数
3、在拦截器中写操作日志

 Microsoft.Owin;
 Swashbuckle.Swagger;
 System.Collections.ObjectModel;
 System.Reflection;
 System.Web.Http.Controllers;
 WebApiDemo
{
     拦截器
     MyActionFilter : ActionFilterattribute
    {
        #region 变量
        private Dictionary<string,1)">string> _dictActionDesc = ServiceHelper.Get<Dictionary<string>>(() => XmlUtil.GetActionDesc());
        #region OnActionExecuting 执行方法前
         执行方法前
         OnActionExecuting(HttpActionContext actionContext)
        {
            .OnActionExecuting(actionContext);

            token验证
            Collection<AllowAnonymousAttribute> attributes = actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>();
            if (attributes.Count == 0)
            {
                IEnumerable<string> value;
                if (actionContext.Request.Headers.TryGetValues(tokenout value))
                {
                    string token = value.ToArray()[];

                    if (false) todo:token验证
                    {
                        actionContext.Response = ApiHelper.ToJson(token不匹配或已过期;
                    }
                }
                else
                {
                    actionContext.Response = ApiHelper.ToJson(请求头中不存在token;
                }
            }

            post参数验证
            if (actionContext.Request.Method == HttpMethod.Post)
            {
                foreach (string key in actionContext.ActionArguments.Keys)
                {
                    object value = actionContext.ActionArguments[key];
                    if (value != )
                    {
                        if (value.GetType().GetCustomAttributes(typeof(MyValidateAttribute),1)">false).Length > )
                        {
                            string errMsg = ;
                            if (!ValidatePropertyUtil.Validate(value,1)"> errMsg))
                            {
                                JsonResult jsonResult =  JsonResult(errMsg,ResultCode.参数不正确);
                                actionContext.Response = ApiHelper.ToJson(jsonResult);
                                ;
                            }
                        }
                    }
                }
            }

            get参数验证
             HttpMethod.Get)
            {
                if (key == pageif ((int)value <= )
                            {
                                JsonResult jsonResult = page必须大于0;
                            }
                        }

                        pageSizeint)value > 10000pageSize大于10000,请分页查询;
                            }
                        }
                    }
                }
            }
        }
        #region OnActionExecutedAsync 执行方法后
         执行方法后
        override Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext,CancellationToken cancellationToken)
        {
            return Task.Factory.StartNew(async () =>
            {
                try
                {
                    Type controllerType = actionExecutedContext.ActionContext.ControllerContext.Controller.GetType();
                    MethodInfo methodInfo = controllerType.getmethod(actionExecutedContext.ActionContext.ActionDescriptor.ActionName);

                    string action = controllerType.FullName + . methodInfo.Name;

                    if (_dictActionDesc.ContainsKey(action))
                    {
                        string jsonResult = ;
                        List<string> paramList = new List<();
                        string param = .Empty;
                        if (actionExecutedContext.Request.Method == HttpMethod.Post)
                        {
                             actionExecutedContext.ActionContext.ActionArguments.Keys)
                            {
                                 actionExecutedContext.ActionContext.ActionArguments[key];
                                null && value as HttpRequestMessage == )
                                {
                                    paramList.Add(JsonConvert.SerializeObject(value));
                                }
                            }
                            param = string.Join(aramList);

                            if (actionExecutedContext.Exception == )
                            {
                                byte[] bArr = await actionExecutedContext.ActionContext.Response.Content.ReadAsByteArrayAsync();
                                jsonResult = Encoding.UTF8.GetString(bArr);
                            }
                            
                            {
                                JsonResult jr = new JsonResult(actionExecutedContext.Exception.Message + \r\n actionExecutedContext.Exception.StackTrace,ResultCode.服务器内部错误);
                                jsonResult = JsonConvert.SerializeObject(jr);
                            }
                        }
                        
                        {
                            )
                                {
                                    paramList.Add(key + = value.ToString());
                                }
                                
                                {
                                    paramList.Add(key + );
                                }
                            }
                            param = &if (actionExecutedContext.ActionContext.Response.Content is StringContent)
                                {
                                     actionExecutedContext.ActionContext.Response.Content.ReadAsByteArrayAsync();
                                    jsonResult = Encoding.UTF8.GetString(bArr);
                                }
                                
                                {
                                    jsonResult = JsonConvert.SerializeObject(new JsonResult<object>());
                                }
                            }
                             JsonConvert.SerializeObject(jr);
                            }
                        }

                        string ip = ;
                        if (actionExecutedContext.Request.Properties.ContainsKey(MS_OwinContext))
                        {
                            OwinContext owinContext = actionExecutedContext.Request.Properties["] as OwinContext;
                            if (owinContext != 
                                {
                                    ip = owinContext.Request.RemoteIpAddress;
                                }
                                catch { }
                            }
                        }

                        todo:写操作日志
                        
                        ServiceHelper.Get<SysOperLogDal>().Log(action,//方法名
                            actionExecutedContext.Request.Method,//请求类型
                            _dictActionDesc[action],//方法注释
                            ip,//IP
                            actionExecutedContext.Request.RequestUri.LocalPath,//URL
                            param,//请求参数
                            jsonResult); //操作结果
                        */
                    }
                }
                 (Exception ex)
                {
                    LogUtil.Error(ex,1)">MyActionFilter OnActionExecutedAsync 写操作日志出错);
                }
            });
        }
        

    }
}
View Code

参数校验工具类

这里只做了必填和日期校验,且字段类型只是基础类型,有待完善

 System.Globalization;
 字段属性验证工具类
     ValidatePropertyUtil
    {
         验证数据 
         true:验证通过 false 验证不通过
        数据</param>
        <param name="errMsg">错误信息</param>
        bool Validate(object data,1)">out  errMsg)
        {
            PropertyInfo[] propertyInfoList = data.GetType().GetProperties();
            foreach (PropertyInfo propertyInfo  propertyInfoList)
            {
                if (propertyInfo.GetCustomAttributes(typeof(requiredAttribute),1)">)
                {
                     propertyInfo.GetValue(data);
                    if (value == )
                    {
                        errMsg = 属性 " + propertyInfo.Name +  必填return ;
                    }
                }

                object[] attrArr = propertyInfo.GetCustomAttributes(typeof(DateTimeAttribute),1)">);
                if (attrArr.Length > )
                {
                    DateTimeAttribute attr = attrArr[0]  DateTimeAttribute;
                     是日期时间格式,格式: attr.Format;
                        ;
                    }
                    
                    {
                        DateTime dt;
                        if (!DateTime.TryParseExact(value.ToString(),attr.Format,CultureInfo.InvariantCulture,DateTimeStyles.None,1)"> dt))
                        {
                            errMsg =  attr.Format;
                            ;
                        }
                    }
                }
            }

            errMsg = ;
        }
    }
}
View Code

swagger.js

复制到输出目录:不复制

生成操作:嵌入的资源

var SwaggerTranslator = (function () {
    定时执行检测是否转换成中文,最多执行500次  即500*50/1000=25s
    var _iexcute = 0;

    var _lock = 中文语言包
    var _words = {
        "Warning: Deprecated": "警告:已过时"arameters": "参数"arameter": "参数"arameter Type": "参数类型"显示/隐藏"显示操作"用户名"获取资源"获取资源列表"显示 Swagger Petstore 示例 Apis"information. Rendering Swagger UI": "已加载资源信息。正在渲染Swagger UI"arameter value": "点击设置参数"
    };

    定时执行转换
    var _translator2Cn =  () {
        if ($("#resources_container .resource").length > 0) {
            _tryTranslate();
        }

        if ($("#explore").text() == "Explore" && _iexcute < 500) {
            _iexcute++;
            setTimeout(_translator2Cn,50);
        }
    };

    设置控制器注释
    var _setControllerSummary = if (!_lock) {
            _lock = ;
            $.ajax({
                type: "get""#input_baseUrl").val(),dataType: "json" (data) {
                    var summaryDict = data.ControllerDesc;
                    var id,controllerName,strSummary;
                    $("#resources_container .resource").each( (i,item) {
                        id = $(item).attr("id");
                         (id) {
                            controllerName = id.substring(9);
                            strSummary = summaryDict[controllerName];
                             (strSummary) {
                                $(item).children(".heading").children(".options").prepend('<lititle="' + strSummary + '">' + strSummary + '</li>');
                            }
                        }
                    });

                    setTimeout( () {
                        _lock = ;
                    },100);
                }
            });
        }
    };

    尝试将英文转换成中文
    var _tryTranslate =  () {
        $('[data-sw-translate]').each( () {
            $(this).html(_getLangDesc($(this).html()));
            $(this).val(_getLangDesc($().val()));
            $(this).attr('title',_getLangDesc($(this).attr('title')));
        });
    };

    var _getLangDesc =  (word) {
        return _words[$.trim(word)] !== undefined ? _words[$.trim(word)] : word;
    };

     {
        translate:  () {
            document.title = "API描述文档";
            $('body').append('<style type="text/css">.controller-summary{color:#10a54a !important;word-break:keep-all;white-space:Nowrap;overflow:hidden;text-overflow:ellipsis;max-width:250px;text-align:right;cursor:default;} </style>');
            $("#logo").html("接口描述").attr("href","/swagger/ui/index");
            设置控制器描述
            _setControllerSummary();
            _translator2Cn();
        }
    };
})();

执行转换
SwaggerTranslator.translate();
View Code

CachingSwaggerProvider.cs

 System.IO;
 System.Xml;

 用于汉化Swagger
     CachingSwaggerProvider : ISwaggerProvider
    {
        static ConcurrentDictionary<new ConcurrentDictionary<();

        readonly ISwaggerProvider _swaggerProvider;

         构造函数
         CachingSwaggerProvider(ISwaggerProvider swaggerProvider)
        {
            _swaggerProvider = swaggerProvider;
        }

         GetSwagger
        public SwaggerDocument GetSwagger(string rootUrl,1)"> apiVersion)
        {
            var cacheKey = string.Format({0}_{1};
                只读取一次
                if (!_cache.TryGetValue(cacheKey,1)"> srcDoc))
                {
                    srcDoc = _swaggerProvider.GetSwagger(rootUrl,apiVersion);

                    srcDoc.vendorExtensions = new Dictionary<object> { { ControllerDesc srcDoc;
            }
            
            {
                SwaggerDocument doc =  SwaggerDocument();
                doc.info =  Info();
                doc.info.title = 接口不存在 doc;
            }
        }

         从api文档中读取控制器描述
        <returns>所有控制器描述</returns>
         GetControllerDesc()
        {
            string xmlpath = {0}/{1}.XML(SwaggerConfig).Assembly.GetName().Name);
            ConcurrentDictionary<string> controllerDescDict =  (File.Exists(xmlpath))
            {
                XmlDocument xmldoc =  XmlDocument();
                xmldoc.Load(xmlpath);
                string type = string.Empty,path = .Empty;

                [] arrPath;
                int length = -1,cCount = Controller.Length;
                XmlNode summaryNode = 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())
                        {
                            获取控制器注释
                            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());
                            }
                        }
                    }
                }
            }
             controllerDescDict;
        }

    }
}
View Code

SwaggerOperationFilter.cs

文件上传与token参数

 System.Web.Http.Description;

 SwaggerOperationFilter : IOperationFilter
    {
         Apply(Operation operation,SchemaRegistry schemaRegistry,ApiDescription apiDescription)
        {
            if (operation.parameters == null) operation.parameters = new List<Parameter>if (apiDescription.RelativePath.Contains(/UploadFile))
            {
                operation.parameters.RemoveAt();

                operation.parameters.Add( Parameter
                {
                    name = folderformData文件夹required = string
                });

                operation.parameters.Add(file文件
                });

                operation.consumes.Add(multipart/form-data);
            }

            Collection<AllowAnonymousAttribute> attributes = apiDescription.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>)
            {
                operation.parameters.Insert(0,1)">new Parameter { name = headerTokenrequired = true,type =  });
            }
        }
    }
}
View Code

SwaggerConfig.cs

 Swashbuckle.Application;
 System.Web;

[assembly: PreApplicationStartMethod(typeof(SwaggerConfig),1)">Register)]

 SwaggerConfig
    {
         Register(HttpConfiguration config)
        {
            var thisAssembly = (SwaggerConfig).Assembly;

            config
                .EnableSwagger(c =>
                    {
                         By default,the service root url is inferred from the request used to access the docs.
                         However,there may be situations (e.g. proxy and load-balanced environments) where this does not
                         resolve correctly. You can workaround this by providing your own code to determine the root URL.
                        //
                        c.RootUrl(req => GetRootUrlFromAppConfig());

                         If schemes are not explicitly provided in a Swagger 2.0 document,then the scheme used to access
                         the docs is taken as the default. If your API supports multiple schemes and you want to be explicit
                         about them,you can use the "Schemes" option as shown below.
                        c.Schemes(new[] { "http","https" });

                         Use "SingleApiVersion" to describe a single version API. Swagger 2.0 includes an "Info" object to
                         hold additional Metadata for an API. Version and title are required but you can also provide
                         additional fields by chaining methods off SingleApiVersion.
                        //
                        c.SingleApiVersion(v1WebApiDemo 测试接口文档);

                        c.OperationFilter<SwaggerOperationFilter>(); 添加过滤器,增加Token令牌验证

                        c.IncludeXmlComments(Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory,1)">@"WebApiDemo.XML));

                        c.CustomProvider((defaultProvider) => new CachingSwaggerProvider(defaultProvider)); 汉化Swagger两步:第一步

                         If you want the output Swagger docs to be indented properly,enable the "PrettyPrint" option.
                        c.PrettyPrint();

                         If your API has multiple versions,use "MultipleApiVersions" instead of "SingleApiVersion".
                         In this case,you must provide a lambda that tells Swashbuckle which actions should be
                         included in the docs for a given API version. Like "SingleApiVersion",each call to "Version"
                         returns an "Info" builder so you can provide additional Metadata per API version.
                        c.MultipleApiVersions(
                            (apiDesc,targetApiVersion) => ResolveVersionSupportByRouteConstraint(apiDesc,targetApiVersion),    (vc) =>
                            {
                                vc.Version("v2","Swashbuckle Dummy API V2");
                                vc.Version("v1","Swashbuckle Dummy API V1");
                            });

                         You can use "BasicAuth","ApiKey" or "OAuth2" options to describe security schemes for the API.
                         See https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md for more details.
                         NOTE: These only define the schemes and need to be coupled with a corresponding "security" property
                         at the document or operation level to indicate which schemes are required for an operation. To do this,1)"> you'll need to implement a custom IDocumentFilter and/or IOperationFilter to set these properties
                         according to your specific authorization implementation
                        c.BasicAuth("basic")
                            .Description("Basic HTTP Authentication");
                         NOTE: You must also configure 'EnableApiKeySupport' below in the SwaggerUI section
                        c.ApiKey("apiKey")
                            .Description("API Key Authentication")
                            .Name("apiKey")
                            .In("header");
                        c.OAuth2("oauth2")
                            .Description("OAuth2 Implicit Grant")
                            .Flow("implicit")
                            .AuthorizationUrl("http://petstore.swagger.wordnik.com/apI/Oauth/dialog")
                        //    .TokenUrl("https://tempuri.org/token    .Scopes(scopes =>
                                scopes.Add("read","Read access to protected resources");
                                scopes.Add("write","Write access to protected resources");
                         Set this flag to omit descriptions for any actions decorated with the Obsolete attribute
                        c.IgnoreObsoleteActions();

                         Each operation be assigned one or more tags which are then used by consumers for varIoUs reasons.
                         For example,the swagger-ui groups operations according to the first tag of each operation.
                         override with any value.
                        c.GroupActionsBy(apiDesc => apiDesc.HttpMethod.ToString());

                         You can also specify a custom sort order for groups (as defined by "GroupActionsBy") to dictate
                         the order in which operations are listed. For example,if the default grouping is in place
                         (controller name) and you specify a descending alphabetic sort order,then actions from a
                         ProductsController will be listed before those from a CustomersController. This is typically
                         used to customize the order of groupings in the swagger-ui.
                        c.OrderActionGroupsBy(new DescendingalphabeticComparer());

                         If you annotate Controllers and API Types with
                         Xml comments (http://msdn.microsoft.com/en-us/library/b2s063f7(v=vs.110).aspx),you can incorporate
                         those comments into the generated docs and UI. You can enable this by providing the path to one or
                         more Xml comment files.
                        c.IncludeXmlComments(GetXmlCommentsPath());

                         Swashbuckle makes a best attempt at generating Swagger compliant JSON schemas for the varIoUs types
                         exposed in your API. However,there may be occasions when more control of the output is needed.
                         This is supported through the "MapType" and "SchemaFilter" options:
                         Use the "MapType" option to override the Schema generation for a specific type.
                         It should be noted that the resulting Schema will be placed "inline" for any applicable Operations.
                         While Swagger 2.0 supports inline deFinitions for "all" Schema types,the swagger-ui tool does not.
                         It expects "complex" Schemas to be defined separately and referenced. For this reason,you should only
                         use the "MapType" option when the resulting Schema is a primitive or array type. If you need to alter a
                         complex Schema,use a Schema filter.
                        c.MapType<ProductType>(() => new Schema { type = "integer",format = "int32" });

                         If you want to post-modify "complex" Schemas once they've been generated,across the board or for a
                         specific type,you can wire up one or more Schema filters.
                        c.SchemaFilter<ApplySchemavendorExtensions>();

                         In a Swagger 2.0 document,complex types are typically declared globally and referenced by unique
                         Schema Id. By default,Swashbuckle does NOT use the full type name in Schema Ids. In most cases,this
                         works well because it prevents the "implementation detail" of type namespaces from leaking into your
                         Swagger docs and UI. However,if you have multiple types in your API with the same class name,you'll
                         need to opt out of this behavior to avoid Schema Id conflicts.
                        c.UseFullTypeNameInSchemaIds();

                         Alternatively,you can provide your own custom strategy for inferring SchemaId's for
                         describing "complex" types in your API.
                        c.SchemaId(t => t.FullName.Contains('`') ? t.FullName.Substring(0,t.FullName.IndexOf('`')) : t.FullName);

                         Set this flag to omit schema property descriptions for any type properties decorated with the
                         Obsolete attribute
                        c.IgnoreObsoleteProperties();

                         In accordance with the built in JsonSerializer,Swashbuckle will,by default,describe enums as integers.
                         You can change the serializer behavior by configuring the StringToEnumConverter globally or for a given
                         enum type. Swashbuckle will honor this change out-of-the-Box. However,if you use a different
                         approach to serialize enums as strings,you can also force Swashbuckle to describe them as strings.
                        c.DescribeAllEnumsAsstrings();

                         Similar to Schema filters,Swashbuckle also supports Operation and Document filters:
                         Post-modify Operation descriptions once they've been generated by wiring up one or more
                         Operation filters.
                        c.OperationFilter<AddDefaultResponse>();
                         If you've defined an OAuth2 flow as described above,you Could use a custom filter
                         to inspect some attribute on each action and infer which (if any) OAuth2 scopes are required
                         to execute the operation
                        c.OperationFilter<AssignOAuth2SecurityRequirements>();

                         Post-modify the entire Swagger document by wiring up one or more Document filters.
                         This gives full control to modify the final SwaggerDocument. You should have a good understanding of
                         the Swagger 2.0 spec. - https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md
                         before using this option.
                        c.DocumentFilter<ApplyDocumentvendorExtensions>();

                         In contrast to WebApi,Swagger 2.0 does not include the query string component when mapping a URL
                         to an action. As a result,Swashbuckle will raise an exception if it encounters multiple actions
                         with the same path (sans query string) and HTTP method. You can workaround this by providing a
                         custom strategy to pick a winner or merge the descriptions for the purposes of the Swagger docs
                        c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());

                         Wrap the default SwaggerGenerator with additional behavior (e.g. caching) or provide an
                         alternative implementation for ISwaggerProvider with the CustomProvider option.
                        c.CustomProvider((defaultProvider) => new CachingSwaggerProvider(defaultProvider));
                    })
                .EnableSwaggerUi(c => Use the "DocumentTitle" option to change the Document title.
                         Very helpful when you have multiple Swagger pages open,to tell them apart.
                        c.DocumentTitle("My Swagger UI");

                         Use the "InjectStylesheet" option to enrich the UI with one or more additional CSS stylesheets.
                         The file must be included in your project as an "Embedded Resource",and then the resource's
                         "Logical Name" is passed to the method as shown below.
                        c.InjectStylesheet(containingAssembly,"Swashbuckle.Dummy.SwaggerExtensions.testStyles1.css");

                         Use the "InjectJavaScript" option to invoke one or more custom JavaScripts after the swagger-ui
                         has loaded. The file must be included in your project as an "Embedded Resource",1)"> "Logical Name" is passed to the method as shown above.
                        c.InjectJavaScript(thisAssembly,"Swashbuckle.Dummy.SwaggerExtensions.testScript1.js");

                         The swagger-ui renders boolean data types as a dropdown. By default,it provides "true" and "false"
                         strings as the possible choices. You can use this option to change these to something else,1)"> for example 0 and 1.
                        c.BooleanValues(new[] { "0","1" });

                        pecs against swagger.io's online validator and display the result
                         in a badge at the bottom of the page. Use these options to set a different validator URL or to disable the
                         feature entirely.
                        c.SetValidatorUrl("http://localhost/validator");
                        c.disableValidator();

                         Use this option to control how the Operation listing is displayed.
                         It can be set to "None" (default),"List" (shows operations for each resource),1)"> or "Full" (fully expanded: shows operations and their details).
                        c.DocExpansion(DocExpansion.List);

                         Specify which HTTP operations will have the 'Try it out!' option. An empty paramter list disables
                         it for all operations.
                        c.SupportedSubmitMethods("GET","HEAD");

                         Use the CustomAsset option to provide your own version of assets used in the swagger-ui.
                         It's typically used to instruct Swashbuckle to return your version instead of the default
                         when a request is made for "index.html". As with all custom content,the file must be included
                         in your project as an "Embedded Resource",and then the resource's "Logical Name" is passed to
                         the method as shown below.
                        c.CustomAsset("index",containingAssembly,"YourWebApiProject.SwaggerExtensions.index.html");

                         If your API has multiple versions and you've applied the MultipleApiVersions setting
                         as described above,you can also enable a select Box in the swagger-ui,that displays
                         a discovery URL for each version. This provides a convenient way for users to browse documentation
                         for different API versions.
                        c.EnablediscoveryUrlSelector();

                         If your API supports the OAuth2 Implicit flow,and you've described it correctly,according to
                         the Swagger 2.0 specification,you can enable UI support as shown below.
                        c.EnableOAuth2Support(
                            clientId: "test-client-id",1)">    clientSecret: null,1)">    realm: "test-realm",1)">    appName: "Swagger UI"
                        additionalQueryStringParams: new Dictionary<string,string>() { { "foo","bar" } }
                        );

                         If your API supports ApiKey,you can override the default values.
                         "apiKeyIn" can either be "query" or "header"
                        c.EnableApiKeySupport("apiKey","header");

                        c.InjectJavaScript(thisAssembly,1)">WebApiDemo.Swagger.swagger.js"); 汉化Swagger两步:第二步
                    });
        }
    }
}
View Code

辅助类XmlUtil.cs

 XML工具类
     XmlUtil
    {
         从XML读取注释
        <returns></returns>
        static Dictionary< GetActionDesc()
        {
            Dictionary<string> result = string xmlPath = (XmlUtil).Assembly.GetName().Name);
             (File.Exists(xmlPath))
            {
                XmlDocument xmlDoc =  XmlDocument();
                xmlDoc.Load(xmlPath);

                XmlNode summaryNode; string type; string desc; int pos;  key;
                in xmlDoc.SelectNodes())
                {
                    type = type = node.Attributes[M:PrisonWebApi.Controllers))
                    {
                        pos = type.IndexOf((if (pos == -1) pos = type.Length;
                        key = type.Substring(2,pos - 2);
                        summaryNode = node.SelectSingleNode();
                        desc = summaryNode.InnerText.Trim();
                        result.Add(key,desc);
                    }
                }
            }

             result;
        }
    }
}
View Code

WebApiHost工程的App.config

WebApiDemo工程的Global.asax.cs和Web.config文件没有用了 

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  startup>
    supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
  </appSettings<!--Web API 服务端口号-->
    add key="WebApiServicePort" value="8500" >
>
View Code

 在线文档截图

 

ASP.NET Web API与Owin OAuth:调用与用户相关的Web API

ASP.NET Web API与Owin OAuth:调用与用户相关的Web API

在前一篇博文中,我们通过以 OAuth 的 Client Credential Grant 授权方式(只验证调用客户端,不验证登录用户)拿到的 Access Token ,成功调用了与用户无关的 Web API。

在这篇博文中,我们将以 OAuth 的 Resource Owner Password Credentials Grant 的授权方式( grant_type=password )获取 Access Token,并以这个 Token 调用与用户相关的 Web API。

对应的应用场景是:为自家的网站开发手机 App(非第三方 App),只需用户在 App 上登录,无需用户对 App 所能访问的数据进行授权。

根据 OAuth 规范,客户端获取 Access Token 的请求方式如下:

复制代码
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded

grant_type=password&username=johndoe&password=A3ddj3w
复制代码

根据上面的请求方式,在 C# 中用 HttpClient 实现一个简单的客户端,代码如下:

复制代码
public class OAuthClientTest
{ private HttpClient _httpClient; public OAuthClientTest()
    {
        _httpClient = new HttpClient();
        _httpClient.BaseAddress = new Uri("http://openapi.cnblogs.com");
    } 

    [Fact] public async Task Get_Accesss_Token_By_Resource_Owner_Password_Credentials_Grant()
    {
        Console.WriteLine(await GetAccessToken());
    } private async Task<string> GetAccessToken()
    { var clientId = "1234"; var clientSecret = "5678"; var parameters = new Dictionary<string, string>();            
        parameters.Add("grant_type", "password");
        parameters.Add("username", "博客园团队");
        parameters.Add("password", "cnblogs.com");

        _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue( "Basic",
            Convert.ToBase64String(Encoding.ASCII.GetBytes(clientId + ":" + clientSecret))
            ); var response = await _httpClient.PostAsync("/token", new FormUrlEncodedContent(parameters)); var responseValue = await response.Content.ReadAsStringAsync(); if (response.StatusCode == System.Net.HttpStatusCode.OK)
        { return JObject.Parse(responseValue)["access_token"].Value<string>();
        } else {
            Console.WriteLine(responseValue); return string.Empty;
        }
    }
}
复制代码

(注:与之前相比,这里的 client_id/client_secret 改为了 Basic Authorization,以更好的遵循 OAuth 规范)

在服务端,基于 Owin OAuth, 针对 Resource Owner Password Credentials Grant 的授权方式,只需重载 OAuthAuthorizationServerProvider.GrantResourceOwnerCredentials() 方法即可。代码如下:

复制代码
public class CNBlogsAuthorizationServerProvider : OAuthAuthorizationServerProvider
{ //... public override async Task GrantResourceOwnerCredentials(
        OAuthGrantResourceOwnerCredentialsContext context)
    { //调用后台的登录服务验证用户名与密码 var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
        oAuthIdentity.AddClaim(new Claim(ClaimTypes.Name, context.UserName)); var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties());
        context.Validated(ticket); await base.GrantResourceOwnerCredentials(context);
    }
}
复制代码

完整的CNBlogsAuthorizationServerProvider实现代码如下(与之前相比,context.TryGetFormCredentials 改为了 context.TryGetBasicCredentials):

复制代码
public class CNBlogsAuthorizationServerProvider : OAuthAuthorizationServerProvider
{ public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
    { string clientId; string clientSecret;
        context.TryGetBasicCredentials(out clientId, out clientSecret); if (clientId == "1234" && clientSecret == "5678")
        {
            context.Validated(clientId);
        } await base.ValidateClientAuthentication(context);
    } public override async Task GrantClientCredentials(OAuthGrantClientCredentialsContext context)
    { var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType); var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties());
        context.Validated(ticket); await base.GrantClientCredentials(context);
    } public override async Task GrantResourceOwnerCredentials(
        OAuthGrantResourceOwnerCredentialsContext context)
    { //调用后台的登录服务验证用户名与密码 var oAuthIdentity = new ClaimsIdentity(context.Options.AuthenticationType);
        oAuthIdentity.AddClaim(new Claim(ClaimTypes.Name, context.UserName)); var ticket = new AuthenticationTicket(oAuthIdentity, new AuthenticationProperties());
        context.Validated(ticket); await base.GrantResourceOwnerCredentials(context);
    }
}
复制代码

这样,运行客户端程序就可以拿到 Access Token 了。

接下来,我们拿着以这种方式获取的 Access Token,就可以调用与用户相关的 Web API 了。

在服务端我们通过一个简单的 Web API 测试一下,代码如下:

复制代码
public class UsersController : ApiController
{
    [Authorize] public string GetCurrent()
    { return User.Identity.Name; //这里可以调用后台用户服务,获取用户相关数所,或者验证用户权限进行相应的操作  }
}
复制代码

然后,客户端用以 grant_type=password 方式拿到的 Access Token 调用这个Web API,客户端增加的代码如下:

复制代码
[Fact] public async Task Call_WebAPI_By_Resource_Owner_Password_Credentials_Grant()
{ var token = await GetAccessToken();
    _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
    Console.WriteLine(await (await _httpClient.GetAsync("/api/users/current")).Content.ReadAsStringAsync());
}
复制代码

客户端运行结果如下:

"博客园团队"

调用成功!运行结果正是获取 Access Token 时所用的 username 。 

结合 ASP.NET 现有的安全机制,借助 OWIN 的威力,Microsoft.Owin.Security.OAuth 的确让开发基于 OAuth 的 Web API 变得更简单。

asp.net web-api – ASP.net Web API RESTful Web服务基本身份验证

asp.net web-api – ASP.net Web API RESTful Web服务基本身份验证

我正在使用ASP.Net Web Api实现RESTful Web服务。我已经得出结论使用基本认证SSL做认证部分。什么是最好/正确的方法来实现呢?

我的第一个尝试是手动,解析授权头,解码和验证用户对我的数据库。它的工作,但我不知道,如果我缺少的东西。

我见过一些使用用户角色和原则的解决方案。虽然我不知道这些实际做什么,我几乎肯定我不会需要这些,因为在我的数据库中,我定义了我自己的用户和他们的角色。

还有我还没有完全理解,是如果服务的消费者必须发送凭证与每个请求或他们以某种方式缓存。我的服务应该做些什么才能发生,还是完全由消费者来处理?

最后一个问题关于客户使用javascript请求。如果他们尝试使用该服务,是否会出现任何“跨域请求”问题?

解决方法

Jamie Kurtze提供了使用基本身份验证在这里 ASP.NET Web API REST Security Basics的一个很好的解释

从我的理解,如果你想要你的请求是无状态的,那么每个请求将需要设置验证字段

Jamie Kurtze在从DelegateHandler派生的类中包装必要的代码,而Rick Strahl使用过滤器检查调用是否有效。你可以在他的博客文章阅读更多在这个主题在A WebAPI Basic Authentication Authorization Filter

asp.net – Webapi,Webhost和Owin之间的关系

asp.net – Webapi,Webhost和Owin之间的关系

我只是想围绕webapi,webhost(iis)和owin之间的关系.我会写下我目前的理解,请你告诉我是否正确.

> Webapi与MVC不同,是以独立于主机的方式编写的.这是在欧文之前,但显然他们预计欧文会迟早会发生.主机独立性主要表示在Webapi代码中的任何地方都不使用System.Web.这是System.Web,它完全依赖IIS,如果没有它就不行.这样一来,Webapi可以理论上托管在任何地方 – 一旦其他主机可用.
> Webhost(Microsoft.Owin.Host.SystemWeb,Microsoft.AspNet.WebApi.WebHost)是一个层次之间的较高级别的API(如Webapi)和IIS.由于Webapi最初是独立于主机,因此需要一个中间层来使其在特定主机(如IIS)上运行. Webapi for Webapi(Microsoft.AspNet.WebApi.WebHost)提供了这个层.后来还将有针对Owin(Microsoft.Owin.Host.SystemWeb)的Webhost层,这将允许托管IIS上的任何Owin兼容.
欧文来了最后.它基本上提供了一个抽象,理论上可以允许在任何主机上托管任何Owin兼容的应用程序,只要在owin和该主机之间有一层. Owin带有Webhost(Microsoft.Owin.Host.SystemWeb)(类似于Webapi与Webhost一起使用),允许O IIS应用程序在IIS上托管.它还带有自主(Microsoft.Owin.SelfHost),允许Owin应用程序托管在任何可执行文件中.就Webapi而言,Owin还与Oapi主机一起使用Webapi(Microsoft.AspNet.WebApi.Owin),它允许在Owin堆栈上运行WebApi.

以上所有这一切意味着在IIS上托管Webapi有两种不同的方式.使用Webapi WebHost可以在没有Owin的情况下完成,也可以使用Oapi Host for Webapi和使用Webhost for Owin来完成.

Nuget参考文献

> Microsoft.Owin.SelfHost
> Microsoft.Owin.Host.SystemWeb
> Microsoft.AspNet.WebApi.WebHost
> Microsoft.AspNet.WebApi.Owin

这个理解正确吗?

解决方法

你的理解一般是正确的,但是OWIN的作用似乎被误解了.更完整的时间表将是:

> OWIN Standard开发描述通用.NET Web界面,一个Wsgi / Rake / Connect(first commit in 2010).
ASP.NET WebAPI是与主机无关开发的,但随着https://www.nuget.org/packages/Microsoft.AspNet.WebApi.WebHost/发布.
> Katana Project实现了几个OWIN主机:

> https://www.nuget.org/packages/Microsoft.Owin.SelfHost/
> https://www.nuget.org/packages/Microsoft.Owin.Host.HttpListener/
> https://www.nuget.org/packages/Microsoft.Owin.Host.IIS/
> https://www.nuget.org/packages/Microsoft.Owin.Host.SystemWeb/

> OWIN的ASP.NET WebAPI适配器发布:https://www.nuget.org/packages/Microsoft.AspNet.WebApi.Owin.

您的摘要:

All the above means that one has two different ways of hosting Webapi on IIS. It can be done without Owin,using Webapi WebHost,or it can be done with Owin Host for Webapi and with Webhost for Owin.

我会重申:

所有这些都意味着有两种不同的托管WebAPI的方式.可以在没有Owin的情况下使用WebAPI WebHost,或者可以使用OWIN adapter for WebAPI和任何OWIN兼容的主机完成. IIS上的托管选项为Microsoft.Owin.Host.IIS和Microsoft.Owin.Host.SystemWeb.还提供了Microsoft.AspNet.WebApi.OwinSelfHost.

关于asp.net-web-api – asp.net web api self hosting / owin / katana的介绍已经告一段落,感谢您的耐心阅读,如果想了解更多关于ASP.NET Web API Demo OwinSelfHost 自宿主 Swagger Swashbuckle 在线文档、ASP.NET Web API与Owin OAuth:调用与用户相关的Web API、asp.net web-api – ASP.net Web API RESTful Web服务基本身份验证、asp.net – Webapi,Webhost和Owin之间的关系的相关信息,请在本站寻找。

本文标签: