ASP.NET Core学习笔记1

ASP.NET Core学习笔记1

RESTful

定义

ASP.NET Core框架被广泛用于创建RESTful服务,但是也可以创建非RESTful服务;

REST指的是一组架构约束条件和原则,满足这些约束条件和原则的应用程序或设计就是RESTful;

REST首次出现于Roy Fielding于2000年发表的博士论文;

RESTful就是一猴子那个架构模式,用于创建使用HTTP作为通信方式的API;

REST体系结构模式制定了系统应遵循的一组约束,即REST约束;

RESTful约束用于限制服务器端只能遵循这些约束来处理和响应客户端的请求;

遵循这些约束可以获取理想 的非函数化属性,如性能、可伸缩性、简单程度、可变能力、可见度、灵活度、可信度;

如果任何一个服务违背了任何一个原则,就不能称作RESTful系统;

REST约束

(1) 客户端服务器约束:这是第一个约束;客户端发送请求,服务端发送响应;服务器架构背后的原则:关注点分离;通过分离用户界面和数据存储这两个关注点,提高了用户界面跨平台的可能性,通过简化服务器组件提高了其可伸缩性;

(2) 无状态约束:客户端和服务器之间的通信请求之间必须是无状态的;这意味着我们不应该在与客户端相关的服务器上存储任何内容;来自客户端的请求应包含服务器处理该请求的所有必要信息;这样可以确保服务器独立处理每个请求;

(3) 可缓存约束:服务器提供的某些数据不会经常更改;该约束表明,应该让客户端知道该数据的有效期是多长,这样客户端就不用一次又一次的通过服务器获取数据了,比如网页缓存;

(4) 统一接口约束:定义了客户端和服务器之间的接口;要了解统一的接口约束,需要知道什么是资源、什么是HTTP谓词(GET、POST、PUT、DELETE);在RESTful API的数据库上下文中,资源通常代表数据实体;每个请求发送的HTTP谓词,告诉API如何处理资源;每个资源都由特定的URI(统一资源定位符)进行标识;

继承谁?

Controller是继承自ControllerBase;

Controller已经添加了对视图的支持;

Web API服务不需要处理视图,因此继承ControllerBase即可;

如果某个服务既要满足视图要求,又要满足Web API,就要继承Controller;

绑定源

1. [FromBody]请求的Body;

2. [FromForm]请求的Form表单数据;

3. [FromHeader]请求的头数据;

4. [FromQuery]请求的Query string参数;

5. [FromRoute]请求的路由数据;

6. [FromService]作为Action参数而注入的服务;

默认绑定规则

[ApiController]更改后的规则

① [FromBody]通常用来推断复杂类型的参数;

② [FromForm]通常用来推断IFormFile和IFormFileCollection类型的Action参数;

③ [FromRoute]用来推断Action的参数名和路由模板中的参数名是否一致;

④ [FromQuery]用来推断其他的Action参数;

数据绑定

FromQueryAttribute: 通过调用WebAPI的URL参数绑定Action参数数据。

FromRouteAttribute: 通过调用WebAPI的URL的路由参数绑定Action参数数据。

FromBodyAttribute:通过Web请求中的body段数据绑定Action参数数据,一个Action方法,只能使用一次FromBodyAttribute属性。

Attribute

■特性attribute:就是一个类,直接,间接继承自Attribute,一般以attribute结尾,标记时可以省略

■特性有什么用:声明--标记

给属性添加特性后,在属性内部产生了一个.custom的元素;

■特性是怎么生效的?

C#反射reflection 可以获取并使用metadata;

在程序运行额过程中通过反射,可以检测到特性,获取特性实例,使用特性的实例;

■ServiceContract为什么可以变成服务呢?

ServiceContract标记到接口,检测ServiceContract特性,有才展示,没有的不展示;

特性attribute本身没有价值,必须在程序运行的过程中,用反射主动发现并使用才有价值

面向切面

AOP又叫"面向切面编程"Aspect Oriented Programming,是对传统的面向对象编程的一个补充;

主要的操作对象就是"切面",可以简单的理解它是贯穿于方法之中,在方法执行前、执行时、执行后、返回值后、异常后要执行的操作。

相当于将我们原本一条线执行的程序在中间切开加入一些其他操作

在应用AOP编程时,任然需要定义公共功能,但可以明确定义这个功能应用在哪里,以什么方式应用,并且不必修改受影响的类。

这样一来横切关注点就被模块化到特殊的类里——这样的类我们通常称为“切面”。

AOP为Aspect Oriented Programming的缩写。

AOP 面向切面编程;

在不破坏封装的前提下,去增加额外的功能

Razor 代码混写

定义

