详解ASP.NET Core Web Api之JWT刷新Token

前言

如题,本节我们进入JWT最后一节内容,JWT本质上就是从身份认证服务器获取访问令牌,继而对于用户后续可访问受保护资源,但是关键问题是:访问令牌的生命周期到底设置成多久呢?见过一些使用JWT的童鞋会将JWT过期时间设置成很长,有的几个小时,有的一天,有的甚至一个月,这么做当然存在问题,如果被恶意获得访问令牌,那么可在整个生命周期中使用访问令牌,也就是说存在冒充用户身份,此时身份认证服务器当然也就是始终信任该冒牌访问令牌,若要使得冒牌访问令牌无效,唯一的方案则是修改密钥,但是如果我们这么做了,则将使得已授予的访问令牌都将无效,所以更改密钥不是最佳方案,我们应该从源头尽量控制这个问题,而不是等到问题呈现再来想解决之道,刷新令牌闪亮登场。

RefreshToken

什么是刷新令牌呢?刷新访问令牌是用来从身份认证服务器交换获得新的访问令牌,有了刷新令牌可以在访问令牌过期后通过刷新令牌重新获取新的访问令牌而无需客户端通过凭据重新登录,如此一来,既保证了用户访问令牌过期后的良好体验,也保证了更高的系统安全性,同时,若通过刷新令牌获取新的访问令牌验证其无效可将受访者纳入黑名单限制其访问,那么访问令牌和刷新令牌的生命周期设置成多久合适呢?这取决于系统要求的安全性,一般来讲访问令牌的生命周期不会太长,比如5分钟,又比如获取微信的AccessToken的过期时间为2个小时。接下来我将用两张表来演示实现刷新令牌的整个过程,可能有更好的方案,欢迎在评论中提出,学习,学习。我们新建一个http://localhost:5000的WebApi用于身份认证,再新建一个http://localhost:5001的客户端,首先点击【模拟登录获取Toen】获取访问令牌和刷新令牌,然后点击【调用客户端获取当前时间】,如下:

接下来我们新建一张用户表(User)和用户刷新令牌表(UserRefreshToken),结构如下:

public class User
  {
    public string Id { get; set; }
    public string Email { get; set; }
    public string UserName { get; set; }

    private readonly List<UserRefreshToken> _userRefreshTokens = new List<UserRefreshToken>();

    public IEnumerable<UserRefreshToken> UserRefreshTokens => _userRefreshTokens;

    /// <summary>
    /// 验证刷新token是否存在或过期
    /// </summary>
    /// <param name="refreshToken"></param>
    /// <returns></returns>
    public bool IsValidRefreshToken(string refreshToken)
    {
      return _userRefreshTokens.Any(d => d.Token.Equals(refreshToken) && d.Active);
    }

    /// <summary>
    /// 创建刷新Token
    /// </summary>
    /// <param name="token"></param>
    /// <param name="userId"></param>
    /// <param name="minutes"></param>
    public void CreateRefreshToken(string token, string userId, double minutes = 1)
    {
      _userRefreshTokens.Add(new UserRefreshToken() { Token = token, UserId = userId, Expires = DateTime.Now.AddMinutes(minutes) });
    }

    /// <summary>
    /// 移除刷新token
    /// </summary>
    /// <param name="refreshToken"></param>
    public void RemoveRefreshToken(string refreshToken)
    {
      _userRefreshTokens.Remove(_userRefreshTokens.FirstOrDefault(t => t.Token == refreshToken));
    }
public class UserRefreshToken
  {
    public string Id { get; private set; } = Guid.NewGuid().ToString();
    public string Token { get; set; }
    public DateTime Expires { get; set; }
    public string UserId { get; set; }
    public bool Active => DateTime.Now <= Expires;
  }

如上可以看到对于刷新令牌的操作我们将其放在用户实体中,也就是使用EF Core中的Back Fields而不对外暴露。接下来我们将生成的访问令牌、刷新令牌、验证访问令牌、获取用户身份封装成对应方法如下:

/// <summary>
    /// 生成访问令牌
    /// </summary>
    /// <param name="claims"></param>
    /// <returns></returns>
    public string GenerateAccessToken(Claim[] claims)
    {
      var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(signingKey));

      var token = new JwtSecurityToken(
        issuer: "http://localhost:5000",
        audience: "http://localhost:5001",
        claims: claims,
        notBefore: DateTime.Now,
        expires: DateTime.Now.AddMinutes(1),
        signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256)
      );

      return new JwtSecurityTokenHandler().WriteToken(token);
    }

    /// <summary>
    /// 生成刷新Token
    /// </summary>
    /// <returns></returns>
    public string GenerateRefreshToken()
    {
      var randomNumber = new byte[32];
      using (var rng = RandomNumberGenerator.Create())
      {
        rng.GetBytes(randomNumber);
        return Convert.ToBase64String(randomNumber);
      }
    }

    /// <summary>
    /// 从Token中获取用户身份
    /// </summary>
    /// <param name="token"></param>
    /// <returns></returns>
    public ClaimsPrincipal GetPrincipalFromAccessToken(string token)
    {
      var handler = new JwtSecurityTokenHandler();

      try
      {
        return handler.ValidateToken(token, new TokenValidationParameters
        {
          ValidateAudience = false,
          ValidateIssuer = false,
          ValidateIssuerSigningKey = true,
          IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(signingKey)),
          ValidateLifetime = false
        }, out SecurityToken validatedToken);
      }
      catch (Exception)
      {
        return null;
      }
    }

