谈谈ASP.NET CORE MVC 2.2入门知识点

第一节:基本环境配置

InProcess:将项目托管在 IIS 工作进程中,性能有所提高
OutOfProcess:项目运行在 Kestrel 服务器,IIS 只做 Web 请求转发

第二节:IConfiguration的配置信息来源

//配置信息来源:
• appsettings.json
• User Secrets
• 系统环境变量
• 命令行参数

如果出现重复的属性,后添加的会覆盖前面的值,所以系统环境变量的优先级高于 appsettings.json

第三节:注册服务与管道

//超文本传输协议HTTP

超文本传输​​协议(HTTP)是用于传输诸如HTML的超媒体文档的应用层协议。
它被设计用于Web浏览器和Web服务器之间的通信,但它也可以用于其他目的。
HTTP遵循经典的客户端-服务端模型,客户端打开一个连接以发出请求,然后等待它收到服务器端响应。
HTTP是无状态协议,意味着服务器不会在两个请求之间保留任何数据(状态)。
虽然通常基于TCP / IP层,但可以在任何可靠的传输层上使用; 也就是说,一个不会静默丢失消息的协议,如UDP。

//依赖:当一个类需要另一个类协作来完成工作的时候就产生了依赖

//我们把依赖的对象叫做服务,把这个服务所对应的对象叫做实例

//依赖注入的生命周期

• Transient:每次被其他类或方法请求都会创建新的实例
• Scoped:每次 Web 请求只创建一个实例
• Singleton:一旦被创建实例,就会一直使用这个实例,直到应用停止

//依赖注入(注入服务)的方式

1、设置注入和获取注入的方式不止一种,示例只是演示了最简单和最常用的使用方式,其他方式可以参考文档;

2、可以替换.net core中的默认注入容器, 如常用的autofac,可以实现更强大的功能

3、可以直接在View中获取注入 @inject IUserInfoService userInfoService

4、可以在httpcontext里直接获取注入HttpContext.RequestServices.GetService();

5、Startup中的ConfigureServices方法就是为了设置注入而存在的;

//管道与中间件的工作机制

管道的注册者和构建者:ApplicationBuilder

描述当前请求的上下文:HttpContext

1)Logger 记录请求信息
2)授权根据 cookie 或 token 进行权限判定
3)路由根据请求 URL 确定调用哪个类的哪个方法
4)成功响应,原路返回 JSON 或 HTML
其中处理的途径称为管道,授权,路由等成为中间件

中间件组件是按照添加到管道的顺序进行执行的
中间件组件应该用NuGet包的形式提供

第四节;中间件

//Environment

内置的三种环境值:
• Development:开发环境
• Production:生产环境
• Staging:预览(演示)环境

//文件伺服

通过 app.UseStaticFiles(); 伺服静态文件后才能访问 index.html 等静态文件。
也可以使用 app.UserFileServer();
它将 Enable all static file middleware (except directory browsing)
for the current request path in the current directory

问题1:为何中间件不直接通过一个RequestDelegate对象来表示,而是表示为一个类型为
Func<RequestDelegate, RequestDelegate>的委托对象呢?

答:中间件并不孤立地存在,所有注册的中间件最终会根据注册的先后顺序组成一个链表,
每个中间件不仅仅需要完成各自的请求处理任务外,还需要驱动链表中的下一个中间件。

第五节:controller的路由

MVC 框架可以将传入的 HTTP 请求映射到某个类的特定方法上,在方法内执行任务并返回数据

//MVC工作机制

Controller:接收请求,构建 Model,选择 View
Model:携带信息和逻辑
View:将 Model 翻译为 HTML 页面

//路由:将HTTP请求映射到正确的controller上的方法

Conventional Routing
Attribute Routing

常见的 MVC 应用通常选择前者,RESTful API 或 Web API 项目通常选择后者

第六节:Controller返回View

