Skip to content

Latest commit

 

History

History
788 lines (621 loc) · 31.6 KB

WebAPI.md

File metadata and controls

788 lines (621 loc) · 31.6 KB

WebAPI

Web API是一个比较宽泛的概念。这里我们提到Web API特指ASP.NET Web API。

这篇文章中我们主要介绍Web API的主要功能以及与其他同类型框架的对比,最后通过一些相对复杂的实例展示如何通过Web API构建http服务,同时也展示了Visual Studio构建.net项目的各种强大。

概念

什么是WebAPI

官方定义如下,强调两个关键点,即可以对接各种客户端(浏览器,移动设备),构建http服务的框架。

ASP.NET Web API is a framework that makes it easy to build HTTP services that reach a broad range of clients, including browsers and mobile devices. ASP.NET Web API is an ideal platform for building RESTful applications on the .NET Framework.

Web API在ASP.NET完整框架中地位如下图,与SignalR一起同为构建Service的框架。

Web API负责构建http常规服务,而SingalR主要负责的是构建实时服务,例如股票,聊天室,在线游戏等实时性要求比较高的服务。

为什么用WebAPI

Web API最重要的是可以构建面向各种客户端的服务。

另外与WCF REST Service不同在于,Web API利用Http协议的各个方面来表达服务(例如 URI/request response header/caching/versioning/content format),因此就省掉很多配置。

当你遇到以下这些情况的时候,就可以考虑使用Web API了。

  • 需要Web Service但是不需要SOAP
  • 需要在已有的WCF服务基础上建立non-soap-based http服务
  • 只想发布一些简单的Http服务,不想使用相对复杂的WCF配置
  • 发布的服务可能会被带宽受限的设备访问
  • 希望使用开源框架,关键时候可以自己调试或者自定义一下框架

功能简介

Web API的主要功能

  1. 支持基于Http verb (GET, POST, PUT, DELETE)的CRUD (create, retrieve, update, delete)操作。通过不同的http动作表达不同的含义,这样就不需要暴露多个API来支持这些基本操作。
  2. 请求的回复通过Http Status Code表达不同含义,并且客户端可以通过Accept header来与服务器协商格式,例如你希望服务器返回JSON格式还是XML格式。
  3. 请求的回复格式支持 JSON,XML,并且可以扩展添加其他格式。
  4. 原生支持OData。
  5. 支持Self-host或者IIS host。
  6. 支持大多数MVC功能,例如Routing/Controller/Action Result/Filter/Model Builder/IOC Container/Dependency Injection。

Web API vs MVC

你可能会觉得Web API 与MVC很类似,他们有哪些不同之处呢?先上图,这就是他们最大的不同之处。

详细点说他们的区别:

  • MVC主要用来构建网站,既关心数据也关心页面展示,而Web API只关注数据
  • Web API支持格式协商,客户端可以通过Accept header通知服务器期望的格式
  • Web API支持Self Host,MVC目前不支持
  • Web API通过不同的http verb表达不同的动作(CRUD),MVC则通过Action名字表达动作
  • Web API内建于ASP.NET System.Web.Http命名空间下,MVC位于System.Web.Mvc命名空间下,因此model binding/filter/routing等功能有所不同
  • 最后,Web API非常适合构建移动客户端服务

Web API vs WCF

发布服务在Web API和WCF之间该如何取舍呢?这里提供些简单地判断规则,

  • 如果服务需要支持One Way Messaging/Message Queue/Duplex Communication,选择WCF
  • 如果服务需要在TCP/Named Pipes/UDP (wcf 4.5),选择WCF
  • 如果服务需要在http协议上,并且希望利用http协议的各种功能,选择Web API
  • 如果服务需要被各种客户端(特别是移动客户端)调用,选择Web API

入门项目

创建项目

在模板窗格中,选择已安装的模板展开Visual C# 节点。 下Visual C#,选择Web。 在项目模板列表中选择ASP.NET Web 应用程序。

命名项目"ProductsApp",然后单击确定。

在中新建 ASP.NET 项目对话框中,选择空模板。 下"添加文件夹和核心引用",检查Web API。 单击 “确定”。

新建Model和Controller

新建Model:

namespace ProductsApp.Models
{
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Category { get; set; }
        public decimal Price { get; set; }
    }
}

新建控制器,在添加基架对话框中,选择Web API 控制器-空,如图:

将控制器命名"ProductsController"。修改控制器内容如下:

public class ProductsController : ApiController
{
    Product[] products = new Product[]
    {
        new Product { Id = 1, Name = "Tomato Soup", Category = "Groceries", Price = 1 },
        new Product { Id = 2, Name = "Yo-yo", Category = "Toys", Price = 3.75M },
        new Product { Id = 3, Name = "Hammer", Category = "Hardware", Price = 16.99M }
    };

    public IEnumerable<Product> GetAllProducts()
    {
        return products;
    }

    public IHttpActionResult GetProduct(int id)
    {
        var product = products.FirstOrDefault((p) => p.Id == id);
        if (product == null)
        {
            return NotFound();
        }
        return Ok(product);
    }
}
  • GetAllProducts方法返回作为产品的整个列表IEnumerable<产品> 类型。
  • GetProduct方法查找单个产品通过其 id。

前端模拟交互

选择Web节点下的Visual C#,然后选择HTML 页项。 将该页命名为"index.html"。

<!DOCTYPE html>
<html>
<head>
    <title>Product App</title>
</head>
<body>

    <div>
        <h2>All Products</h2>
        <ul id="products" />
    </div>
    <div>
        <h2>Search by ID</h2>
        <input type="text" id="prodId" size="5" />
        <input type="button" value="Search" onclick="find();" />
        <p id="product" />
    </div>

    <!--此处使用的是Microsoft Ajax CDN-->
    <script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.0.3.min.js"></script>
    <script>
        //注意此处开始需要有 斜杠'/',否则会因为路径的问题导致数据获取有误
        var uri = '/api/products';

        $(document).ready(function () {
            // Send an AJAX request
            $.getJSON(uri)
                .done(function (data) {
                    // On success, 'data' contains a list of products.
                    $.each(data, function (key, item) {
                        // Add a list item for the product.
                        $('<li>', { text: formatItem(item) }).appendTo($('#products'));
                    });
                });
        });

        function formatItem(item) {
            return item.Name + ': $' + item.Price;
        }

        function find() {
            var id = $('#prodId').val();
            $.getJSON(uri + '/' + id)
                .done(function (data) {
                    $('#product').text(formatItem(data));
                })
                .fail(function (jqXHR, textStatus, err) {
                    $('#product').text('Error: ' + err);
                });
        }
    </script>
</body>
</html>

开始调试应用程序。 网页应如下所示:

操作结果

Web API 控制器操作可以返回以下任一项:

返回类型 Web API 创建响应的方式
void 返回空 204 (无内容)
HttpResponseMessage 转换为直接 HTTP 响应消息。
IHttpActionResult 调用ExecuteAsync来创建HttpResponseMessage,然后将转换为 HTTP 响应消息。
其他类型 将序列化的返回值写入到响应正文中;返回 200 (正常)。

路由

路由表

在 ASP.NET Web API 中,控制器是处理 HTTP 请求的类。 调用控制器的公共方法操作方法或只需操作。

当 Web API 框架收到请求时,它将请求路由到某个操作。

若要确定要调用的操作,框架将使用路由表。 【WebApiConfig.cs】文件

routes.MapHttpRoute(
    name: "API Default",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional }
);

路由表中的每个条目包含路由模板。 Web API 的默认路由模板"api / {controller} / {id}"。

在此模板中, "api"是文本路径段和 {controller} 和 {id} 是占位符变量。

Web API 框架接收 HTTP 请求时,它尝试匹配根据一个路由表中的路由模板的 URI。

如果没有路由匹配,客户端收到 404 错误。 例如,下面的 Uri 匹配的默认路由:

  • / api/联系人
  • /api/contacts/1
  • /api/products/gizmo1

但是,下面的 URI 不匹配,因为它缺少"api"段:

  • / contacts/1

在路由中使用"api"的原因是避免使用 ASP.NET MVC 路由冲突。 这样一来,可以具有"/contacts"转到 MVC 控制器,并"/api/contacts"转到 Web API 控制器。 当然,如果您不喜欢这种约定,您可以更改默认路由表。

一旦找到匹配的路由,Web API 选择控制器和操作:

  • 若要查找控制器,Web API 将添加"控制器"的值 {controller} 变量。
  • 若要查找操作,Web API 的 HTTP 方法,将查看,然后查找其名称以与该 HTTP 方法名称的操作。 例如,使用 GET 请求,Web API 查找操作以开头的"GET...",如"GetContact"或"GetAllContacts"。 此约定仅适用于GET、 POST、 PUT 和 DELETE 方法。 可以通过在控制器上使用属性来启用其他 HTTP 方法。 我们将看到一个示例说明更高版本。
  • 其他占位符变量在路由模板中,如 {id} 映射到操作参数。