Razor 不是一种编程语言。它是服务器端的标记语言。可以让您将基于服务器的代码(Visual Basic 和 C#)嵌入到网页中。

当服务器读取页面时,它首先运行 Razor 代码,然后再发送 HTML 页面到浏览器。在服务器上执行的代码能够执行一些在浏览器上不能完成的任务,比如,访问服务器数据库。服务器代码能创建动态的 HTML 内容,然后发送到浏览器。从浏览器上看,服务器代码生成的 HTML 与静态的 HTML 内容没有什么不同。

什么是Razor ?

Razor 不是一种编程语言,而是一种标记语法,可以将基于服务器的代码(Visual Basic 和 C#)嵌入到网页中。

Razor 是基于 ASP.NET 的,是为创建 Web 应用程序而设计的。

Razor支持代码混写

带 Razor 语法的 ASP.NET 网页有特殊的文件扩展名cshtml(Razor C#)或者vbhtml(Razor VB)。

语法

l Razor 代码块包含在 @{ ... } 中 ,一行或多行。

l 内联表达式(变量和函数)以 @ 开头 :@变量 或 @( 表达式 )

l 代码语句用分号结束

l 变量使用 var 关键字声明

l 字符串用引号括起来

l C# 代码区分大小写

l C# 文件的扩展名是 .cshtml

l @{...}中的内容都会被视为C#代码

l @{...}中添加纯文本@ {<text>abc</text> @: abc}

l 注释:使用@**@ 在@{}中使用C#的注释

作用域

大括号里面的就是表示作用域的范围,用形如 “@{code}”来写一段代码块。在作用域 “@(code)” 中输出也是用@符号的。

我的年龄:

@{

int age = 25;

string sex = "男";

@age

}性别: @(sex)

混编

Razor和Html混合编写

a.在作用域内容如果是以html标签开始则视为文本输出

b.如果输出@,则使用@@

c.如果需在代码块中直接输出纯文字而不带HTML标签,则可以使用@:标签,如果在 "@:" 后面加上@就表示Razor语句的变量。

@{

var strzm = "abc";

@:this is a mail:2734796332@qq.com.this is var:@strzm,this is mail@strzm,this is @@

//输出abc

@strzm

}

变量

一个变量的名称必须以字母字符开头,并且不能包含空格或者保留字符。变量使用 var 关键字声明,或通过使用具体类型声明。

// Using the var keyword:

var greeting = "Welcome to RUNOOB.COM";

var counter = 103;

// Using data types:

string greeting = "Welcome to RUNOOB.COM";

int counter = 103;

数据类型

int 整数(全数字) 103, 12, 5168

float 浮点数 3.14, 3.4e38

decimal 十进制数字(高精度) 1037.196543

bool 布尔值 true, false

string 字符串 "Hello RUNOOB.COM", "John"

类型转换

转换数据类型(string类型才能转)

AsInt()\IsInt(): 转换字符串为整数

if (myString.IsInt()){

myInt=myString.AsInt();

}

AsFloat()\IsFloat(): 转换字符串为浮点数。

AsDecimal()\IsDecimal():转换字符串为十进制数。

AsDateTime()\IsDateTime(): 转换字符串为 ASP.NET DateTime 类型。

AsBool()\IsBool(): 转换字符串为布尔值。

ToString(): 转换任何数据类型为字符串。

var myInt=1234;

myString=myInt.ToString();

循环

■For 循环

<body>@for(var i = 10; i < 21; i++) {<p>Line @i</p>}</body>

■For Each 循环

<body><ul>@foreach (var x in Request.ServerVariables){<li>@x</li>}</ul></body>

■While 循环

<body>@{ var i = 0; while (i < 5) {i += 1; <p>Line @i</p> } }</body>

逻辑条件

■If 条件

@{var price=50;}

<html><body>

@if (price>30){<p>The price is too high.</p>}

</body></html>

■If Else 条件

@{var price=20;}

<html><body>

@if (price>30){<p>The price is too high.</p> } else { <p>The price is OK.</p>}

</body></html>

数组

<p>Kai is now in position</p>

@{

string[] members = {"Jani", "Hege", "Kai", "Jim"};

int i = Array.IndexOf(members, "Kai")+1;

int len = members.Length;

string x = members[2-1];

}

<html><body>

<h3>Members</h3>

@foreach (var person in members){<p>@person</p>}

<p>The number of names in Members are @len</p>

<p>The person at position 2 is @x</p>

<p>Kai is now in position @i</p>

</body></html>

布局Layout

RenderBody

布局(Layout)(@RenderBody()方法)

解释:Layout方式布局就相当于一个模板,我们在它地址地方去添加代码。类似于ASP.NET母版页中的ContentPlaceHolder服务器控件,在MVC中使用@RenderBody()来呈现子Web页面的内容,它可以省去我们在每个视图文件中写相同的html元素、JS和样式等的工作。

母版页:(~/Views/Layout/_SiteLayout.cshtml)

母版的呈现是MVC内部处理的,这种以下划线(_)开头的视图文件,一般是不能直接返回给用户。

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="utf-8"/>

<title>我的网站 - @Page.Title</title>

</head>

<body> @RenderBody() </body>

</html>

子页面:(~/Views/Home/About.cshtml)

@{ Layout = "~/Views/Layout/_SiteLayout.cshtml";}

<h1> 关于我的网站</h1>

<p>

这是一些内容显示在关于我们这个页面,我们用的是SiteLayout.cshtml这个主页母版页。

<br />

当前时间:@DateTime.Now

</p>

布局Layout

RenderPage

页面(@RenderPage()方法)

解释:page当需要在一个页面中,输出另外一个Razor文件(页面)的内容时候用到,比如头部或尾部这些公共的内容时需要用到,用@RenderPage()方法

母版页:(~/Views/Layout/_SiteLayout.cshtml)

<!DOCTYPE html>

<html><head>

<meta name="viewport" content="width=device-width" />

<title>Simple Site</title>

</head><body>

<!--头部-->

@RenderPage("~/Views/Layout/_header.cshtml")

</body>

</html>

公共页:(~/Views/Layout/_header.cshtml)

<div id="header">

<a href="#" rel="external nofollow" rel="external nofollow" >主页</a>

<a href="#" rel="external nofollow" rel="external nofollow" >关于我们</a>

</div>

布局Layout

RenderSection

Section区域(@RenderSection())

解释:Section是定义在Layout的页面中使用的。在Layout的页面中用。在要Layout的父页面中使用@RenderSection()方法。

@RenderSecion()这个占位符表示:在这里会渲染页面里面的一个节(可以是html代码也可以是c#代码和Html的结合体)。

@RenderSection("scripts", required: false)做一个补充说明,第一个参数指明:在子页面被渲染的节的名称,第二个参数:指定子页面这个节是否是必需的,如果指定了required:true;但是在子页面没有给这个名称的节的话,编译是通不过的。

母版页:(~/Views/Layout/_SiteLayout.cshtml)

<!DOCTYPE html>

<html><head>

<meta name="viewport" content="width=device-width" />

<title>Simple Site</title>

</head><body>

<div id="left-menu"> @RenderSection("menu",true) </div>

</body></html>

公共页:(~/Views/Layout/_menu.cshtml)

@{

Layout = "~/Views/Layout/_SiteLayout.cshtml";

}

<h1> 关于我的网站</h1>

<p> 这是一些内容显示在关于我们这个页面,我们用的是SiteLayout.cshtml这个主页母版页。

<br /> 当前时间:@DateTime.Now</p>

@section menu{

<ul id="sub-menu"> <li>菜单1</li> <li>菜单2</li> <li>菜单3</li> <li>菜单4</li> </ul> }

如果在子页面中没有去实现了menu了,则会抛出异常。我们可以使用它的重载@RenderSection("menu", false)

@if (IsSectionDefined("menu")) { @RenderSection("menu", false) } else { <p>menu Section is not defined!</p> }

Razor 帮助器

ASP.NET 帮助器是通过几行简单的 Razor 代码即可访问的组件。您可以使用 Razor 语法构建自己的帮助器,或者使用内建的 ASP.NET 帮助器。

下面是一些有用的 Razor 帮助器的简短说明:

1. Web Grid(Web 网格)

2. Web Graphics(Web 图形)

3. Google Analytics(Google 分析)

4. Facebook Integration(Facebook 集成)

5. Twitter Integration(Twitter 集成)

6. Sending Email(发送电子邮件)

7. Validation(验证)

@Href("~/")//表示网站的根目录

@Html.Raw('<font color='red'>红字</font>')就会显示出红色的”红字“,不用的话会直接显示这段html字符串(<font color='red'>红字</font>)

自定义Helper

@helper就是可以定义可重复使用的帮助器方法,不仅可以在同一个页面不同地方使用,还可以在不同的页面使用。

1、新建一个HelperMath.cshtml页面

2、HelperMath.cshtml页面写方法

@*求和*@

@helper sum(int a, int b)

{

int result = a + b;

@result

}

3、Index.cshtml页面调用

1+2= @HelperMath.sum(1, 2)

<br />

语法2

Razor C#基本语法规则

① 使用@将代码块添加到页面中

■内联表达式(Inline expressions)

■单语句块(Single statement blocks)

■多语句块(Multi-statement block)

<!-- Inline expressions -->

<p>You are using @Request.Broswer.Broswer!</p>

<!-- Single statement blocks -->

@{ ViewBag.title = "Home Page"; }

@{ var myMessage = "Hello World"; }

<!-- Multi-statement block -->

@{

var name = "Jason";

var greeting = "Nice to meet you, ";

var greetingMessage = greeting + name;

}

<p>The greeting is: @greetingMessage</p>

② 代码块括在大括号中,代码语句用分号结束

③ 使用 var 关键字,声明变量存储值

④ 字符串要用引号括起来

@{ var myString = "This is just an example"; }

⑤ C#代码是区分大小写

⑥ 空格和换行符不影响语句

可以通过增加空格或者换行符提高代码的可读性。

但是对于字符串,不可以

@{ var test = "This is a long

string"; } // Does not work!

⑦ 内联的helper方法

@helper formatAmount(decimal amount)

{

var color = "green";

if (amount < 0)

{

color = "red";

}

<span style="color:@color">@String.Format("{0:c}", amount)</span>

}

然后可以在其他地方使用helper方法,比如:

@{var amounts = new List<decimal> {100, 25.50m, -40, 276.99m}

}

<ul>

@foreach(decimal amount in amounts)

{

<li>@formatAmount(amount)</li>

}

</ul>

⑧ @{}中的内容都会被视为C#代码

如果想要添加纯文本,两种方法

@ {

//方法1

■<text>djskfadsfhadsjfk</text>

//方法2

■@: fhdshfjskhfksfs

}

输出@符号

@ { <p>Have a good weekend @@LA</p> }

//output: Have a good weekend @LA

⑨ 注释

■使用@**@

@* A one-line code comment. *@

@*

This is a multiline code comment.

It can continue for any number of lines.

*@

■在@{}中使用C#的注释格式

@{

// This is a comment.

var myVar = 17;

/* This is a multi-line comment

that uses C# commenting syntax. */

}

模型绑定、模型验证

定义

模型绑定,就是将HTTP请求中的数据,映射到操作方法的对应参数上

MVC将请求的数据绑定到操作方法对应的参数中,这个过程就叫模型绑定;

查找数据

模型绑定将按照下列顺序查找HTTP请求中的数据:

Form Values 表单中的数据

Route Values路由中的值

Query Strings 查询字符串

验证属性

ASP.Net Core内置的模型验证属性

属性

作用

Required

该字段是必须的

Range

指定允许的最大值和最小值

MinLength

指定字符串最小长度

MaxLength

指定字符串最大长度

Compare

比较模型的2个属性

例如,比较Email和ConfirmEmail属性;

RegularExpression

正则表达式,验证提供的值是否匹配正则表达式

例子

[Display(Name=”名字”)]

[Required(ErrorMessage=”请输入名字,不能为空”)]

public string Name{get;set;}

[Display(Name=”电子邮箱”)]

[RegularExpression(@”^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-]+$”,

ErrorMessage(“邮箱格式不正确”))]

[Required(ErrorMessage(“请输入邮箱,不能为空”))]

public string Email{get;set;}

TageHelper

定义

TagHelper是服务器端的组件,他们在服务器上运行;

TagHelper在Razor文件中创建和渲染HTML元素

TagHelper类似于HTML TagHelper;

TagHelper可以提高生产效率,生成更加稳定、可靠、可维护的代码;

导入

_ViewImports.cshtml文件中导入TagHelper

@addTagHelper *,Microsoft.AdpNetCore.Mvc.TagHelpers

*表示要导入MVC中的所有TagHelper;

Microsoft.AdpNetCore.Mvc.TagHelpers是内置的TagHelper组件的位置;

示例

<a href=”/home/details/@student.Id”>查看</a>

@Html.ActionLink(“查看”,”details”,new{id=student.Id})

@Url.Action(“details”,”home”,new{id=student.Id})

<a asp-controller=”home”asp-action=”details” asp-route-id=”@student.Id”>查看</a>

优点

TagHelper是根据应用程序的路有模板自动生成的链接;

如果更改路由模板,则TagHelper会针对新的路有模板进行自动修改和适配;

路由模板修改后,TagHelper生成的链接依然能够正常工作;

如果硬编码超链接,当修改 路由模板时,必须在很多地方修改对应的超链接,这样很傻;

Image

Image TagHelper,服务器上的图片更改了就从服务器下载图片,否则就从本地缓存加载图片;

<img src=”~images/noimage.png”asp-append-version=”true”/>

Image TagHelper增强了img的标签属性,为静态图像文件提供了缓存清除行为,通过散列计算生成唯一的散列值并将其附加到img的URL中。

唯一的散列值会提示客户端从服务器重新加载图片,而不是从浏览器的缓存重新加载;

只有当服务器上的图片更改时,才会计算并缓存新的散列值;

如果图片未更改,则不会重新计算散列值,使用此散列值,浏览器会跟踪服务器上的图片内容是否已更改;

Environment TagHelper,根据不同的环境呈现不同的内容;

TagHelper

Form TagHelper

Label TagHelper

Input TagHelper

Select TagHelper

Textarea TagHelper

Validation TagHelper

ASP.Net Core MVC中的路由

两种路由技术

常规路由和属性路由

路由的定义

浏览器的请求到达应用程序时,MVC中的控制器会处理传入的HTTP请求,并响应用户操作;

请求的URL会被映射到控制器的操作方法上,这个映射过程就是由应用程序的路由规则来完成;

举例

http:://localhost:12345/Home/Index,其中的Home会映射到HomeController控制器类,而Index会映射到HomeController类中的Index()方法;

Home/Details/1会映射到HomeController类中的Details()方法,方法的参数是1;也就是HomeController类→Details(id=1)

默认路由

在Startup.cs文件中的Configure()方法中:

public Config(IApplicationBuilder app,IHostingEnvironment env)

{

if(env.IsDevelopment()){app.UseDeveloperExceptionPage();}

app.UseStaticFiles();

app.UseMvcWithDefaultRout();

}

其中的UseMvcWithDefaultRout()就将MVC和默认路由添加到了应用程序的请求处理管道中;

默认路由规则是{controller=Home}/{action=Index}/{id?}

使用默认路由的代码:

public static IapplicationBuilder UseMvcDefaultRout(this IapplicationBuilder app)

{

if(app==null){throw new ArgumentNullException(nameof(app));}

return app.UseMvc(routs=>

{

routs.MapRout(name:”default”,template:”{controller=Home}/{action=Index}/{id?}”;)

})

}

默认路由举例

http://localhost:1234/Student/Details/1

这个映射过程称为模型绑定;

id后面的?表示该参数可选;

Student

StudentController类

Details

Details(int id)方法

/1

id参数

{controller=Home}表示controller的默认值是Home;

{action=Index}表示action的默认值是Index;

{id?}表示id是一个可选参数;

http://localhost:1234程序导航到应用程序根目录,则会使用默认的controller值和默认的action值;

自定义路由

使用UseMvc()方法而不是使用UseMvcWithDefaultRout();

app.UseMvc(routes=>

routes.MapRoute(“default”,”{controller=Home}/{action=Index}/{id?}”);)

属性路由

public class HomeController:Controller

{

[Route(“”)]

[Route(“Home”)]

[Route(“Home/Index”)]

public ViewResult Index(){return View();}

}

[Route(“”)]

http://localhost:4560/

[Route(“Home”)]

http://localhost:4560/Home

[Route(“Home/Index”)]

http://localhost:4560/Home/Index

3种路由规则都会访问HomeController类中的Index()方法;

可选参数

public class HomeController:Controller

{

private IStudentRepository _studentRepository;

//使用构造注入IStudentRepository

public HomeController(IStudentRepository studentRepository)

{_studentRepository=studentRepository;}

//使用?表示id参数是可选参数,如果没有?,则id是必选参数

//?使得id参数可以为空

[Route(“Home/Details/{id?}”)]

public IActionResult Details(int ? id)

{

HomeDetailsViewModel homeDetailsViewModel=new HomeDetailsViewModel

//id??1表示,如果id不是空就使用id,如果id为空,就是用1

//相当于三元表达式id==null?1:id

{Student=_studentRepository.GetStudent(id??1),PageTitle=”学生信息”;}

return View(homeDetailsViewModel);

}

名称问题

[Route(“WC”)]

[Route(“NH/Index”)]

public string Welcome(){return “hello world”;}

属性路由中的名称和控制器名称、操作方法名称之间没有强关联关系;

多层属性路由

可以在控制器和操作方法分别标识各自的路由,而无需在控制器中的每个操作方法中标识完整路径的路由信息;

[Route(“Home/Index”)]

[Route(“Home/Student”)]

在控制器上标识控制器的路由[Route(“Home”)]

而在两个操作方法上标识各自的Action路由即可,无需多次重复标识Home路由信息;

[Route(“Index”)]

[Route(“Student”)]

如果操作方法路由上以/或者~开头,则Controller路由不会与Action路由组合在一起;

/或者~都表示从根目录开始查找该操作方法;

动态属性路由

[Route(“[controller]”)]标注在控制器上,[controller]就会自动替换为该控制器的名称;

[Route(“[action]”)]标注在操作方法上[action]就会自动替换为操作方法的名称;

方括号支持标记替换,如果修改了控制器或者操作方法的名称,就无需再次修改对应的Route;

对比

常规路由

属性路由

用于HTML页面控制器

用于RESTful API控制器

灵活性一般

灵活性更强

一次定义,终生使用

每次都要单独定义

布局视图

原因

大多数的Web应用程序网站通常包含以下部分:Header头部,Footer页脚,Menu导航菜单,View具体内容视图;

为了所有页面具有相同的布局,其中一个方法是每个页面视图都添加相同的元素,这将导致每个页面都具有很多重复性元素,最为严重的是,一旦修改布局,则要修改成百上千的视图页,维护工作量相当大;

另个一方法就是使用布局视图技术 ,该布局视图拥有通用的布局元素,具体的视图页中只需编写不同的显示代码即可,也就是说,布局视图相当于一个框架,对于不同的内容,均套在这个框架里面,从而保证所有视图的布局一致性;

代码

<!DOCTYPE html>

<html>

<head>

<meta name="viewport" content="width=device-width" />

<title>@ViewBag.Title</title>

</head>

<body>

<div>

@RenderBody()

</div>

</body>

</html>

使用布局视图

布局视图的文件名和普通视图一样,都是cshtml;

使用布局视图:

方法一:在每个视图页中添加Layout属性;

@{Layout=”~/Views/Shared/_Layout.cshtml”;ViewBag.Title=”Student Details”;}

方法二:在每个视图中使用渲染节点;

@RenderSection(“Scripts”,required:false);

@if(IsSection(“Scripts”)){@RenderSection(“Scripts”,required:false);}

布局视图优先级

_ViewStart.cshtml文件,支持分层,优先级如下:

1. 视图文件中的Layout属性;

2. 同文件夹中的_ViewStart.cshtml文件;

3. 父文件夹中的_ViewStart.cshtml文件;

4. 父父文件夹中的_ViewStart.cshtml文件;

这样,就可以实现,在不同的目中层次中的视图文件,使用不同的布局;

可以创建多个不同的视图文件,以适应不同的场合:

_AdminLayout.cshtml

_NonAdminLayout.cshtml

或者在同一个布局文件中,通过条件判断,以使用不同的布局;

@{

if(User.IsInRole(“Admin”)){Layout=”_AdminLayout”;}

else {Layout=”_NonAdminLayout”;}

}

命名空间导入

_ViewImport.cshtml文件,向所有视图文件添加相同的命名空间;

该文件通常放在Views文件夹中;当然也可以放在各级子文件夹中;

_ViewImport.cshtml文件同样支持分层功能,优先级顺序同_ViewStart.cshtml文件;

@using MockSchoolManagement.Models

@using MockSchoolManagement.ViewModels

支持的指令

_ViewImport.cshtml文件支持的@指令包括:

1) @using

2) @addTagHelper

3) @removeTagHelper

4) @tagHelperPrefix

5) @model