当用户点击登录,访问身份认证服务器,登录成功后我们创建访问令牌和刷新令牌并返回,如下:

[HttpPost("login")]
    public async Task<IActionResult> Login()
    {
      var user = new User()
      {
        Id = "D21D099B-B49B-4604-A247-71B0518A0B1C",
        UserName = "Jeffcky",
        Email = "2752154844@qq.com"
      };

      await context.Users.AddAsync(user);

      var refreshToken = GenerateRefreshToken();

      user.CreateRefreshToken(refreshToken, user.Id);

      await context.SaveChangesAsync();

      var claims = new Claim[]
      {
        new Claim(ClaimTypes.Name, user.UserName),
        new Claim(JwtRegisteredClaimNames.Email, user.Email),
        new Claim(JwtRegisteredClaimNames.Sub, user.Id),
      };

      return Ok(new Response() { AccessToken = GenerateAccessToken(claims), RefreshToken = refreshToken });
    }

此时我们回到如上给出的图,我们点击【模拟登录获取Token】,此时发出Ajax请求,然后将返回的访问令牌和刷新令牌存储到本地localStorage中,如下:

<input type="button" id="btn" value="模拟登录获取Token" />

<input type="button" id="btn-currentTime" value="调用客户端获取当前时间" />
//模拟登陆
    $('#btn').click(function () {
      GetTokenAndRefreshToken();
    });

    //获取Token
    function GetTokenAndRefreshToken() {
     $.post('http://localhost:5000/api/account/login').done(function (data) {
        saveAccessToken(data.accessToken);
        saveRefreshToken(data.refreshToken);
      });
    }
//从localStorage获取AccessToken
    function getAccessToken() {
      return localStorage.getItem('accessToken');
    }

    //从localStorage获取RefreshToken
    function getRefreshToken() {
      return localStorage.getItem('refreshToken');
    }

    //保存AccessToken到localStorage
    function saveAccessToken(token) {
      localStorage.setItem('accessToken', token);
    }

    //保存RefreshToken到localStorage
    function saveRefreshToken(refreshToken) {
      localStorage.setItem('refreshToken', refreshToken);
    }

此时我们再来点击【调用客户端获取当前时间】,同时将登录返回的访问令牌设置到请求头中,代码如下:

$('#btn-currentTime').click(function () {
      GetCurrentTime();
    });

    //调用客户端获取当前时间
    function GetCurrentTime() {
      $.ajax({
        type: 'get',
        contentType: 'application/json',
        url: 'http://localhost:5001/api/home',
        beforeSend: function (xhr) {
          xhr.setRequestHeader('Authorization', 'Bearer ' + getAccessToken());
        },
        success: function (data) {
          alert(data);
        },
        error: function (xhr) {

        }
      });
    }

客户端请求接口很简单,为了让大家一步步看明白,我也给出来,如下:

[Authorize]
    [HttpGet("api/[controller]")]
    public string GetCurrentTime()
    {
      return DateTime.Now.ToString("yyyy-MM-dd");
    }

好了到了这里我们已经实现模拟登录获取访问令牌,并能够调用客户端接口获取到当前时间,同时我们也只是返回了刷新令牌并存储到了本地localStorage中,并未用到。当访问令牌过期后我们需要通过访问令牌和刷新令牌去获取新的访问令牌,对吧。那么问题来了。我们怎么知道访问令牌已经过期了呢?这是其一,其二是为何要发送旧的访问令牌去获取新的访问令牌呢?直接通过刷新令牌去换取不行吗?有问题是好的,就怕没有任何思考,我们一一来解答。我们在客户端添加JWT中间件时,里面有一个事件可以捕捉到访问令牌已过期(关于客户端配置JWT中间件第一节已讲过,这里不再啰嗦),如下:

options.Events = new JwtBearerEvents
         {
           OnAuthenticationFailed = context =>
           {
             if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
             {
               context.Response.Headers.Add("act", "expired");
             }
             return Task.CompletedTask;
           }
         };

通过如上事件并捕捉访问令牌过期异常,这里我们在响应头添加了一个自定义键act,值为expired,因为一个401只能反映未授权,并不能代表访问令牌已过期。当我们在第一张图中点击【调用客户端获取当前时间】发出Ajax请求时,如果访问令牌过期,此时在Ajax请求中的error方法中捕捉到,我们在如上已给出发出Ajax请求的error方法中继续进行如下补充:

  error: function (xhr) {
          if (xhr.status === 401 && xhr.getResponseHeader('act') === 'expired') {
            // 访问令牌肯定已过期
          }
        }

到了这里我们已经解决如何捕捉到访问令牌已过期的问题,接下来我们需要做的则是获取刷新令牌,直接通过刷新令牌换取新的访问令牌也并非不可,只不过还是为了安全性考虑,我们加上旧的访问令牌。接下来我们发出Ajax请求获取刷新令牌,如下:

//获取刷新Token
    function GetRefreshToken(func) {
      var model = {
        accessToken: getAccessToken(),
        refreshToken: getRefreshToken()
      };
      $.ajax({
        type: "POST",
        contentType: "application/json; charset=utf-8",
        url: 'http://localhost:5000/api/account/refresh-token',
        dataType: "json",
        data: JSON.stringify(model),
        success: function (data) {
          if (!data.accessToken && !data.refreshToken) {
            // 跳转至登录
          } else {
            saveAccessToken(data.accessToken);
            saveRefreshToken(data.refreshToken);
            func();
          }
        }
      });
    }

发出Ajax请求获取刷新令牌的方法我们传入了一个函数,这个函数则是上一次调用接口访问令牌过期的请求,点击【调用客户端获取当前时间】按钮的Ajax请求error方法中,最终演变成如下这般:

error: function (xhr) {
          if (xhr.status === 401 && xhr.getResponseHeader('act') === 'expired') {

            /* 访问令牌肯定已过期,将当前请求传入获取刷新令牌方法,
             * 以便获取刷新令牌换取新的令牌后继续当前请求
            */
            GetRefreshToken(GetCurrentTime);
          }
        }

接下来则是通过传入旧的访问令牌和刷新令牌调用接口换取新的访问令牌,如下:

/// <summary>
    /// 刷新Token
    /// </summary>
    /// <returns></returns>
    [HttpPost("refresh-token")]
    public async Task<IActionResult> RefreshToken([FromBody] Request request)
    {
      //TODO 参数校验

      var principal = GetPrincipalFromAccessToken(request.AccessToken);

      if (principal is null)
      {
        return Ok(false);
      }

      var id = principal.Claims.First(c => c.Type == JwtRegisteredClaimNames.Sub)?.Value;

      if (string.IsNullOrEmpty(id))
      {
        return Ok(false);
      }

      var user = await context.Users.Include(d => d.UserRefreshTokens)
        .FirstOrDefaultAsync(d => d.Id == id);

      if (user is null || user.UserRefreshTokens?.Count() <= 0)
      {
        return Ok(false);
      }

      if (!user.IsValidRefreshToken(request.RefreshToken))
      {
        return Ok(false);
      }

      user.RemoveRefreshToken(request.RefreshToken);

      var refreshToken = GenerateRefreshToken();

      user.CreateRefreshToken(refreshToken, id);

      try
      {
        await context.SaveChangesAsync();
      }
      catch (Exception ex)
      {
        throw ex;
      }

      var claims = new Claim[]
      {
        new Claim(ClaimTypes.Name, user.UserName),
        new Claim(JwtRegisteredClaimNames.Email, user.Email),
        new Claim(JwtRegisteredClaimNames.Sub, user.Id),
      };

      return Ok(new Response()
      {
        AccessToken = GenerateAccessToken(claims),
        RefreshToken = refreshToken
      });
    }

如上通过传入旧的访问令牌验证并获取用户身份,然后验证刷新令牌是否已经过期,如果未过期则创建新的访问令牌,同时更新刷新令牌。最终客户端访问令牌过期的那一刻,通过刷新令牌获取新的访问令牌继续调用上一请求,如下:

到这里关于JWT实现刷新Token就已结束,自我感觉此种实现刷新令牌将其存储到数据库的方案还算可取,将刷新令牌存储到Redis也可行,看个人选择吧。上述若刷新令牌验证无效,可将访问者添加至黑名单,不过是添加一个属性罢了。别着急,本节内容结束前,还留有彩蛋。

EntityFramework Core Back Fields深入探讨

无论是看视频还是看技术博客也好,一定要动手验证,看到这里觉得上述我所演示是不是毫无问题,如果阅读本文的你直接拷贝上述代码你会发现有问题,且听我娓娓道来,让我们来复习下Back Fields。Back Fields命名是有约定dei,上述我是根据约定而命名,所以千万别一意孤行,别乱来,比如如下命名将抛出如下异常:

 private readonly List<UserRefreshToken> _refreshTokens = new List<UserRefreshToken>();

 public IEnumerable<UserRefreshToken> UserRefreshTokens => _refreshTokens;

上述我们配置刷新令牌的Back Fields,代码如下:

 private readonly List<UserRefreshToken> _userRefreshTokens = new List<UserRefreshToken>();
 public IEnumerable<UserRefreshToken> UserRefreshTokens => _userRefreshTokens;

要是我们配置成如下形式,结果又会怎样呢?

 private readonly List<UserRefreshToken> _userRefreshTokens = new List<UserRefreshToken>();
 public IEnumerable<UserRefreshToken> UserRefreshTokens => _userRefreshTokens.AsReadOnly();

此时为了解决这个问题,我们必须将其显式配置成Back Fields,如下:

protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
      modelBuilder.Entity<User>(u =>
      {
        var navigation = u.Metadata.FindNavigation(nameof(User.UserRefreshTokens));
        navigation.SetPropertyAccessMode(PropertyAccessMode.Field);
      });
    }

在我个人著作中也讲解到为了性能问题,可将字段进行ToList(),若进行了ToList(),必须显式配置成Back Fields,否则获取不到刷新令牌导航属性,如下:

private readonly List<UserRefreshToken> _userRefreshTokens = new List<UserRefreshToken>();
public IEnumerable<UserRefreshToken> UserRefreshTokens => _userRefreshTokens.ToList();