• Controller 父类:提供很多上下文相关信息及封装方法
• this.File() 返回文件
• 返回 IActionResult
• 使用 sting,int 等作为返回值时,会立即返回
• 使用 IActionResult 时不会立即返回值,只会决定返回什么值,具体的返回值操作由 MVC 框架来做

第七节:View 的 Model 和 Tag Helpers

在 Controller 内将 Entity Model 转化为 View Model 再传递给 View

这里ViewModel的作用是:
(1)将VIew中变化的数据通过VIewModel这个中间调度者更新到Mode中
(2)将Model中的数据变化渲染到View中

MVVM模式的优点:

低耦合:View可以独立于Model变化和修改,一个ViewModel可以绑定到不同的View上,
当View变化的时候Model可以不变,当Model变化的时候View也可以不变。

可重用性: 可以把一些视图逻辑放在一个ViewModel里面,让很多View重用这段视图逻辑。

独立开发: 开发人员可以专注于业务逻辑和数据的开发,设计人员可以专注于页面的设计。

Razor 是一种标记语法,用于将基于服务器的代码嵌入网页中。
Razor 语法由 Razor 标记、C# 和 HTML 组成。 包含 Razor 的文件通常具有 .cshtml 文件扩展名。
在 Razor 组件文件 (.razor) 中也可以找到 Razor。

MVVM模式与MVC模式的区别在于MVVM模式实现了数据的双向绑定

主要的Razor C#语法规则

剃刀代码块包含在@ {…}中
内联表达式(变量和函数)以@开头
代码语句用分号结束
变量使用var关键字声明
弦用引号括起来
C#代码区分大小写
C#文件的扩展名是.cshtml

第八节:输入 Model 和防止重复 Post

将注册的服务使用AddsingleTon方法,可以保留页面的会发表单

避免页面重复提交:通过 Post-Redirect-Get 模式重定向防止重复提交。
在执行HTTPPost动作以后,执行一次页面的重定向

第九节:Model验证

Tag Helper 结合 Data Annotations 可以生成不同属性的标签及验证规则。

第十节:EFCore

一个 DbContext 类对应一个数据库
一个 DbSet 集合对应数据库中的一张表

Add-Migration 执行数据库迁移,生成数据库SQL语句
Update-Database 更新数据到数据库里

HomeController、 IRepository 和 InMemoryRepository 的设计遵循了依赖反转原则:
• InMemoryRepository 实现了 IRepository 接口
• HomeController 依赖于 IRepository 接口,不依赖具体实现类
• HomeController 这个高级别的模块不依赖于 InMemoryRepository 这个低级别的模块
• 高级别的模块应该依赖于抽象

第十一节:_Layout, _ViewStart, _ViewImports

_Layout.cshtml 相当于ASP.NET中的母版页,其必须放在/~Views/Shared下

_ViewStart.cshtml 用来配置每个页面所引用的Layout view等,其必须放在/~Views/下才能对所有的view文件的引用起作用

_ViewImports .cshtml用来配置所有view的命名空间引用

第十二节:Paritial View View Components

Paritial View 分部视图
• 复用 View 代码
• 可以嵌套
• 没有自己的 Model ,依赖于其所在父级view的数据逻辑
• 两种用法
• partial Tag Helper(推荐)
• @Html.Partial("_PartialViewName", data)

View Components 视图组件

典型应用场景

(1)动态导航菜单
(2)标签云
(3)登录面板
(4)购物车
(5)最近文章
(6)博客侧边栏

适用场景:例如定义一个登录面板VC,在不同的页面可以调用该VC
在用户未登录时调用
在登陆后某个页面想进行更改账户时
在不同身份登录时,渲染不同view

特点:

可复用
独立的组件
有独立的逻辑/数据,其不依赖于父级view的数据逻辑
相当于迷你 MVC 请求
不依赖于父级 View 的数据

第十二节:使用使用 NPM安装前端库