让我们看一个示例。 假设定义以下控制器:

public class ProductsController : ApiController
{
    public IEnumerable<Product> GetAllProducts() { }
    public Product GetProductById(int id) { }
    public HttpResponseMessage DeleteProduct(int id){ }
}

下面是一些可能的 HTTP 请求,以及每个调用的操作:

HTTP 方法 URI 路径 操作 参数
GET api/products GetAllProducts (无)
GET api/products/4 GetProductById 4
DELETE api/products/4 DeleteProduct 4

请注意, {id} 段的 URI,如果存在,将映射到id操作的操作参数。 在此示例中,控制器定义了两个 GET 方法,另一个使用id参数,另一个不带任何参数。

以上,为默认的apil路由,相比较mvc的路由缺少了{action}段,在使用上来看并没有那么方便。

我们可以在路由中包含action操作名称来解决这个问题,修改【WebApiConfig.cs】文件新增action段如下:

routes.MapHttpRoute(
    name: "ActionApi",
    routeTemplate: "api/{controller}/{action}/{id}",
    defaults: new { id = RouteParameter.Optional }
);

在此路由模板中, {action} 参数名称在控制器上的操作方法。 使用此样式的路由,使用属性来指定允许的 HTTP 方法。

例如,假设您的控制器具有以下方法:

public class ProductsController : ApiController
{
    [HttpGet]
    public string Details(int id);
}

在这种情况下,"api/products/Details/1"的 GET 请求将映射到的详细信息的方法。

这种路由是类似于 ASP.NET MVC 中,并可能适用于 RPC 样式 API。

可以通过重写操作的名称ActionName属性。 在以下示例中,有两个操作映射到"api/products/thumbnail/id"。

一种支持 GET方法 和另一种支持 POST方法 :

public class ProductsController : ApiController
{
    [HttpGet]
    [ActionName("Thumbnail")]
    public HttpResponseMessage GetThumbnailImage(int id);

    [HttpPost]
    [ActionName("Thumbnail")]
    public void AddThumbnailImage(int id);
}

若要防止为一个操作中调用一个action,使用NonAction属性。 NonAction表明该方法并非一个action,即使它符合路由规则。

// Not an action method.
[NonAction]  
public string GetPrivateData() { ... }

尝试访问会返回:No action was found on the controller 'ControllerName' that matches the name 'ActionName'.

WebAPI对于复杂对象的传递也十分的方便,如下:

static List<Product> listProducts = new List<Product>();
/// <summary>
/// 使用 x-www-form-urlencoded 键值对方式发送数据,使用$.ajax()默认类型发送请求
/// </summary>
/// <param name="pro"></param>
/// <returns></returns>
public IHttpActionResult SaveProduct(Product pro)
{
    if (null != pro)
    {
        listProducts.Add(pro);
    }
    return Ok(listProducts);
}

token验证

安全问题

基于前面新的webapi,可以通过PostMan进行测试:

获取所有产品信息

http://localhost:10034/api/Products/GetAllProducts

获取指定id的product

http://localhost:10034/api/Products/GetProduct?id=1

可以看到这个产品的API是公开访问的,没有任何验证,这样不是很安全,下一步我将加上token验证。

添加Owin包

打开NuGet包管理器控制台

在控制台依次输入如下指令安装扩展包:

Install-Package Microsoft.AspNet.WebApi.Owin
Install-Package Microsoft.Owin.Host.SystemWeb
Install-Package Microsoft.AspNet.Identity.Owin
Install-Package Microsoft.Owin.Cors
Install-Package EntityFramework

如下所示:

每个程序包都由其所有者许可给你。Microsoft 不负责第三方程序包,也不授予其许可证。一些程序包可能包括受其他许可证约束的依赖项。单击程序包源(源) URL 可确定任何依赖项。

程序包管理器控制台主机版本 3.0.0.0

键入 "get-help NuGet" 可查看所有可用的 NuGet 命令。