或者进行如下配置,我想应该也可取,不会存在性能问题,如下:

private readonly List<UserRefreshToken> _userRefreshTokens = new List<UserRefreshToken>();
 public IReadOnlyCollection<UserRefreshToken> UserRefreshTokens => _userRefreshTokens.AsReadOnly();

这是关于Back Fields问题之一,问题之二则是上述我们请求获取刷新令牌中,我们先在刷新令牌的Back Fields中移除掉旧的刷新令牌,而后再创建新的刷新令牌,但是会抛出如下异常:

我们看到在添加刷新令牌时,用户Id是有值的,对不对,这是为何呢?究其根本问题出在我们移除刷新令牌方法中,如下:

/// <summary>
    /// 移除刷新token
    /// </summary>
    /// <param name="refreshToken"></param>
    public void RemoveRefreshToken(string refreshToken)
    {
      _userRefreshTokens.Remove(_userRefreshTokens.FirstOrDefault(t => t.Token == refreshToken));
    }

我们将查询出来的导航属性并将其映射到_userRefreshTokens字段中,此时是被上下文所追踪,上述我们查询出存在的刷新令牌并在跟踪的刷新令牌中进行移除,没毛病,没找到原因,于是乎,我将上述方法修改成如下看看是否必须需要主键才能删除旧的刷新令牌:

/// <summary>
    /// 移除刷新token
    /// </summary>
    /// <param name="refreshToken"></param>
    public void RemoveRefreshToken(string refreshToken)
    {
      var id = _userRefreshTokens.FirstOrDefault(t => t.Token == refreshToken).Id;
      _userRefreshTokens.Remove(new UserRefreshToken() { Id = id });
    }

倒没抛出异常,创建了一个新的刷新令牌,但是旧的刷新令牌却没删除,如下:

至此未找到问题出在哪里,当前版本为2.2,难道不能通过Back Fields移除对象?这个问题待解决。

总结

本节我们重点讲解了如何实现JWT刷新令牌,并也略带讨论了EF Core中Back Fields以及尚未解决的问题,至此关于JWT已结束,下节开始正式进入Docker小白系列,感谢阅读。