6) @inherits

7) @inject

ASP.Net MVC中的@与<% %>

区别

@

<% %>

视图引擎

Razor(cshtml)

ASPX(C#)

位置

@ 后面放置C#代码

<% %>之间放置C#代码

作用

嵌入C#代码,实现html和C#混编

嵌入C#代码,实现html和C#混编

优劣

写法简洁

写法复杂

ASP.NET中<%=%>、<%%>、<%@%>、<%#%>的用法与区别

1、<%= %>

嵌入C#表达式

里面放变量名,获取后台的变量值,直接输入变量到页面上,里面放的变量名,未经过encode

eg: 后台: seession["ab"]=ab; 前台:<%= session["ab"] %> === 取值

<%:%> 里面放的变量名,经过encode

<%=%> <% = expression %> 用于解析表达式

2、<% %>

嵌入C#代码

<%%>之间可以写服务器端代码,中间一般放函数或方法,典型的asp程序写法

<% %>嵌入式代码块是在呈现页面的过程中执行的服务器代码。

块中的代码可以执行编程语句,并调用当前页类中的函数。

eg: <% for(var i=0;i<10;i++) { } %>

后台:public string GetString(){}

前台:<% GetString(); %>

3、<%@%>

表示引用

<%@...%>这个是页面指令,一般放在每个页面的最顶部,对页面的运行进行控制,如设置缓存,引用用户控件,导入命名空间

eg:<%@ Page Language="C#" %>

4、<%# %>

数据绑定

服务器端控件的数据上下文绑定,只能用在数据绑定控件中

Model→View数据传递

方式

数据从Controller传递到View的方法

  • 使用ViewData;

  • 使用ViewBag;

  • 使用强类型模型对象,也称为强类型视图;

视图发现

HomeController对应Home文件夹、StudentController对应Student文件夹;

所有HomeController的视图都位于Views文件夹中的Home文件夹内;

所有StudentController的视图都位于Views文件夹中的Student文件夹内;

Controller中的每一个操作方法对应一个cshtml视图文件;

Details()方法对应Details.cshtml文件,Edit()方法对应Edit.cshtml视图文件;

举例

HomeController类;

Details()方法会调用View()方法,返回一个视图;

View()方法是由基类Controller提供的;

Details()操作方法会返回一个视图,因此默认情况下,MVC会查找具有相同名称且扩展名为.cshtml的视图文件;

按照指定顺序在以下两个位置查找:/Views/Home/文件夹中,/Views/Shared/文件夹;

如果找不到视图文件,则会报错;The view ‘Details’was not found;

自定义

按名称查找

自定义视图发现(指定名称查找):

View(string viewName)的重载版本;

public ViewResult Details(){return View(“Test”);}

MVC就会查找Test.cshtml而不是Details.cshtml的视图文件;

如果没有找到指定的视图名称,就会按照操作方法名去查找Details.cshtml;

按路径查找

public ViewResult Details(){return View(“MyViews/Test.cshtml”);}

public ViewResult Details(){return View(“/MyViews/Test.cshtml”);}推荐

public ViewResult Details(){return View(“~/MyViews/Test.cshtml”);}推荐

使用绝对路径,必须加上视图的扩展名.cshtml;

使用绝对路径,MVC会从项目的根目录开始搜索;

public ViewResult Details(){return View(“../../MyViews/Test.cshtml”);}

View(object model)将模型数据从控制器传递到视图;

View(string viewName,object model)指定视图名称,和要传递的模型数据;

ViewData

ViewData是弱类型的字典对象;

使用string类型的键值来存储和查询ViewData字典中的数据;

可以从ViewData直接访问数据,而无需转换为string类型;

如果访问的是其他类型数据,则要显式转换为需要的类型;

ViewData在运行时进行动态解析,而不会提供编译时的检查,因此没有智能提示;

这会导致代码编写速度降低,容易出现拼写错误;

并且只能在运行时知道这些错误;

因此,不常使用ViewData;

当使用ViewData的时候,要创建一个弱类型的视图;

ViewBag

ViewBag是ViewData的包装器;

ViewData通过string类型的键值来存储查询数据;

ViewBag,使用动态属性而不是字符串键值;

要将ViewBag的数据从HomeController的Details()操作方法传递到View;

在View中,同样使用动态属性访问数据成员;

对比

ViewData和ViewBag的对比:

二者都可以将Controller数据传递到View;

二者都是弱类型的视图;

二者都是在运行时动态解析;

二者不提供编译时类型检查,不能的到智能提示;

ViewBag是ViewData的包装器;

ViewData使用字符串键值,ViewBag使用动态属性;

强类型视图

@model MockSchoolManagement.Models.Student

@Model.Name @ViewBag.PageTitle @Model.Email @Model.Major

优点:

提供编译时的类型检查和智能提示;

减小错误几率,提高工作效率;

因此,建议始终使用强类型视图,将数据从控制器传递到视图

ViewModel

Model对象可能无法包含所有数据;

无法包含的数据要通过ViewData或者ViewBag传递,又变成了弱类型视图,又容易出错了;

创建一个ViewModel对象,将所有数据包含进去;

通常创建一个文件夹ViewModels,专门存放所有ViewModel;

布局视图

Header头部、Footer页脚、Menu导航菜单、View具体内容的视图;

如果没有布局视图,那么将在每个视图中重复显示很多内容;

使用布局视图,会让所有视图布局一致,并且便于修改;

布局视图也是一个后缀为.cshtml的文件,默认文件名_Layout.cshtml;

这种下划线开头的视图文件,不是直接面向浏览器的,因此用户无法直接访问他们;

布局视图相当于WebForm中的母版页;

布局视图不属于特定控制器,因此放在Views/Shared/文件夹中;

一个ASP程序可以包含多个布局视图;

_ViewStart.cshtml

_ViewStart.cshtml可以放置多份;

支持分层功能,文件夹中的视图优先使用本文件夹中的_ViewStart.cshtml文件,本文件夹没有,再逐层向上调用;

或者本文件夹中的_ViewStart.cshtml会覆盖掉上级文件夹中的_ViewStart.cshtml;

这样,可以为不同文件夹中的视图指定不同的布局了;

子文件中的_ViewStart.cshtml将覆盖Views文件夹中的_ViewStart.cshtml

_ViewImports.cshtml

用于包含公共命名空间,因此不必在每个视图中引用这些命名空间;

@using指令用于包含公共命名空间;

_ViewImports.cshtml还支持以下指令:

@addTagHelper @removeTagHelper @tagHelperPrefix @model @inherits @inject

_ViewImports.cshtml也支持分层功能,除了可以放在Views文件夹中,在其他子文件夹中放置不同的_ViewImports.cshtml;

子文件中的_ViewImports.cshtml将覆盖Views文件夹中的_ViewImports.cshtml

路由

MVC中的两种路由技术:常规路由、属性路由;

路由的定义:浏览器发出请求到达ASP程序,控制器处理传入的请求,将请求URL映射到控制器的操作方法上。这个映射过程就叫路由;

http:://localhost:13380/Home/Details/1

Home将映射到HomeController控制器,Details将映射到Details()操作方法,1将自动映射到id;也就是调用HomeController类中的Details(int id)方法;

app.UseMvcWithDefaultRoute();

默认路由模板规则:{controller=Home}/{action=Index}/{id?}

http:://localhost:3290/Student/Details/1

第一个路径段/Student,是在URL中的控制器名称,没有Controller。而MVC会自动添加Controller,然后再按照完整的关键字StudentController进行查找;

第二个路径段/Details,映射到StudentController的Details(int id)方法;

第三个路径段/1,映射到操作方法的参数id;

这个步骤被称为模型绑定

路由模板中id后面的问号?,表示该参数是可选参数;

controller=Home表示controller默认值是Home;

action=Index表示action默认值是Index;

依赖注入

容器服务

AddSingleton单例模式,程序只创建依次Singleton服务;

AddTransient暂时模式,每次请求都会创建一个新的Transient实例;

AddScoped范围模式,在范围内,每次请求创建一个新的实例;

优点

降低耦合;让代码易于测试;面向对象机制;

中间件

定义

中间件是一个可以处理HTTP请求或响应的软件管道;

举例

Startup.cs中Configure方法的中间件:

app.UseDeveloperExceptionPage(); app.UseStaticFiles();

app.UseMiddleware(); app.UseCors();

app.UseSwagger(); app.UseSwaggerUI(); app.UseRouting(); app.UseEndpoints();

原理

中间件可以同时处理传入请求和传出响应;

中间件处理传入请求,然后将该请求传递给下一个中间件,以进行下一步处理;

终端中间件terminal middleware传递的最后一个中间件;

中间件可以对传入的HTTP请求进行响应;中间件可以处理传出响应;

中间件按照添加到管道中的顺序执行;

大型团队中,中间件应该以NuGet包的形式提供;

Run方法注册的中间件是终端中间件,无法调用下一个中间件;

总结

ASP中间件,可以被 同时访问和请求;既可以被访问,也可以被请求;

可以处理请求,并传递给下一个;

可以处理请求,并完成传递;

可以处理传出响应;

按照添加顺序执行;

工作流程

所有请求都在中间件调用next方法之前除法;

请求按照顺序依次穿越管道;

当中间件处理请求并产生响应时,请求处理流程在管道中反向传播;

所有响应都在调用next之后触发,响应依次穿过所有管道;

基础

入口

Program.cs

ASP项目的入口

public static void Main(string[] args)

{ CreateWebHostBuilder(args).Build().Run(); }

开始

Startup.cs

// This method gets called by the runtime. Use this method to add services to the container.

public void ConfigureServices(IServiceCollection services)

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)