PM> Install-Package Microsoft.AspNet.WebApi.Owin
正在尝试收集与目标为“.NETFramework,Version=v4.5”的项目“ProductApp”有关的程序包“Microsoft.AspNet.WebApi.Owin.5.2.7”的相关依赖项信息
正在尝试解析程序包“Microsoft.AspNet.WebApi.Owin.5.2.7”的依赖项,DependencyBehavior 为“Lowest”
正在解析操作以安装程序包“Microsoft.AspNet.WebApi.Owin.5.2.7”
已解析操作以安装程序包“Microsoft.AspNet.WebApi.Owin.5.2.7”
已从“packages.config”中删除程序包“Microsoft.AspNet.WebApi.Client.5.2.3”
已从 ProductApp 成功卸载“Microsoft.AspNet.WebApi.Client.5.2.3”
已从“packages.config”中删除程序包“Microsoft.AspNet.WebApi.Client.zh-Hans.5.2.3”
已从 ProductApp 成功卸载“Microsoft.AspNet.WebApi.Client.zh-Hans.5.2.3”
已从“packages.config”中删除程序包“Microsoft.AspNet.WebApi.Core.5.2.3”
已从 ProductApp 成功卸载“Microsoft.AspNet.WebApi.Core.5.2.3”
已从“packages.config”中删除程序包“Microsoft.AspNet.WebApi.Core.zh-Hans.5.2.3”
已从 ProductApp 成功卸载“Microsoft.AspNet.WebApi.Core.zh-Hans.5.2.3”
程序包“Microsoft.AspNet.WebApi.Client.5.2.7”已存在于文件夹“D:\Codes\dotNet\WebApiDemo\packages”中
已将程序包“Microsoft.AspNet.WebApi.Client.5.2.7”添加到“packages.config”
已将“Microsoft.AspNet.WebApi.Client 5.2.7”成功安装到 ProductApp
程序包“Microsoft.AspNet.WebApi.Client.zh-Hans.5.2.7”已存在于文件夹“D:\Codes\dotNet\WebApiDemo\packages”中
已将程序包“Microsoft.AspNet.WebApi.Client.zh-Hans.5.2.7”添加到“packages.config”
已将“Microsoft.AspNet.WebApi.Client.zh-Hans 5.2.7”成功安装到 ProductApp
程序包“Microsoft.AspNet.WebApi.Core.5.2.7”已存在于文件夹“D:\Codes\dotNet\WebApiDemo\packages”中
已将程序包“Microsoft.AspNet.WebApi.Core.5.2.7”添加到“packages.config”
已将“Microsoft.AspNet.WebApi.Core 5.2.7”成功安装到 ProductApp
程序包“Microsoft.AspNet.WebApi.Core.zh-Hans.5.2.7”已存在于文件夹“D:\Codes\dotNet\WebApiDemo\packages”中
已将程序包“Microsoft.AspNet.WebApi.Core.zh-Hans.5.2.7”添加到“packages.config”
已将“Microsoft.AspNet.WebApi.Core.zh-Hans 5.2.7”成功安装到 ProductApp
程序包“Owin.1.0.0”已存在于文件夹“D:\Codes\dotNet\WebApiDemo\packages”中
已将程序包“Owin.1.0.0”添加到“packages.config”
已将“Owin 1.0.0”成功安装到 ProductApp
正在将程序包“Microsoft.Owin.2.0.2”添加到文件夹“D:\Codes\dotNet\WebApiDemo\packages”
已将程序包“Microsoft.Owin.2.0.2”添加到文件夹“D:\Codes\dotNet\WebApiDemo\packages”
已将程序包“Microsoft.Owin.2.0.2”添加到“packages.config”
已将“Microsoft.Owin 2.0.2”成功安装到 ProductApp
程序包“Microsoft.AspNet.WebApi.Owin.5.2.7”已存在于文件夹“D:\Codes\dotNet\WebApiDemo\packages”中
已将程序包“Microsoft.AspNet.WebApi.Owin.5.2.7”添加到“packages.config”
已将“Microsoft.AspNet.WebApi.Owin 5.2.7”成功安装到 ProductApp
正在从文件夹“D:\Codes\dotNet\WebApiDemo\packages”中删除程序包“Microsoft.AspNet.WebApi.Client.5.2.3”
已从文件夹“D:\Codes\dotNet\WebApiDemo\packages”中删除程序包“Microsoft.AspNet.WebApi.Client.5.2.3”
正在从文件夹“D:\Codes\dotNet\WebApiDemo\packages”中删除程序包“Microsoft.AspNet.WebApi.Client.zh-Hans.5.2.3”
已从文件夹“D:\Codes\dotNet\WebApiDemo\packages”中删除程序包“Microsoft.AspNet.WebApi.Client.zh-Hans.5.2.3”
正在从文件夹“D:\Codes\dotNet\WebApiDemo\packages”中删除程序包“Microsoft.AspNet.WebApi.Core.5.2.3”
已从文件夹“D:\Codes\dotNet\WebApiDemo\packages”中删除程序包“Microsoft.AspNet.WebApi.Core.5.2.3”
正在从文件夹“D:\Codes\dotNet\WebApiDemo\packages”中删除程序包“Microsoft.AspNet.WebApi.Core.zh-Hans.5.2.3”
已从文件夹“D:\Codes\dotNet\WebApiDemo\packages”中删除程序包“Microsoft.AspNet.WebApi.Core.zh-Hans.5.2.3”
PM> Install-Package Microsoft.Owin.Host.SystemWeb
正在尝试收集与目标为“.NETFramework,Version=v4.5”的项目“ProductApp”有关的程序包“Microsoft.Owin.Host.SystemWeb.4.0.1”的相关依赖项信息
正在尝试解析程序包“Microsoft.Owin.Host.SystemWeb.4.0.1”的依赖项,DependencyBehavior 为“Lowest”
正在解析操作以安装程序包“Microsoft.Owin.Host.SystemWeb.4.0.1”
已解析操作以安装程序包“Microsoft.Owin.Host.SystemWeb.4.0.1”
已从“packages.config”中删除程序包“Microsoft.Owin.2.0.2”
已从 ProductApp 成功卸载“Microsoft.Owin.2.0.2”
程序包“Microsoft.Owin.4.0.1”已存在于文件夹“D:\Codes\dotNet\WebApiDemo\packages”中
已将程序包“Microsoft.Owin.4.0.1”添加到“packages.config”
已将“Microsoft.Owin 4.0.1”成功安装到 ProductApp
程序包“Microsoft.Owin.Host.SystemWeb.4.0.1”已存在于文件夹“D:\Codes\dotNet\WebApiDemo\packages”中
已将程序包“Microsoft.Owin.Host.SystemWeb.4.0.1”添加到“packages.config”
已将“Microsoft.Owin.Host.SystemWeb 4.0.1”成功安装到 ProductApp
正在从文件夹“D:\Codes\dotNet\WebApiDemo\packages”中删除程序包“Microsoft.Owin.2.0.2”
已从文件夹“D:\Codes\dotNet\WebApiDemo\packages”中删除程序包“Microsoft.Owin.2.0.2”
PM> Install-Package Microsoft.AspNet.Identity.Owin
正在尝试收集与目标为“.NETFramework,Version=v4.5”的项目“ProductApp”有关的程序包“Microsoft.AspNet.Identity.Owin.2.2.2”的相关依赖项信息
正在尝试解析程序包“Microsoft.AspNet.Identity.Owin.2.2.2”的依赖项,DependencyBehavior 为“Lowest”
正在解析操作以安装程序包“Microsoft.AspNet.Identity.Owin.2.2.2”
已解析操作以安装程序包“Microsoft.AspNet.Identity.Owin.2.2.2”
程序包“Microsoft.AspNet.Identity.Core.2.2.2”已存在于文件夹“D:\Codes\dotNet\WebApiDemo\packages”中
已将程序包“Microsoft.AspNet.Identity.Core.2.2.2”添加到“packages.config”
已将“Microsoft.AspNet.Identity.Core 2.2.2”成功安装到 ProductApp
程序包“Microsoft.Owin.Security.3.0.1”已存在于文件夹“D:\Codes\dotNet\WebApiDemo\packages”中
已将程序包“Microsoft.Owin.Security.3.0.1”添加到“packages.config”
已将“Microsoft.Owin.Security 3.0.1”成功安装到 ProductApp
程序包“Microsoft.Owin.Security.Cookies.3.0.1”已存在于文件夹“D:\Codes\dotNet\WebApiDemo\packages”中
已将程序包“Microsoft.Owin.Security.Cookies.3.0.1”添加到“packages.config”
已将“Microsoft.Owin.Security.Cookies 3.0.1”成功安装到 ProductApp
程序包“Microsoft.Owin.Security.OAuth.3.0.1”已存在于文件夹“D:\Codes\dotNet\WebApiDemo\packages”中
已将程序包“Microsoft.Owin.Security.OAuth.3.0.1”添加到“packages.config”
已将“Microsoft.Owin.Security.OAuth 3.0.1”成功安装到 ProductApp
程序包“Microsoft.AspNet.Identity.Owin.2.2.2”已存在于文件夹“D:\Codes\dotNet\WebApiDemo\packages”中
已将程序包“Microsoft.AspNet.Identity.Owin.2.2.2”添加到“packages.config”
已将“Microsoft.AspNet.Identity.Owin 2.2.2”成功安装到 ProductApp
PM> Install-Package Microsoft.Owin.Cors
正在尝试收集与目标为“.NETFramework,Version=v4.5”的项目“ProductApp”有关的程序包“Microsoft.Owin.Cors.4.0.1”的相关依赖项信息
正在尝试解析程序包“Microsoft.Owin.Cors.4.0.1”的依赖项,DependencyBehavior 为“Lowest”
正在解析操作以安装程序包“Microsoft.Owin.Cors.4.0.1”
已解析操作以安装程序包“Microsoft.Owin.Cors.4.0.1”
程序包“Microsoft.AspNet.Cors.5.0.0”已存在于文件夹“D:\Codes\dotNet\WebApiDemo\packages”中
已将程序包“Microsoft.AspNet.Cors.5.0.0”添加到“packages.config”
已将“Microsoft.AspNet.Cors 5.0.0”成功安装到 ProductApp
程序包“Microsoft.Owin.Cors.4.0.1”已存在于文件夹“D:\Codes\dotNet\WebApiDemo\packages”中
已将程序包“Microsoft.Owin.Cors.4.0.1”添加到“packages.config”
已将“Microsoft.Owin.Cors 4.0.1”成功安装到 ProductApp
PM> Install-Package EntityFramework
正在尝试收集与目标为“.NETFramework,Version=v4.5”的项目“ProductApp”有关的程序包“EntityFramework.6.2.0”的相关依赖项信息
正在尝试解析程序包“EntityFramework.6.2.0”的依赖项,DependencyBehavior 为“Lowest”
正在解析操作以安装程序包“EntityFramework.6.2.0”
已解析操作以安装程序包“EntityFramework.6.2.0”
程序包“EntityFramework.6.2.0”已存在于文件夹“D:\Codes\dotNet\WebApiDemo\packages”中
已将程序包“EntityFramework.6.2.0”添加到“packages.config”
正在执行脚本文件“D:\Codes\dotNet\WebApiDemo\packages\EntityFramework.6.2.0\tools\init.ps1”
正在执行脚本文件“D:\Codes\dotNet\WebApiDemo\packages\EntityFramework.6.2.0\tools\install.ps1”

