zoukankan      html  css  js  c++  java
  • Asp.Net Core学习笔记:入门篇

    Asp.Net Core 学习

    基于.Net Core 2.2版本的学习笔记。

    常识

    像Django那样自动检查代码更新,自动重载服务器(太方便了)

    dotnet watch run
    

    托管设置

    设置项目文件的AspNetCoreHostingModel属性。

    <PropertyGroup>
        <TargetFramework>netcoreapp2.2</TargetFramework>
        <AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
    </PropertyGroup>
    
    • InProcess:使用IIS服务器托管
    • OutOfProcess:使用自带Kestrel服务器托管

    中间件入门

    • 可同时被访问和请求
    • 可以处理请求后,将请求传递给下一个中间件
    • 可以处理请求后,使管道短路
    • 可以传出响应
    • 中间件是按照添加顺序执行的

    通过在Configure中添加参数ILogger<Startup> logger引入Asp.Net Core自带的日志组件。

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILogger<Startup> logger)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
    
        app.UseStaticFiles();
    
        app.Use(async (context, next) =>
        {
            context.Response.ContentType = "text/plain;charset=utf-8";
    
            //await context.Response.WriteAsync("Hello!");
    
            logger.LogDebug("M1: 传入请求");
            await next();
            logger.LogDebug("M1: 传出响应");
        });
    
    
        app.Use(async (context, next) =>
        {
            context.Response.ContentType = "text/plain;charset=utf-8";
    
            logger.LogDebug("M2: 传入请求");
            await next();
            logger.LogDebug("M2: 传出响应");
        });
    
        app.Run(async (context) =>
        {
            //await context.Response.WriteAsync("你好!");
            await context.Response.WriteAsync("M3: 处理请求,生成响应");
            logger.LogDebug("M3: 处理请求,生成响应");
        });
    }
    

    输出日志:(可以看到三个中间件的执行过程)

    Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/2.0 GET https://localhost:44383/  
    StudyManagement.Startup:Debug: M1: 传入请求
    StudyManagement.Startup:Debug: M2: 传入请求
    StudyManagement.Startup:Debug: M3: 处理请求,生成响应
    StudyManagement.Startup:Debug: M2: 传出响应
    StudyManagement.Startup:Debug: M1: 传出响应
    Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 52.8954ms 200 text/plain;charset=utf-8
    StudyManagement.Startup:Debug: M1: 传入请求
    StudyManagement.Startup:Debug: M2: 传入请求
    StudyManagement.Startup:Debug: M3: 处理请求,生成响应
    StudyManagement.Startup:Debug: M2: 传出响应
    StudyManagement.Startup:Debug: M1: 传出响应
    Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 34.3387ms 200 text/plain;charset=utf-8
    

    静态文件支持

    所有静态文件都在目录wwwroot

    首先

    // 设置默认文件
    // 不设置的话,默认就是index.html/default.html这几个
    var defaultFileOpinions = new DefaultFilesOptions();
    defaultFileOpinions.DefaultFileNames.Clear();
    defaultFileOpinions.DefaultFileNames.Add("test.html");
    
    // 添加默认文件中间件,必须在UseStaticFiles之前注册
    app.UseDefaultFiles(defaultFileOpinions);
    
    // 添加静态文件中间件
    app.UseStaticFiles();
    

    DirectoryBrowser 中间件

    可以在浏览器浏览 wwwroot 下的内容。不推荐在生产环境中使用。

    app.UseDirectoryBrowser();
    

    FileServer 中间件

    集成了UseDefaultFiles, UseStaticFiles, UseDirectoryBrowser三个中间件的功能。同样不推荐在生产环境中使用。

    var fileServerOpinions = new FileServerOptions();
    fileServerOpinions.DefaultFilesOptions.DefaultFileNames.Clear();
    fileServerOpinions.DefaultFilesOptions.DefaultFileNames.Add("test.html");
    
    app.UseFileServer(fileServerOpinions);
    

    开发者异常页面

    if (env.IsDevelopment())
    {
        var developerExceptionPageOptions = new DeveloperExceptionPageOptions();
        // 显示代码行数
        developerExceptionPageOptions.SourceCodeLineCount = 10;
        app.UseDeveloperExceptionPage();
    }
    
    app.Run(async (context) =>
    {
        throw new Exception("自己抛出的异常");
    });
    

    开发环境变量

    • Development:开发环境
    • Staging:演示(模拟、临时)环境
    • Production:正式(生产)环境

    Ops:

    • 使用ASPNETCORE_ENVIRONMENT环境变量设置开发环境。
    • 在开发机上,在launchSettings.json文件中设置环境变量。
    • 在Staging和Production环境时,尽量在操作系统设置环境变量。
    • 使用IHostEnvironment服务访问运行时环境
    • 除了标准环境之外还支持自定义环境(UAT、QA等)

    引入MVC框架

    首先添加MVC服务。

    public void ConfigureServices(IServiceCollection services)
    {
        // 单纯引入核心MVC服务,只有核心功能
        services.AddMvcCore();
        // 一般用这个,功能多
        services.AddMvc();
    }
    

    添加中间件

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILogger<Startup> logger)
    {
        if (env.IsDevelopment())
        {
            var developerExceptionPageOptions = new DeveloperExceptionPageOptions();
            // 显示代码行数
            developerExceptionPageOptions.SourceCodeLineCount = 10;
            app.UseDeveloperExceptionPage();
        }
    
        app.UseStaticFiles();
        app.UseMvcWithDefaultRoute();
    }
    

    MVC路由规则:/控制器名称/方法名称,(不区分大小写)

    例如下面例子的路由是:/home/index

    HomeController代码:

    public class HomeController : Controller
    {
        public string Index()
        {
            return "home controller";
        }
    }
    

    初步了解模型和依赖注入

    定义模型

    public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string ClassName { get; set; }
        public string Email { get; set; }
    }
    

    定义接口

    public interface IStudentRepository
    {
        Student GetById(int id);
        void Save(Student student);
    }
    

    实现接口

    目前还没接入数据库,定义一个假数据的类

    public class MockStudentRepository : IStudentRepository
    {
        private List<Student> _students;
    
        public MockStudentRepository()
        {
            _students = new List<Student>
            {
                new Student { Id=1, Name="小米", ClassName="红米", Email="hello1@deali.cn" },
                new Student { Id=2, Name="华为", ClassName="荣耀", Email="hello2@deali.cn" },
                new Student { Id=3, Name="oppo", ClassName="vivo", Email="hello3@deali.cn" },
            };
        }
    
        public Student GetById(int id)
        {
            return _students.FirstOrDefault(a => a.Id == id);
        }
    
        public void Save(Student student) => throw new NotImplementedException();
    }
    

    注册依赖注入

    Asp.Net Core依赖注入容器注册服务有三种

    • AddSingleton
    • AddTransient
    • AddScoped

    依赖注入的优点

    • 低耦合
    • 高测试性,更加方便进行单元测试
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();
        // 注册依赖注入,将实现类与接口绑定
        services.AddSingleton<IStudentRepository, MockStudentRepository>();
    }
    

    在模型中使用依赖注入

    public class StudentController : Controller
    {
        private readonly IStudentRepository _studentRepository;
    
        // 通过构造函数注入的方式注入 IStudentRepository
        public StudentController(IStudentRepository studentRepository)
        {
            _studentRepository = studentRepository;
    
        }
    
        public JsonResult Index(int id)
        {
            return Json(_studentRepository.GetById(id));
        }
    }
    

    控制器入门

    内容格式协商

    在控制器方法中使用 ObjectResult 返回类型,支持内容协商,根据请求头参数返回数据,

    // 支持内容格式协商
    public ObjectResult Details(int id)
    {
        return new ObjectResult(_studentRepository.GetById(id));
    }
    

    如:

    Accept: application/xml
    

    将返回xml格式。注:还要添加xml序列化器。

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc()
            // 注册XML序列化器
            .AddXmlSerializerFormatters();
    }
    

    视图入门

    将数据从控制器传递到视图的方法

    前两种都是弱类型的

    • ViewData
    • ViewBag
    • 强类型视图

    ViewData

    • 弱类型字典对象
    • 使用string类型的键值,存储和chaxun
    • 运行时动态解析
    • 没有智能感知,编译时也没有类型检查

    使用方法:

    ViewData["Title"] = "学生视图";
    ViewData["Model"] = model;
    

    cshtml代码:

    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title></title>
    </head>
    <body>
        <h1>@ViewData["Title"]</h1>
        @{
            var student = ViewData["model"] as StudyManagement.Models.Student;
        }
        <div>姓名:@student.Name</div>
        <div>班级:@student.ClassName</div>
    </body>
    </html>
    

    ViewBag

    // 直接给动态属性赋值
    ViewBag.PageTitle = "ViewBag标题";
    ViewBag.Student = model;
    

    cshtml使用:

    <h1>@ViewBag.PageTitle</h1>
    <div>姓名:@ViewBag.Student.Name</div>
    <div>班级:@ViewBag.Student.ClassName</div>
    

    强类型视图

    在控制器中传给View()模型

    public IActionResult GetView()
    {
        var model = _studentRepository.GetById(1);
        return View(model);
    }
    

    在cshtml中指定模型类型

    @model StudyManagement.Models.Student
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title></title>
    </head>
    <body>
        <h1>强类型模型</h1>
        <ul>
            <li>@Model.Id</li>
            <li>@Model.Name</li>
            <li>@Model.ClassName</li>
            <li>@Model.Email</li>
        </ul>
    
    </body>
    </html>
    

    ViewModel 视图模型

    类似于DTO(数据传输对象)

    定义ViewModel

    public class StudentDetailsViewModel
    {
        public Student Student { get; set; }
        public string PageTitle { get; set; }
    }
    

    修改控制器

    public IActionResult Details()
    {
        var model = _studentRepository.GetById(1);
        var viewModel = new StudentDetailsViewModel
        {
            Student = model,
            PageTitle = "viewmodel里的页面标题"
        };
        return View(viewModel);
    }
    

    在View中使用

    <!-- 这里注册的模型改成了ViewModel了 -->
    @model StudyManagement.ViewModels.StudentDetailsViewModel
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title></title>
    </head>
    <body>
        <h1>强类型模型</h1>
        <h2>@Model.PageTitle</h2>
        <ul>
            <li>@Model.Student.Id</li>
            <li>@Model.Student.Name</li>
            <li>@Model.Student.ClassName</li>
            <li>@Model.Student.Email</li>
        </ul>
    </body>
    </html>
    

    View中使用循环

    @model IEnumerable<StudyManagement.Models.Student>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title></title>
    </head>
    <body>
        <table border="1">
            <tr>
                <td>Id</td>
                <td>姓名</td>
                <td>班级</td>
                <td>邮箱</td>
            </tr>
            @foreach (var student in Model)
            {
                <tr>
                    <td>@student.Id</td>
                    <td>@student.Name</td>
                    <td>@student.ClassName</td>
                    <td>@student.Email</td>
                </tr>
            }
        </table>
    </body>
    </html>
    

    布局视图 LayoutView

    创建布局视图

    <!DOCTYPE html>
    
    <html>
    <head>
        <meta name="viewport" content="width=device-width" />
        <title>@ViewBag.Title</title>
    </head>
    <body>
        <div>
            @RenderBody()
        </div>
    
        @RenderSection("Scripts", required: false)
    </body>
    </html>
    

    渲染视图

    @model IEnumerable<StudyManagement.Models.Student>
    @{
        Layout = "~/Views/Shared/_Layout.cshtml";
        ViewBag.Title = "首页 学生列表";
    }
    <div></div>
    

    视图节点 Section

    在布局视图里渲染节点

    @RenderSection("Scripts", required: false)
    

    在普通视图里定义节点

    @section Scripts{ 
        <script>
            document.write("hello");
        </script>
    }
    

    视图开始 ViewStart

    我的理解就是_ViewStart.cshtml文件所在目录下的每个视图文件开始渲染先执行这个文件的内容。一般直接放在Views目录下,全局生效,可以放在各个子文件夹下,这样可以覆盖全局的_ViewStart.cshtml

    @{
        Layout = "_Layout";
    }
    

    视图导入 ViewImports

    用来导入命名空间、注册模型等等n多种操作。

    生效机制和ViewStart差不多。

    路由

    • 常规路由(传统路由)
    • 属性路由

    常规路由

    MapRoute方法中传入就好了。

    // 自定义路由
    app.UseMvc(route =>route.MapRoute("default",
    	"{controller=Home}/{action=Index}/{id?}"));
    

    属性路由

    比传统路由更加灵活,可以搭配传统路由使用。

    即在控制器方法上添加路由注解,一个方法可以同时映射多个路由。

    [Route("Home/Index")]
    public IActionResult Index()
    {
        return View(_studentRepository.GetAll());
    }
    

    路由中也可以指定参数

    [Route("test/{id?}")]
    public IActionResult Details(int id = 1)
    {
        var model = _studentRepository.GetById(id);
        var viewModel = new StudentDetailsViewModel
        {
            Student = model,
            PageTitle = "viewmodel里的页面标题"
        };
        return View(viewModel);
    }
    

    可以直接在控制器类上加注解,[controller]/[action]

    欢迎交流

    交流问题请在微信公众号后台留言,每一条信息我都会回复哈~

  • 相关阅读:
    [LeetCode] 67. 二进制求和
    [LeetCode] 66. 加一
    [LeetCode] 65. 有效数字
    [LeetCode] 63. 不同路径 II
    [LeetCode] 64. 最小路径和
    [LeetCode] 61. 旋转链表
    [LeetCode] 62. 不同路径
    [LeetCode] 59. 螺旋矩阵 II
    [LeetCode] 60. 第k个排列
    [LeetCode] 58. 最后一个单词的长度
  • 原文地址:https://www.cnblogs.com/deali/p/13933356.html
Copyright © 2011-2022 走看看