环境变量

Properties/launchSettings.json

"windowsAuthentication": false,启用windows身份验证

"anonymousAuthentication": true,启用匿名身份验证

"profiles": {

"IIS Express": {

"commandName": "IISExpress",

"launchBrowser": true,

"environmentVariables": {

"ASPNETCORE_ENVIRONMENT": "Development"

}

appsettings.json

appsettings.Development.json

"Logging": {

"LogLevel": {

"Default": "Debug",

"System": "Information",

"Microsoft": "Information"

用户机密

secrets.json

保存密钥、保存数据库的地址、账号、密码;

右击项目名称→管理用户及密;

C:\Users\Administrator\AppData\Roaming\Microsoft\UserSecrets\e166b10e-3884-4bef-8cbf-c60863aa72ff\secrets.json

依赖注入

IConfiguration

public Startup(IConfiguration configuration, IWebHostEnvironment env)

{ Configuration = configuration; GlobalContext.LogWhenStart(env); GlobalContext.HostingEnvironment = env; }

在Startup的构造函数中,注入的IConfiguration服务,采用的是依赖注入的形式;

IConfiguration服务是为了,从ASP中所有配置资源读取配置信息而设计的;

如果多个配置资源中,有重名的配置项,则后面的配置将覆盖前面的值;

WebHost.CreateDefaultBuilder(args)在程序启动时,会自动调用,按照特定顺序读取配置资源;

配置信息读取顺序:

1. appsettings.json;

2. appsettings.Development.json;

3. 用户机密;

4. 环境变量;

5. 命令行参数;

RESTFUL是一种网络应用程序的设计风格和开发方式,基于HTTP,可以使用XML格式定义或JSON格式定义。RESTFUL适用于移动互联网厂商作为业务接口的场景,实现第三方OTT调用移动网络资源的功能,动作类型为新增、变更、删除所调用资源。

GMT(格林尼治标准时间) GMT一般指世界时。

.net中 NuGet 包怎么念?发音 - 百度知道最佳答案: [nuget] (怒盖特) 或 [nju:get](牛盖特)牛之特/牛盖特/怒盖特

server: marco/2.19

Marco 英[ˈmɑːkəʊ]美[ˈmɑrkoʊ]n. 【地名】马科;

Server: nginx

Nginx读作['ɛndʒɪn eks]。Nginx (engine x)

server: Tengine/Aserver

Tengine是由淘宝网发起的Web服务器项目。在Nginx的基础上,针对大访问量网站的需求,添加了很多高级功能和特性。

Server: Microsoft-IIS/8.5

Internet Information Server (Microsoft)

Server: Apache

英音[əˈpɑ:ʃ] 美音[əˈpæʃ, ɑˈpɑʃ] apache

server: JSP3/2.0.14

JSP(全称JavaServer Pages)是由Sun Microsystems公司主导创建的一种动态网页技术标准。JSP 文件就是在传统的 HTML 文件中插入 Java 代码和 JSP 标签,后缀名为.jsp。 JSP 与 PHP、ASP、ASP.NET 等语言类似,都运行在服务端。

2022年09月,Web服务器排行榜

市场份额的排名

排名 服务器名称 当月总数 当月占比 上个月总数 上个月占比 对比上月

1 nginx 407086442 34.03% 448602806 36.45% -2.42%

2 Apache 330682809 27.64% 318307245 25.87% 1.77%

3 Microsoft 95944600 8.02% 118126662 9.6% -1.58%

4 Google 45689961 3.82% 44326227 3.6% 0.22%

活跃网站的排名

排名 服务器名称 当月总数 当月占比 上个月总数 上个月占比 对比上月

1 Apache 50925159 26.46% 50966410 26.62% -0.16%

2 nginx 37549911 19.51% 37794331 19.74% -0.23%

3 Google 18663687 9.7% 18767650 9.8% -0.1%

4 Microsoft 7861906 4.08% 8009744 4.18% -0.1%

Web Server 说明

  • Apache:Apache 软件基金会的一个开放源码的网页服务器,可以在大多数计算机操作系统中运行。由于其多平台和安全性被广泛使用,是流行的 Web服务器端软件之一。它快速、可靠并且可通过简单的 API 扩展,将 Perl/Python 等解释器编译到服务器中。

  • Microsoft:微软提供的基于运行 Microsoft Windows 的互联网基本服务。互联网信息服务(Internet Information Services),简称 IIS。

  • Google:谷歌自主开发的 Google Web Server,简称 gws 。

  • nginx:由伊戈尔·赛索耶夫为俄罗斯访问量第二的 Rambler.ru 站点开发的,免费开源、轻量级、高性能 Web 服务器。

IIS服务器

定义

IISweb服务器

IIS是Internet Information Server的简称。IIS作为当今流行的Web服务器之一,提供了强大的Internet和Intranet服务功能。

IIS通过超文本传输协议(HTTP)传输信息,还可配置IIS以提供文件传输协议(FTP)和其他服务,如NNTP服务、SMTP服务等。

中文名IISweb服务器外文名Internet Information Server缩 写IIS类 别服务器

历程

IIS在Web服务器阵营里一直稳居Number 2的位置,据相关资料,它在Web服务器软件市场上占据约20%的份额,在商业市场中,它有绝对的优势,它几乎是商业服务器软件的代名词。IIS是与Windows服务器版操作系统一起发放的,这个策略使它成为Windows平台服务器的首选Web服务器。它与整个Windows 系统紧密的整合在一起,可以利用Windows系统内置的安全机制来保护自己。一直以来,由于Windows系统本身较为脆弱的安全机制,IIS的相关丑闻不断。但无论如何,如果你使用Windows作为服务器操作系统,在绝大多数情况下你还是会选择IIS。 好在,随微软的不断努力,新版操作系统的稳定与安全性在不断提高,而微软积极的补丁发布策略也在让它的操作系统与IIS变得更加安全,IIS正在向一个成功的Web服务器不断迈进。微软最新的服务器操作系统Windows Server 2003上的IIS6.0可以看做微软IIS的最新成果。

区别

IIS与其它的WEB的差别

对于IIS与其它Web服务器软件的差别是明显的,网络管理员们一般会容易的在采用IIS还是其它服务器软件这间根据自己的情况做出选择,关于 IIS让人更加容易让人迷惑的是不同的微软服务器操作系统之间的区别以及如何选择的问题,由于它与操作系统结合得非常紧密,这使IIS的问题不再局限与 IIS。

简单的说,其实IIS就是发布网站的后台支撑程序!!可以使你的机器成为一台WEB服务器

Kestrel服务器

定义

kestrel [英[ˈkestrəl]美[ˈkɛstrəl]] n.(产于欧洲的)茶隼sǔn(一种鹰) 网络茶隼;小鹰;红隼sǔn;

Kestrel 是一个跨平台的Web服务器

Kestrel是开源的(GitHub提供的源代码),事件驱动的异步I/O服务器,用于在任何平台上托管ASP.NET应用程序。这是一个监听服务器和一个命令行界面。您将侦听服务器安装在Windows或Linux服务器上,并在计算机上安装命令行界面(安装.netcore会自动一整套安装)。(Kestrel发音: ['kestr(ə)l])

它是与ASP.NET它是与ASP.NET Core一起由微软推出的。所有ASP.NET Core应用程序都使用新的MVC框架和Kestrel Web服务器。这些新的应用程序可以运行在完整的.NET Framework或.NET Core上。

概述

Kestrel Web服务器的概述

Kestrel被认为是较新ASP.NET应用程序的首选Web服务器(请参阅这篇文章与IIS比较, why you need both)。它j机遇libuv library,与node.js使用的库相同。Libuv支持事件驱动的编程风格。它的一些核心工具包括:

非阻塞网络支持

异步文件系统访问

计时器

子进程

它允许ASP.NET Core应用程序在其他跨平台的Web服务器(如Jexus,Nginx和Apache)上轻松运行,而无需解决不同的启动配置。通过使用Kestrel作为进程内服务器, 即使有跨平台支持,应用程序也将具有一致的处理(Startup (Main(), Startup.ConfigireServices(), Startup.Configure())

原理

Kestrel Web服务器的工作原理

应用程序通常是为了响应人的行为而编写的。使用事件驱动的编程,有一个循环来监听事件。然后触发一个回调函数。为了减少SYS调用的数量,所有其他工作都在标准.NET工作线程的托管代码中执行。

Kestrel提供了一个事件循环和基于回调的I/O通知。Libuv管理从操作系统收集和监视事件。此外,用户可以在事件发生时注册回调。所以,Kestrel使用libuv进行I/O工作,并支持运行多个事件循环。

由于它轻巧,Kestrel不允许你进行SSL termination,URL重写或GZip压缩,但是相同的轻量级设计使他比起其他服务器会更快。实际上,它比静态和纯文本操作的node.js快6倍。

好处

Kestrel支持.NET Core支持的所有平台和版本。此外,它默认包含的ASP.NET Core新项目模板中,可以提供更好的请求处理的性能。在Visual Studio中创建新项目时,项目会自动配置为在Kestrel中运行。

如前所述,这不是一个功能齐全的网络服务器,但这正是为什么它很快。如果您觉得需要速度,Kestrel就是答案 - 特别是因为它被设计用于ASP.NETCore的生产。

你可以做的是在一个功能更全面的网络服务器(如IIS或NGNIX)之后运行它。您可以使用HttpPlatformHandler在IIS 后面运行它,或者在Visual Studio 后面使用HttpPlatformHandler在IIS Express 后面运行它。而且,您需要在ASP.NET Core项目中支持它,以便开发人员可以在任何支持的平台上方便地运行它们。

现在,即使您不是跨平台的,也可以直接从web服务器命令行上运行ASP.NET

由于Kestrel不是一个全功能的Web服务器,您应该让web程序在在IIS或NGNIX 之后运行(IIS;Jexus或者NGINX代理web程序,提供转发到Kestrel的形式工作)。它旨在使ASP.NET尽可能快,但其管理安全性和提供静态文件的能力有限。如果您使用的是Kestrel作为web服务器的ASP.NET Core,则 可以利用 Prefix来实现强大的代码分析功能。

BS和CS

简称

CS

BS

全称

CS:客户端服务器架构模式

Client/Server(客户机/服务器)结构

BS:浏览器服务器架构模式

Browser/Server(浏览器/服务器)结构

优点

充分利用客户端机器的资源,减轻服务器的负荷(一部分安全要求不高的计算任务存储任务放在客户端执行 ,不需要把所有的计算和存储都在服务器端执行,从而能够减轻服务器的压力,也能够减轻网络负荷)

客户端不需要安装;

维护成本较低

缺点

需要安装客户端程序;

升级维护成本较高

所有的计算和存储任务都是放在服务器端的.服务器的负荷较重;在服务端计算完成之后把结果再传输给客户端,因此客户端和服务器端会进行非常频繁的数据通信,从而网络负荷较重

举例

就像平时玩游戏,假如它不是CS模式是BS模式,通过网页的方式展示的。如果你的网络有些卡,你正在跑毒网络一卡,或者是服务器没来得及给你计算去更新地图和敌人。里面的人物定位还没更新过来,你这边一直在显示原地跑,卡了一两秒,你就被别人爆头了。就是因为网络有延迟,所以CS可以去减少服务器端计算的压力,去进行一些安全要求不高的计算任务。

就像是抖音,百度等等软件,一到节日他们就在他们的图标中去添加节日特色。但他们更新我们并不需要去升级浏览器什么的,所以他们维护成本比较低。例如4399小游戏里面的游戏,我们并不需要下载就可以玩,只要我们可以上网就行。这些游戏都是BS模式。

硬件

CS架构一般建立在专用的网络上, 小范围里的网络环境, 局域网之间再通过专门服务器提供连接和数据交换服务.

BS架构建立在广域网之上的, 不必是专门的网络硬件环境,例与电话上网, 租用设备. 信息自己管理. 有比C/S更强的适应范围, 一般只要有操作系统和浏览器就行

安全

CS架构一般面向相对固定的用户群, 对信息安全的控制能力很强. 一般高度机密的信息系统采用CS架构结构适宜. 可以通过B/S发布部分可公开信息.

BS架构建立在广域网之上, 对安全的控制能力相对弱, 可能面向不可知的用户。

架构

CS架构程序可以更加注重流程, 可以对权限多层次校验, 对系统运行速度可以较少考虑.

BS架构对安全以及访问速度的多重的考虑, 建立在需要更加优化的基础之上. 比C/S有更高的要求 B/S结构的程序架构是发展的趋势, 从MS的.Net系列的BizTalk 2000 Exchange 2000等, 全面支持网络的构件搭建的系统. SUN 和IBM推的JavaBean 构件技术等,使 B/S更加成熟.

重用

CS架构程序可以不可避免的整体性考虑, 构件的重用性不如在B/S要求下的构件的重用性好.

BS架构对的多重结构,要求构件相对独立的功能. 能够相对较好的重用.例如买来的餐桌可以再利用,而不是做在墙上的石头桌子

维护

CS架构程序由于整体性, 必须整体考察, 处理出现的问题以及系统升级. 升级难. 可能是再做一个全新的系统

BS架构构件组成,方面构件个别的更换,实现系统的无缝升级. 系统维护开销减到最小.用户从网上自己下载安装就可以实现升级.

处理问题

CS架构程序可以处理用户面固定, 并且在相同区域, 安全要求高需求, 与操作系统相关. 应该都是相同的系统

BS架构建立在广域网上, 面向不同的用户群, 分散地域, 这是C/S无法作到的. 与操作系统平台关系最小.

用户接口

CS架构多是建立的Window平台上,表现方法有限,对程序员普遍要求较高

BS架构建立在浏览器上, 有更加丰富和生动的表现方式与用户交流. 并且大部分难度减低,减低开发成本.

信息流

CS架构程序一般是典型的中央集权的机械式处理, 交互性相对低

BS架构信息流向可变化, B-B B-C B-G等信息、流向的变化, 更像交易中心。

哪个好

CS框架与BS框架各有优缺点,CS交互性强,响应速度快,安全性强,一般应用于局域网中,对硬件的要求高,但是开发维护成本高;BS交互性相对弱些,响应速度相对慢,安全性相对低,一般应用于广域网中,可以实现跨平台,客户端零维护。

现在两者的边界是很模糊的,很难完全区分开来,特别是现在做物联网,一个完整的系统里这两种架构应该都会涉及到。

end

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值