Type 'get-help EntityFramework' to see all available Entity Framework commands.
已将“EntityFramework 6.2.0”成功安装到 ProductApp
PM> 

StartUp类

在项目根目录下添加Owin“Startup”类

// Owin启动项类
[assembly: OwinStartup(typeof(ProductApp.Startup))]
namespace ProductApp
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            HttpConfiguration config = new HttpConfiguration();
            ConfigureOAuth(app);

            WebApiConfig.Register(config);
            app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
            app.UseWebApi(config);
        }

        public void ConfigureOAuth(IAppBuilder app)
        {
            OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
            {
                AllowInsecureHttp = true,
                TokenEndpointPath = new PathString("/token"),
                AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(20),//过期时间
                Provider = new SimpleAuthorizationServerProvider()
            };
            app.UseOAuthAuthorizationServer(OAuthServerOptions);
            app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
        }
    }
}

使用Owin的Setup类,就不需要MVC的Global类了,删除之。

添加验证类

在项目下添加 SimpleAuthorizationServerProvider 类,用于用户的授权认证。

public class SimpleAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
    public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
    {
        context.Validated();
    }

    public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {

        context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });

        //模拟登陆验证未通过
        if (!context.UserName.Equals("admin") || !context.Password.Equals("123"))
        {
            context.SetError("验证失败", "用户名或密码错误");
            return;
        }


        var identity = new ClaimsIdentity(context.Options.AuthenticationType);
        identity.AddClaim(new Claim("sub", context.UserName));
        identity.AddClaim(new Claim("role", "user"));

        context.Validated(identity);
    }
}