到此这篇关于详解ASP.NET Core Web Api之JWT刷新Token的文章就介绍到这了,更多相关ASP.NET Core Web Api JWT刷新Token内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • asp.net core集成JWT的步骤记录

    [什么是JWT] JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案. JWT的官网地址:https://jwt.io/ 通俗地来讲,JWT是能代表用户身份的令牌,可以使用JWT令牌在api接口中校验用户的身份以确认用户是否有访问api的权限. JWT中包含了身份认证必须的参数以及用户自定义的参数,JWT可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对进行签名. [什么时候应该使用JSON Web令牌?] 授权:这是使用JWT的最常见方案.一旦用户登录

  • Asp.Net Core基于JWT认证的数据接口网关实例代码

    前言 近日,应一位朋友的邀请写了个Asp.Net Core基于JWT认证的数据接口网关Demo.朋友自己开了个公司,接到的一个升级项目,客户要求用Aps.Net Core做数据网关服务且基于JWT认证实现对前后端分离的数据服务支持,于是想到我一直做.Net开发,问我是否对.Net Core有所了解?能不能做个简单Demo出来看看?我说,分道扬镳之后我不是调用别人的接口就是提供接口给别人调用,于是便有了以下示例代码. 示例要求能演示获取Token及如何使用该Token访问数据资源,在Demo中实现

  • ASP.NET Core使用JWT认证授权的方法

    demo地址: https://github.com/william0705/JWTS 名词解析 认证 : 识别用户是否合法 授权: 赋予用户权限 (能访问哪些资源) 鉴权: 鉴定权限是否合法 Jwt优势与劣势 优势 1. 无状态 token 存储身份验证所有信息 , 服务端不需要保存用户身份验证信息, 减少服务端压力 , 服务端更容易水平扩展, 由于无状态, 又会导致它最大缺点 , 很难注销 2. 支持跨域访问 Cookie是不允许垮域访问的,token支持 3. 跨语言 基于标准化的 JSO

  • ASP.NET Core学习之使用JWT认证授权详解

    概述 认证授权是很多系统的基本功能 , 在以前PC的时代 , 通常是基于cookies-session这样的方式实现认证授权 , 在那个时候通常系统的用户量都不会很大, 所以这种方式也一直很好运行, 随着现在都软件用户量越来越大, 系统架构也从以前垂直扩展(增加服务器性能) -> 水平扩展(增加服务器数量) cookies-session 工作方式 客户端提交用户信息 -> 服务器识别用户 -> 服务端保存用户信息 -> 返回session-id客户端 -> 客户端保存ses

  • 浅谈ASP.NET Core 中jwt授权认证的流程原理

    1,快速实现授权验证 什么是 JWT ?为什么要用 JWT ?JWT 的组成? 这些百度可以直接找到,这里不再赘述. 实际上,只需要知道 JWT 认证模式是使用一段 Token 作为认证依据的手段. 我们看一下 Postman 设置 Token 的位置. 那么,如何使用 C# 的 HttpClient 访问一个 JWT 认证的 WebAPI 呢? 下面来创建一个 ASP.NET Core 项目,尝试添加 JWT 验证功能. 1.1 添加 JWT 服务配置 在 Startup.cs 的 Confi

  • ASP.Net Core3.0中使用JWT认证的实现

    JWT认证简单介绍 关于Jwt的介绍网上很多,此处不在赘述,我们主要看看jwt的结构. JWT主要由三部分组成,如下: HEADER.PAYLOAD.SIGNATURE HEADER 包含token的元数据,主要是加密算法,和签名的类型,如下面的信息,说明了 加密的对象类型是JWT,加密算法是HMAC SHA-256 {"alg":"HS256","typ":"JWT"} 然后需要通过BASE64编码后存入token中 eyJ

  • 详解ASP.NET Core Web Api之JWT刷新Token

    前言 如题,本节我们进入JWT最后一节内容,JWT本质上就是从身份认证服务器获取访问令牌,继而对于用户后续可访问受保护资源,但是关键问题是:访问令牌的生命周期到底设置成多久呢?见过一些使用JWT的童鞋会将JWT过期时间设置成很长,有的几个小时,有的一天,有的甚至一个月,这么做当然存在问题,如果被恶意获得访问令牌,那么可在整个生命周期中使用访问令牌,也就是说存在冒充用户身份,此时身份认证服务器当然也就是始终信任该冒牌访问令牌,若要使得冒牌访问令牌无效,唯一的方案则是修改密钥,但是如果我们这么做了,

  • 详解如何在ASP.NET Core Web API中以三种方式返回数据

    在 ASP.NET Core 中有三种返回 数据 和 HTTP状态码 的方式,最简单的就是直接返回指定的类型实例,如下代码所示: [ApiController] [Route("[controller]")] public class WeatherForecastController : ControllerBase { [HttpGet] public IEnumerable<WeatherForecast> Get() { var rng = new Random()

  • 详解ASP.NET Core 反向代理部署知多少

    引言 最近在折腾统一认证中心,看到开源项目IdentityServer4.Admin集成了IdentityServer4和管理面板,就直接拿过来用了.在尝试Nginx部署时遇到了诸如虚拟目录映射,请求头超长.基础路径映射有误等问题,简单记录,以供后人参考. Nginx 配置路由转发 首先来看下IdentityServer4.Admin的项目结构: IdentityServer4.Admin / ├── Id4.Admin.Api # 用于提供访问Id4资源的WebApi项目 ├── Id4.Ad

  • 详解Asp.Net Core 2.1+的视图缓存(响应缓存)

    响应缓存Razor 页与 ASP.NET 核心 2.0 中不支持. 此功能将支持ASP.NET 核心 2.1 版本. 在老的版本的MVC里面,有一种可以缓存视图的特性(OutputCache),可以保持同一个参数的请求,在N段时间内,直接从mvc的缓存中读取,不去走视图的逻辑. [OutputCache(Duration =20)]//设置过期时间为20秒 public ActionResult ExampleCacheAction() { var time=DateTime.Now.ToStr

  • 详解ASP.NET Core中配置监听URLs的五种方式

    默认情况下,ASP. NET Core应用会监听一下2个Url: http://localhost:5000 https://localhost:5001 在本篇博文中,我将展示如何使用五种不同的方式改变应用监听的URLs. 在ASP.NET Core项目启动时,有多种配置监听Url的方式,在我之前的一篇博客中,已经展示了在ASP.NET Core 1.0中如何应用不同的方式配置,在ASP.NET Core 3.x中,大部分方式还是一样的. UseUrls() - 在Program.cs配置程序

  • 详解asp.net core 依赖注入

    前言 好久没有写微博了,因为前段时间由于家庭原因决定从工作了3年多的北京转移到上海去.依赖注入在学习net core的时候也有写过类似的东西,只是实践的较少,结果来到上海新公司系统框架涉及到了这块知识点,所以在了解完自己的项目之后决定做一些相关的总结.接下来就让我们先来了解hewi依赖注入. 什么是依赖注入 依赖注入,全称是"依赖注入到容器", 容器(IOC容器)是一个设计模式,它也是个对象,你把某个类(不管有多少依赖关系)放入这个容器中,可以"解析"出这个类的实例

  • 详解ASP.NET Core端点路由的作用原理

    端点路由(Endpoint Routing)最早出现在ASP.NET Core2.2,在ASP.NET Core3.0提升为一等公民. Endpoint Routing的动机 在端点路由出现之前,我们一般在请求处理管道的末尾,定义MVC中间件解析路由.这种方式意味着在处理管道中,MVC中间件之前的中间件将无法获得路由信息. 路由信息对于某些中间件非常有用,比如CORS.认证中间件(认证过程可能会用到路由信息). 同时端点路由提炼出端点概念,解耦路由匹配逻辑.请求分发. Endpoint Rout

  • 在IIS上部署ASP.NET Core Web API的方法步骤

    对于本文,我想与您分享有关如何在IIS上部署ASP.NET Core Web API的指南.我将指导您安装Visual Studio 2019,.NET Core Runtime 3.0.我还确保我逐步引导您在服务器(Web IIS)中启用它,使用no受管代码选项创建新的应用程序池,创建ASP.NET Core Web API项目以及发布ASP.NET Core Web API. 了解并遵循正确的步骤来准备开发和部署环境后,在IIS上部署ASP.NET Core Web API就是一件容易的事.

  • ASP.NET Core Web API 教程Project Configuration

    目录 1. 创建新项目 2. launchSettings.json 文件 3. Program.cs 和 Startup.cs 4. 扩展方法和 CORS 配置 5. IIS 配置 6. Startup 类中的其它代码 7. 基于环境的设置 前言: 本系列文章主要参考了<Ultimate ASP.NET Core 3 Web API>一书,对原文进行了翻译,同时适当删减.修改了一部分内容. 对于某些概念和原理,原书和本文中都没有进行详细描述,如果一一详细介绍,内容就显得臃肿且混乱,我个人是先

  • 详解ASP.NET Core 网站发布到Linux服务器

    长期以来,使用.NET开发的应用只能运行在Windows平台上面,而目前国内蓬勃发展的互联网公司由于成本的考虑,大量使用免费的Linux平台,这就使得.NET空有一身绝技但无法得到广大的施展空间,.NET平台被认为只适合开发企业内部应用系统. 2016年6月27日,微软正式发布.NET Core 1.0.ASP.NET 1.0和Entity Framework Core 1.0,通吃 Windows.OS X和Linux三大操作系统..NET Core作为新一代跨平台.开源的.NET平台备受瞩目

随机推荐