可以直接添加 NPM 配置文件:右键项目添加 - 新建项 - 搜索 npm。
然后直接编辑 NPM 配置文件(pacakge.json)并保存,VS 就会自动添加包。
自动创建的 node_modules 文件夹默认不被伺服,可以通过修改 Startup 手动将其伺服。

通过 asp-fallback-src 和 asp-fallback-test 实现回落机制,当测试发现 CDN 的 js 无法使用时,切换为本地 js

第十三节:ASP.NET Core Identity
作用
• 身份认证和授权系统
• 成员管理
• 默认使用 MSSQL
• 支持外部的 Provider

使用 ASP.NET Core Identity
• 登录和注册的 View
• AccountController
• Model
ASP.NET Core Identity 重点类
• UserManager:用户管理
• SignInManager:身份认证
Identity 的具体使用
官方教程:https://docs.microsoft.com/en-us/aspnet/core/security/authentication/identity?view=aspnetcore-2.2&tabs=visual-studio

第十二节 identityUser扩展

扩展 IdentityUser
查看 IdentityUser 的源码,不难发现它的属性并不多。我们可以通过继承它来创建属性更丰富的 IdentityUser。
添加了身份证号和出生日期的 ApplicationUser

public class ApplicationUser:IdentityUser
{
    [MaxLength(18)]
    public string IdCard { get; set; }

    [DataType(DataType.Date)]
    public DateTime BirthDate { get; set; }
}

然后将 Configure Services 里面的代码:services.AddDefaultIdentity<IdentityUser> 修改为 services.AddDefaultIdentity<ApplicationUser>
修改实现类ApplicationDbContext

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    ...
}

然后 Add-Migration + Update-Database 更新数据库。
最后将程序中所有原来使用 IdentityUser 的地方替换为 ApplicationUser。
主要涉及Login认证,

注意:EditUser.cshtml中引用model为ApplicationUser不是EditUserView Model,这点与Adduser.cshtml不同,因为修改需要读出数据库中ApplicationDbContext用户信息,而AddUser.cshtml则将AddUserViewModel中的信息上传到数据库

自定义密码规则
Identity 默认要求用户设置复杂的强密码,我们可以通过 IdentityOptions 自定义密码规则。

services.AddDefaultIdentity<ApplicationUser>(options =>
    {
        options.Password.RequireNonAlphanumeric = false;
        options.Password.RequireLowercase = false;
        options.Password.RequireUppercase = false;
        options.Password.RequiredLength = 6;
    })
    .AddDefaultUI(UIFramework.Bootstrap4)
    .AddEntityFrameworkStores<ApplicationDbContext>();

第十二节 Role Manager

创建,删除 Role
把用户添加到 Role
对 Role 进行授权 [Authorize(Roles = “xxxRle”)]
要启用默认的 IdentityRole,在 Startup 里面配置时就不能使用 AddDefaultIdentity 了。需要使用 AddIdentity 并指定 User 和 Role

services.AddIdentity<ApplicationUser, IdentityRole>(options =>
        {
            options.Password.RequireNonAlphanumeric = false;
            ...
        })
    .AddDefaultUI(UIFramework.Bootstrap4)
    .AddEntityFrameworkStores<ApplicationDbContext>();

Role Controller
通过注入的 UserManager 和 RoleManager 操作角色。
单独操作 Role 的代码和 UserController 相似,主要不同在于修改 User 的 Role

[Authorize]
public class RoleController : Controller
{
    private readonly UserManager<ApplicationUser> _userManager;

    private readonly RoleManager<IdentityRole> _roleManager;

    public RoleController(
        UserManager<ApplicationUser> userManager,
        RoleManager<IdentityRole> roleManager)
    {
        _userManager = userManager;
        _roleManager = roleManager;
    }

    public async Task<IActionResult> Index()
    {
        var roles = await _roleManager.Roles.ToListAsync();
        return View(roles);
    }

    public IActionResult AddRole()
    {
        return View();
    }