在ASP.NET Web API中启用OAuth的Access Token验证非常简单,只需在相应的Controller或Action加上[Authorize]标记

Authorize授权

针对action增加Authorize特性

[Authorize]
public IEnumerable<Product> GetAllProducts()
{
    return products;
}

增加验证后,通过PostMan模拟请求,被服务器拒绝授权:

需要先通过授权拿到token令牌,请求~/token,并附加username和password信息获取token

TokenEndpointPath = new PathString("/token"),

如上图所示,通过验证获取服务器生成的 【access_token】,有效时间在【StartUp】类【ConfigureOAuth】方法配置

在新的请求header中附加token信息,在Headers中新增【Authorization】,值为:【bearer token】

Authorization:bearer Gqq4cdp4E3MxX0GFWnnqMG_Qs6csz7v7Np2uJUHjaKEbtorUL84mIBAYcwG1QARec1_HLzwbQ4CHmOto3D0azsChCWgdnQ-8g3ASK4sYZ9nYZjxPjEsfxfjVsEXETlWAOkM3riWXrp6gv3o8bY3oJdM5KiVkhHO7Yfbg8MctxqGDbSIcAr3Qc4p9ne-KTTXYAUjt-75-WkLsTXBwbR7CNfVCmfY3wolXrTrlzCxIl_E

根据指定id返回数据也是同样的道理:

这样我们就完成了简单的WEB API的token验证

前端html请求

构造前端html页面如下:

<button id="btnAllProduct">获取数据</button>
<button id="btnToken">登陆授权</button>
<button id="btnGetByToken">附加token的请求</button>
<div style="color:green;" id="suc_div"></div>
<div style="color:red;" id="err_div"></div>

模拟ajax未附加token的请求如下:

// Headers未附加token令牌的请求
$('#btnAllProduct').click(function () {
    $.ajax({
        url: 'http://localhost:10034/api/Products/GetAllProducts',
        type: 'POST',
        dataType: 'json',
        success: function (resp) {
        },
        error: function (err) {
            $('#err_div').append(err.status + err.responseText);
            $('#err_div').append('<br/>');
        }
    });
});

无访问授权,返回401

模拟用户登录授权,返回token令牌,保存至浏览器localStorage

// 密码验证授权,获取token令牌
$('#btnToken').click(function () {
    $.ajax({
        url: 'http://localhost:10034/token',
        type: 'POST',
        dataType: 'json',
        data: {
            grant_type: 'password',
            username: 'admin',
            password: '123'
        },
        success: function (resp) {
            // 获取服务器返回的token并保存至localStorage
            localStorage.access_token = resp.access_token;
            $('#suc_div').append('access_token:' + resp.access_token);
            $('#suc_div').append('<br/>');
        },
        error: function (err) {
            $('#err_div').append(err.status + err.responseText);
        }
    });
});

验证通过,服务器返回token信息:

使用获取的token令牌,添加至Headers部分,重新请求数据:

// Headers附加token请求数据
$('#btnGetByToken').click(function () {
    $.ajax({
        url: 'http://localhost:10034/api/Products/GetAllProducts',
        type: 'POST',
        headers: {
            Authorization: 'bearer ' + localStorage.access_token//bearer后面有个空格!!!
        },
        dataType: 'json',
        success: function (resp) {
            $.each(resp, function (i, t) {
                $('#suc_div').append(`Id:${t.Id},Name:${t.Name},Category:${t.Category}`);
                $('#suc_div').append('<br/>');
            });
        },
        error: function (err) {
            $('#err_div').append(err.status + err.responseText);
            $('#suc_div').append('<br/>');
        }
    });
});

通过ajax请求附加token信息,除了上面使用的Headers配置,还可以使用beforeSend回调进行设置:

function GetDateForServiceCustomer(userId) {
    $.ajax({
        url: 'http://*******/api',
        data: {
        },
        beforeSend: function(request) {
            request.setRequestHeader("Authorization", token);
        },
        dataType: 'JSON',
        type: 'GET',
        success: function (resp) {
        },
        error: function () {
        }
    });
}

以上,完成了简单的WebApi身份验证。


https://www.cnblogs.com/guyun/p/4589115.html

https://docs.microsoft.com/zh-cn/aspnet/web-api/overview/getting-started-with-aspnet-web-api/tutorial-your-first-web-api

webapi 用户验证

https://www.cnblogs.com/landeanfen/p/5287064.html#_label3_0

webapi token

https://www.cnblogs.com/relax/p/4956441.html