    [HttpPost]
    public async Task<IActionResult> AddRole(RoleAddViewModel roleAddViewModel)
    {
        if (!ModelState.IsValid)
        {
            return View(roleAddViewModel);
        }

        var role = new IdentityRole { Name = roleAddViewModel.RoleName };

        var result=await _roleManager.CreateAsync(role);
        if (result.Succeeded)
        {
            return RedirectToAction("Index");
        }

        foreach (var error in result.Errors)
        {
            ModelState.AddModelError(string.Empty, error.Description);
        }
        return View(roleAddViewModel);
    }

    public async Task<IActionResult> EditRole(string id)
    {
        var role=await _roleManager.FindByIdAsync(id);

        if (role == null)
        {
            return RedirectToAction("Index");
        }

        var roleEditViewModel = new RoleEditViewModel
        {
            Id = id,
            RoleName = role.Name,
            Users = new List<string>()
        };

        var users=await _userManager.Users.ToListAsync();
        foreach (var user in users)
        {
            if (await _userManager.IsInRoleAsync(user, role.Name))
            {
                roleEditViewModel.Users.Add(user.UserName);
            }
        }

        return View(roleEditViewModel);
    }

    [HttpPost]
    public async Task<IActionResult> EditRole(RoleEditViewModel roleEditViewModel)
    {
        var role = await _roleManager.FindByIdAsync(roleEditViewModel.Id);

        if (role != null)
        {
            role.Name = roleEditViewModel.RoleName;

            var result = await _roleManager.UpdateAsync(role);

            if (result.Succeeded)
            {
                return RedirectToAction("Index");
            }

            ModelState.AddModelError(string.Empty, "更新角色时出错");

            return View(roleEditViewModel);
        }

        return RedirectToAction("Index");
    }

    [HttpPost]
    public async Task<IActionResult> DeleteRole(string id)
    {
        var role = await _roleManager.FindByIdAsync(id);

        if (role != null)
        {
            var result = await _roleManager.DeleteAsync(role);
            if (result.Succeeded)
            {
                return RedirectToAction("Index");
            }
            ModelState.AddModelError(string.Empty, "删除角色时出错");
        }

        ModelState.AddModelError(string.Empty, "没找到该角色");
        return View("Index", await _roleManager.Roles.ToListAsync());
    }

    public async Task<IActionResult> AddUserToRole(string roleId)
    {
        var role = await _roleManager.FindByIdAsync(roleId);

        if (role == null)
        {
            return RedirectToAction("Index");
        }

        var vm = new UserRoleViewModel
        {
            RoleId = role.Id
        };

        var users = await _userManager.Users.ToListAsync();

        foreach (var user in users)
        {
            if (!await _userManager.IsInRoleAsync(user, role.Name))
            {
                // 筛选出候选用户
                vm.Users.Add(user);
            }
        }

        return View(vm);
    }

    [HttpPost]
    public async Task<IActionResult> AddUserToRole(UserRoleViewModel userRoleViewModel)
    {
        var user = await _userManager.FindByIdAsync(userRoleViewModel.UserId);

        var role = await _roleManager.FindByIdAsync(userRoleViewModel.RoleId);

        if (user != null && role != null)
        {
            var result = await _userManager.AddToRoleAsync(user, role.Name);

            if (result.Succeeded)
            {
                return RedirectToAction("EditRole", new { id = role.Id });
            }

            foreach (var error in result.Errors)
            {
                ModelState.AddModelError(string.Empty, error.Description);
            }
            return View(userRoleViewModel);
        }

        ModelState.AddModelError(string.Empty, "用户或角色未找到");
        return View(userRoleViewModel);
    }

    public async Task<IActionResult> DeleteUserFromRole(string roleId)
    {
        var role = await _roleManager.FindByIdAsync(roleId);

        if (role == null)
        {
            return RedirectToAction("Index");
        }

        var vm = new UserRoleViewModel
        {
            RoleId = role.Id
        };

        var users = await _userManager.Users.ToListAsync();

        foreach (var user in users)
        {
            if (await _userManager.IsInRoleAsync(user, role.Name))
            {
                vm.Users.Add(user);
            }
        }

        return View(vm);

    }

    [HttpPost]
    public async Task<IActionResult> DeleteUserFromRole(UserRoleViewModel userRoleViewModel)
    {
        var user = await _userManager.FindByIdAsync(userRoleViewModel.UserId);

        var role = await _roleManager.FindByIdAsync(userRoleViewModel.RoleId);

        if (user != null && role != null)
        {
            if (await _userManager.IsInRoleAsync(user, role.Name))
            {
                var result = await _userManager.RemoveFromRoleAsync(user, role.Name);

                if (result.Succeeded)
                {
                    return RedirectToAction("EditRole", new { id = role.Id });
                }

                foreach (var error in result.Errors)
                {
                    ModelState.AddModelError(string.Empty, error.Description);
                }
                return View(userRoleViewModel);
            }

            ModelState.AddModelError(string.Empty, "用户不在角色里");
            return View(userRoleViewModel);
        }

        ModelState.AddModelError(string.Empty, "用户或角色未找到");
        return View(userRoleViewModel);
    }

}
第十三节:基于角色/策略/声明的授权

基于角色的授权只需要在控制器上加

[Authorize(Roles = "Administrator")]

更改简单授权指定角色名称,则所有登录的用户均可以操作该控制器

[Authorize]

大部分情况下,基于角色的授权可以满足大部分的应用场景,需要更加具体的指定授权范围可以使用基于声明/策略的授权

使用基于声明的授权需要在ApplicationUser中声明一个类型为IdentityUserClaim的字段,并且将其初始化

public async Task<IActionResult> ManageClaims(string id)
{
    var user = await _userManager.Users.Include(x => x.Claims)
        .Where(x => x.Id == id).SingleOrDefaultAsync();
    if (user == null)
    {
        return RedirectToAction("Index");
    }
  
    // 筛选出该 User 没有的 Claim
    var leftClaims = ClaimTypes.AllClaimTypeList.Except(user.Claims.Select(x => x.ClaimType)).ToList();

    var vm = new ManageClaimsViewModel
    {
        UserId = id,
        AvailableClaims = leftClaims
    };

    return View(vm);
}

[HttpPost]
public async Task<IActionResult> ManageClaims(ManageClaimsViewModel vm)
{
    var user = await _userManager.FindByIdAsync(vm.UserId);
    if (user == null)
    {
        return RedirectToAction("Index");
    }
**//这里声明一个IdentityUserClaim的实体,往实体中添加claim类型及值**
    var claim = new IdentityUserClaim<string>
    {
        ClaimType = vm.ClaimId,
        ClaimValue = vm.ClaimId
    };

    user.Claims.Add(claim);

    var result = await _userManager.UpdateAsync(user);
    if (result.Succeeded)
    {
        return RedirectToAction("EditUser", new { id = vm.UserId });
    }

    ModelState.AddModelError(string.Empty, "编辑用户Claims时发生错误");
    return View(vm);
}y

[HttpPost]
public async Task<IActionResult> RemoveClaim(string id, string claim)
{
    var user = await _userManager.Users.Include(x => x.Claims)
        .Where(x => x.Id == id).SingleOrDefaultAsync();
    if (user == null)
    {
        return RedirectToAction("Index");
    }

    var claims = user.Claims.Where(x => x.ClaimType == claim).ToList();

    foreach (var c in claims)
    {
        user.Claims.Remove(c);
    }

    var result = await _userManager.UpdateAsync(user);
    if (result.Succeeded)
    {
        return RedirectToAction("EditUser", new { id });
    }

    ModelState.AddModelError(string.Empty, "编辑用户Claims时发生错误");
    return RedirectToAction("ManageClaims", new { id });
}

利用上面的基于角色的授权以及基于声明的授权,可以满足大部分应用场景。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值