CSharp/C#-学习笔记
问题
implements,extend是什么?
学习单词命名法则!!
那个argv是什么意思?
C# object有哪些?
为什么lambda可以作为表达式
可以对变量进行数据绑定吗?可以的话该如何做?
basestream.seek
lambda 作为函数可以让委托添加:
method m += (a,b) =>a+b;
这是一个函数 delegate int Func(int a,int b){return a+b;}
学习笔记
write与writeline区别
Console.Write("Hello World!");
与
Console.WriteLine("Hello World!");
区别:前者输出后不换行,后者输出后自动换行
namespace
class 的更上一层.
程序终止
Environment.Exit(0);
class 2
转义符
@对字符串的优化作用
驼峰命名法
形参前添加ref的作用
相当于c++的 &(引用),可以修改原来的变量
out参数
out就是说,就是说可以在函数里面修改变量的值。
c#的debug
using System.Diagnostics;
Debug.WriteLine("…");
class3:对象编程
在写对象的时候要用关系图描绘关系,然后再选取合适的类的结构,当然要首先搞清楚有哪些类结构,他们分别对应哪些类型
属性与字段的比较
虚函数与重载
链接: 虚函数详解.
控制符的范围排序
索引器
定义:
C#之接口
接口来由:
希望有一个子类可以有多个父类,并可以继承他们的属性或成员,但是c#不支持多重继承,而是用接口来实现
接口的使用:
定义接口
abstract是什么东西?
abstract的类就是只声明,但是并不会去实现。
interface型的类
同abstract一样
接口的实现方法:
interface runner {}
interface swimmer {}
interface player {}…
abstract class animal{}
class person:animal,runner,swimmer,payer…{}
class implement{
public static void main(string [] args)
{…
}
}
实现方法2:
方法2更加简单,但是不够灵活,变量的访问权限是固定的,不灵活的。
class4: c#高级特性
lambda表达式
参数=>表达式
异常处理
https://www.runoob.com/csharp/csharp-exception-handling.html
class5:特征
as
引用变量
stringbuilder
变量类型,比string好在:
每次连接字符串的时候,系统不会创建新的字符串类型;
linq的使用步骤
var var_name = selection of an array
然后转换回来
dictionary:
key -> value(对应关系)
value = dictionary[key];
class 6:文本的读出和写入
filestream类
介绍:
表示在磁盘或网络路径上指向文件的流。这个类提供了在文件中读写字节的方法,但经常使用StreamReader或StreamWriter执行这些功能.
创建:
FileStream writeStream =
new FileStream(file_name,FileMode.Create,FileAccess.Write);
memorystream类
创建:
MemoryStream memStream = new MemoryStream()
构造函数:
MemoryStream()
使用初始化为零的可扩展容量初始化 MemoryStream 类的新实例。
MemoryStream(Byte[])
创建后不可扩展容量
属性:
略
streamreader类
new streamreader(path,fileencode)
readline 读到换行符结束
readtoend 读完
read 读一个字符
streamWriter
new streamwrite(path,fileMode,fileEncode)
主要方法:
writeLine: 写到换行符
write : 写一个字符
flush : 写整个流里边的内容
编码类型
encoding.xxx
xxx:
ASCII
Default
Unicode
UTF32
UTF8
DeleteComments(string s):
if s = reader.readline()
sentence means that:
去掉注释,表示把这一行中的注释部分删掉
非托管资源释放
释放方式:
way1:
streamWriter sw = ..
try{}
finally{
if(sw != null) sw.Close();
}
way2:which is more convenient:
using(StreamWriter sw = File.CreateText(path)){
// sw的操作
}//using 结束后会自动关闭sw
其功能同way1
序列化和反序列化
way1:binaryFormatter
way2:xmlSerializer
文件\目录类
file类
一个静态类,不需要创建对象
功能:
directory静态类
功能:
fileinfo类
创建
FileInfo fileInfo = new FileInfo(@"E:\DATA\ASC测试数据\原始数据\FileName017-0001.asc");
功能:
directoryInfo对象
头文件:
using System.IO;
创建:
string load = @"D:\桌面文件\笔记\我的笔记\";
DirectoryInfo info = new DirectoryInfo(load);
属性:
https://docs.microsoft.com/en-us/dotnet/api/system.io.directoryinfo?view=net-5.0
path静态类
文件变更监听
单元测试
看ppt-第六次
个人感觉没什么用,因为不能测试UI界面的操作
class 7 : winform 及其控件
容器作用
容器有: groupbox and panel
作用:
容器中的单选按选项控件会互斥
ListBox 的使用
属性:
checklistbox 控件
特点:
比listbox增加了check的属性,总之就是比checkListBox好
属性:
进度条 progressbar
选项卡 tabcontrol
成果:
窗体控件的界面布局
dcok
布局属性
布局技巧
一个成功的案例
anchor
class 8: winform窗体控件的使用
数据绑定
datagridview数据绑定注意事项
(1)要创建winform .net 版的控制器
(2)作为数据来绑定的类必须是public class
(3)一定要先生成解决方案,datagridview的更新才会显现出来
(4) 属性栏的中介控件绑定的是类
(5)最后一步写一行代码:
如:studentBindingList.DataSource = xxx(你要展现的数据);
局限性:
控件的数据会随着变量的变化而变化,但是改变控件的数据不能改变变量的数据。
菜单控件
控件名: menuStrip
右键菜单:ContextMenuItem
工具栏:功能丰富的菜单, TOOLBAR
progressBar:状态栏是一个容器,使用方法自己查
模态与非模态
show():可以海
showDialog():要专一
MDI窗体的应用
(1)设置主窗体:
属性栏- IsMdiContainer-true
(2)在主窗体代码中创建新窗体:
其他对话框
OpenFileDialog
SaveFileDialog
导出文件全过程代码
导入文件全过程:
class 9 :文本、XML、网络编程
TCPListener类的方法
TCPClient方法:
UdpClient类的方法:
TCP/代码解读
TCPListener等待接收
listener:
IP地址是本机的IP地址,10000是端口号
using(TcpClient tcpclient = listener.AcceptTcpClient())含义:
等待连接,如果未连接将不会执行里边的代码
TCPClient连接服务器
这时的tcpClient为主动连接端口,上面的方式是等待别人连接。
UDP:服务端/接收端
IPEndPoint point = new IPEndPoint(IPAddress.Any,0);
byte[] buffer = reciver.Receive(ref point);
代码细节:不明白,作用就是接收发送端的数据,下面的代码就是转码数据流,
然后输出
System.Web
终端和地址的区别:
System.Net中的常用类:
WebClient类:
E-mail编程:
方法:使用SmtpClient发送,使用MailMessage当载体。
发送的message要包含有4个信息:俩个地址,主题,内容。
SmtpServer要有2个信息:端口,发送地址,密码.
正则表达式
基本概念:
字符集和数量词和位置:
分组
或:
(good|bad)boy :表示匹配 goodboy badboy
(?<名称>xxxxxx):表示命名,如:
(?<word>\w+)表示匹配一个或多个单词.
匹配普通字符
字符 | 描述 |
---|---|
[abc] | 匹配text当中所有的a,b,c字母 |
[^abc] | 匹配所有非a,b,c字母 |
[A-Z] | 匹配所有大写字母,同理[a-z] |
[\s\S] | \s空格符,\S非空格符,俩个一起就表示匹配所有 |
[\w] | 等价于[A-Za-z0-9],表示匹配字母和数字 |
. | 特殊字符 .(点,句号)在正则表达式中用来表示除了"新行"之外的所有字符。所以模式 ^.5$ 与任何两个字符的、以数字5结尾和以其他非"新行"字符开头的字符串匹配。模式 . 可以匹配任何字符串,换行符(\n、\r)除外。 |
匹配特殊字符
字符 | 描述 |
---|---|
( ) | 标记开始和结束的位置 |
* | 匹配前面的表达式0次或多次 |
+ | 匹配前面的表达式一次或多次 |
\ | 表示将下一个字符标记为特殊字符,原义字符。 |
限定符
字符 | 描述 |
---|---|
* | 例如,zo* 能匹配 “z” 以及 “zoo”。* 等价于{0,}。 |
+ | 例如,‘zo+’ 能匹配 “zo” 以及 “zoo”,但不能匹配 “z”。+ 等价于 {1,} |
? | 例如,“do(es)?” 可以匹配 “do” 、 “does” 中的 “does” 、 “doxy” 中的 “do” 。? 等价于 {0,1},表示前面的子表达式是可选的 |
{n} | n 是一个非负整数。匹配确定的 n 次。例如,‘o{2}’ 不能匹配 “Bob” 中的 ‘o’,但是能匹配 “food” 中的两个 o。 |
{n,} | n 是一个非负整数。至少匹配n 次。例如,‘o{2,}’ 不能匹配 “Bob” 中的 ‘o’,但能匹配 “foooood” 中的所有 o。‘o{1,}’ 等价于 ‘o+’。‘o{0,}’ 则等价于 ‘o*’。 |
{n,m} | 例如,“o{1,3}” 将匹配 “fooooood” 中的前三个 o。‘o{0,1}’ 等价于 ‘o?’。请注意在逗号和两个数之间不能有空格。 |
/<.*>/ 表示匹配从开始小于符号 (<) 到关闭 h1 标记的大于符号 (>) 之间的所有内容
匹配单词边界
字符 | 描述 |
---|---|
/\bCha/ | 匹配单词 Chapter 的开头三个字符,因为出现在单词边界的后面 |
/ter\b/ | 匹配单词 Chapter 中的字符串 ter,因为它出现在单词边界的前面 |
/\Bapt/ | 字符串 apt 出现在单词 Chapter 中的非单词边界处,但出现在单词 aptitude 中的单词边界处。对于 \B 非单词边界运算符,位置并不重要,因为匹配不关心究竟是单词的开头还是结尾 |
选择
字符 | 描述 |
---|---|
exp1(?=exp2) | 查找 exp2 前面的 exp1 |
(?<=exp2)exp1 | 查找 exp2 后面的 exp1 |
exp1(?!exp2) | 查找后面不是 exp2 的 exp1 |
(?<!exp2)exp1 | 查找前面不是 exp2 的 exp1 |
例子剖析
var str = "Is is the cost of of gasoline going up up";
var patt1 = /\b([a-z]+) \1\b/ig;
document.write(str.match(patt1));
捕获的表达式,正如 [a-z]+ 指定的,包括一个或多个字母。正则表达式的第二部分是对以前捕获的子匹配项的引用,即,单词的第二个匹配项正好由括号表达式匹配。\1 指定第一个子匹配项。
单词边界元字符确保只检测整个单词。否则,诸如 “is issued” 或 “this is” 之类的词组将不能正确地被此表达式识别。
正则表达式后面的全局标记 g 指定将该表达式应用到输入字符串中能够查找到的尽可能多的匹配。
表达式的结尾处的不区分大小写 i 标记指定不区分大小写。
修饰符
标记不写在正则表达式里,标记位于表达式之外,
修饰符 | 含义 | 描述 |
---|---|---|
i | ignore - 不区分大小写 | 将匹配设置为不区分大小写,搜索时不区分大小写: A 和 a 没有区别。 |
g | global - 全局匹配 | 查找所有的匹配项。 |
m | multi line - 多行匹配 | 使边界字符 ^ 和 $ 匹配每一行的开头和结尾,记住是多行,而不是整个字符串的开头和结尾 |
s | 特殊字符圆点 . 中包含换行符 \n | 默认情况下的圆点 . 是 匹配除换行符 \n 之外的任何字符,加上 s 修饰符之后, . 中包含换行符 \n。 |
var str="Google runoob taobao runoob";
var n1=str.match(/runoob/); // 查找第一次匹配项
var n2=str.match(/runoob/g); // 查找所有匹配项
var str="Google runoob taobao RUNoob";
var n1=str.match(/runoob/g); // 区分大小写
var n2=str.match(/runoob/gi); // 不区分大小写
var str="runoobgoogle\ntaobao\nrunoobweibo";
var n1=str.match(/^runoob/g); // 匹配一个
var n2=str.match(/^runoob/gm); // 多行匹配
//m 修饰符可以使 ^ 和 $ 匹配一段文本中每行的开始和结束位置
XML文档:
XML文件格式:
XML声明
文档注释
属性
元素
XML的处理方式:
DOM:Document Object Model
类:XmlDocument
读取全部文档,形成一个树,对树进行操作。
SAX:
类:XmlTextReader,XmlTextWriter
DOM
SAX
SAX的写和读xml
XPath
XPath表示的是对XML进行查询的表达式
认识XPath:
XML转HTML
使用 XslCompiledTransform
class 10 : Thread
概念理解:
名称 | 解释 |
---|---|
同步 | 一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推 |
异步 | 每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的 |
回调函数 | 回调函数剖析 |
uri | 一个用于存储url的类,仅可读 |
线程的创建
//创建无参线程:
Thread thread = new Thread(obj.fun);
thread.start();
//创建带参方法:
Thread thread = new Thread(()=>obj2.Count2(15));
线程的挂起/停止
//强制终止
Abort()
//挂起
Suspend();
//恢复
Resume();
//暂停
Sleep(SetTime);
Thread常见属性
Property | Description |
---|---|
IsAlive | 判断线程是否在生命周期内 |
IsBackground | 判断是否为后台执行 |
Name | 获取线程的名字 |
ThreadState | 获取线程的状态 |
线程优先级
Member | Description |
---|---|
Highest | … |
AboveNormal | better than normal |
Normal | |
BelowNormal | lower than normal |
Lowest | the lowest |
线程状态 ThreadState
Member | Description |
---|---|
Aborted | 中断且撤销 |
Background | 充当后台线程的角色 |
… | … |
Join()作用:
将其分开,线程将不能同步执行。
数据争用解决办法:使用LOCK语句为对象上锁
含义:
用来解决多个线程访问同一个代码块造成的冲突问题。
类比:代码块就是厕所,lock就是锁,线程就是人,谁先来谁先上厕所,这个人拉完就出来,下一个人进来,即:代码块不能同时被访问。
线程池 ThreadPool
//方法理解:
ThreadPool.SetMaxThread(2,2); // 表示设置最大值
ThreadPool.GetMaxThread(out int workerThreads, out int ioThreads);
//表示获取最大值,第二句也可以写成:
int workerThreads,ioThreads;
ThreadPool.GetMaxThread(out workerThreads,ioThreads);
//都表示将设置的值赋给这俩个局部变量
Timer类
Timer timer = new Timer(); // create a new timer
Timer timer2 = new Timer(Method,state,dueTime,period);
/* Method is function
state is the param of Method
dueTime means wait for a while before running Method.
period is the time before method run again.
*/
timer.Dispose(); // stop it.
集合的线程安全性
使用进行过线程包装的集合对象来创造实例
ConcurrentDictionary<Key,Value> myDict = new ConcurrentDictionary<Key,Value>();
// safer Dictionary
ConcurrentStack<T> myStack = new ConcurrentStack<T>();
//safer Stack
使用Synchronized()方法:
ArrayList list = ArrayList.Synchronized(new ArrayList());
Task类
Task myTask = new Task(Method);// create.
//create second task that follow the previous one
//task2 will run as soon as mytask is done
Task task2 = myTask.ContinueWith( );
myTask.Start();
myTask.wait(); // 等待task执行完毕,才执行下一条语句。
Parallel 类
Parallel继承 Task,可以更便捷地实现相应功能.
Parallel的使用:
Parallel.Invoke(action[] actions);
//并行执行这些委托函数
Parallel.For(0,100,i => {...});
// run function with i as param from 0 to 99
Parallel.ForEach(list, item => {...});
// foreach item in list , run function.
总结:Parallel就是并行运算在c#中的应用
并行运算的实例
Parallel Linq:
//使用 Asparallel() 进行并行筛选.
var query = from n in persons.AsParallel() where n.Age>20 select n;
query.ForAll(i => Console.WriteLine(i));
异步编程:
作用:
提高相应能力,特别是Ui界面等待下载文件的时候。
方式:
多线程执行.
异步在c#中的体现
async修饰方法,方法中需要有await语句
await表示这个方法等待,主线程序继续执行.
例子1:异步网络下载:
//固定模板.
WebClient client = new WebClient();
Uri uri = new Uri(address);
client.DownloadStringCompleted += new
DownloadStringCompletedEventHandler(DownloadStringCallback2);
client.DownloadStringAsync(uri); // when this function is done ,callback 2 is run.
// you can't set the property before it.
private void DownloadStringCallback2(Object sender,
DownloadStringCompletedeEventArgs e
){
// write sth to show that the task is done!
Console.WriteLine(e.Message);
}
例子2:异步下载文件:
async static void WriteFile(){
using(StreamWriter sw = new StreamWriter(
new FileStream("aaa.txt",FileMode.Create))){
await sw.WriteAsync("My Text");
}
}
Console.WriteLine("Write File Ok!");
多线程更新winform
概念 | 含义 |
---|---|
async | 用async修饰的函数会后台执行,然后继续在主线程中执行函数下面的语句 |
await | 功能同task.wait(),在函数中只有当await修饰的语句执行完之后,才会执行下面的语句块 |
实战1:
描述:
点击按钮开始爬虫,完毕事件后,会触发完毕事件(触发事件写在task任务函数里边),完毕事件就是显示download成功.
只能在创建控件的线程里面进行设置属性和调用方法
线程间操作无效:从不是创建控件""的线程访问它.
解决方案:
lesson 11 :数据库编程
关系型数据库
概念 | 描述 |
---|---|
记录Record | 表的行 |
字段Field | 表的列,组成行的单位 |
主键 Primary Key | 作为标识的字段 |
外键 Foreign Key | 自己的表中不是主键,但是在其他的表中是主键,这样的字段叫主键 |
关键字理解
关键字 | 描述 |
---|---|
SELECT | 选择你想要的字段,语法是:字段之间用逗号分开 |
FROM | 从 一个或多个表中选择 |
WHERE | 后面接上选择的条件 |
GROUP BY | 分组,更细致的概念理解 |
HAVING | 分组的条件,通常与GROUP BY连用 |
ORDER BY XXX ASC/DESC | 用升序或降序来进行排序 |
INSERT | 如:INSERT INTO Book VALUES (‘xxx’,‘xxx’,‘xxx’) |
DELETE | DELETE FROM Book WHERE 单价<10.0 |
UPDATE | UPDATE Book SET 单价 = 单价 *0.8 WHERE 单价>30 AND 单价<50 |
ADO.NET概述
数据库的访问
字符串拼接:
会造成安全漏洞
// bug:
cmd.CommandText = " SELECT * FROM users
WHERE uname =
''+ name + '' AND password = ''+password ;
// name , password 由输入框输入。
// 当 name = "admin or 1=1"
//一拼接就是:
uname = admin or 1=1
//bug free code:
cmd.CommandText = "INSERT INTO
Nations(CName,FName) VALUES(@CName,@FName);
cmd.Prepare();
cmd.Parameters.AddWithValue("@CName","aaaaa");
MySqlConnection访问数据库:
string text = "INSERT INTO Authors(Name) VALUES(@Name)"
private static void InsertAuthor(){
using (MyConnection connection = GetConnection()){
using(MySqlCommand cmd = new MySqlCommand(text,connection)){
cmd.Prepare();
cmd.Parameters.AddWithValue("@Name","莫言");
cmd.ExecuteNonQuery();
}}}
MySqlDataReader访问对象:
private static void QueryAuthors(){
using(MySqlConnection conn = GetConnection()){
string stm = "SELECT*FROM Authors";
using(MySqlCommand cmd = new MySqlCommand(stm,conn)){
using(MySqlDataReader reader = cmd.ExecuteReader()){
while(reader.Read()){
Console.WriteLine(reader.GetInt32(0)+":"+reader.GetString(1));
}
}
}
}
}
MySqlDataAdapter访问数据库:
using(MySqlConnection conn = GetConnection()){
String sql = "SELECT*FROM Authors";
using(MySqlDataAdapter dataAdapter = new MySqlDataAdapter(sql,conn)){
using(DataSet ds = new DataSet()){
dataAdapter.Fill(ds);
foreach(DataRow row in ds.Tables[0].Rows){
foreach(object field in row.ItemArray){
Console.Write(field + "\t");
}
Console.WriteLine();
}
}
}
}
MySqlDataAdapter修改数据库:
using(MySqlDataAdapter dataAdapter = new MySqlDataAdapter(sql,conn)){
MySqlCommandBuilder cmdBuilder = new MySqlCommandBuilder(dataAdapter);
using(DataSet ds = new DataSet()){
dataAdapter.Fill(ds);
DataRow[] rows = ds.Tables[0].Select("AuthorId=1");
for(int i =0; i<rows.Length; i++){
rows[i].BeginEdit();
rows[i][1] = 6;//修改数据
rows[i].EndEdit();
}
dataAdapter.Updata(ds);// 提交
}
}
//添加记录:
DataRow newRow = ds.Tables[0].NewRow();
newRow[1] = 6;
newRow[2] = "New Book";
ds.Tables[0].Rows.Add(newRow);
dataAdapter.Update(ds);
Entity Framework 编程
使用Nuget为项目安装EF框架
使用方法:
Code First :
Model First :
Database First :
Code First 开发示例:
声明和创建
// step1 :声明类
public class Blog {
public int BlogId {get ; set }
public string Url {get ; set }
public int rating {get ; set }
public List<Post> Posts {get ;set}
}
public class Post {
public int PostId {get; set }
public string Title {get ; set }
public int BlogId {get ; set}
public Blog Blog {get ; set }
}
// step 2 :创建类
public class BLoggingContext : DbContext {
public BloggingContext() : base("BlogDataBase"){
Database.SetInitializer(new DropCreateDatabaseIfModelChanges<BloggingContext>());
}
public DbSet<Blog> Blogs {get; set;}
public DbSet<Post> Posts {get; set;}
}// BloggingContext 是连接数据库和代码的桥梁,是将类转换成数据库内容的媒介
进行数据库操作
// 添加数据
using (var db = new BloggingCOntext()){
var blog = new Blog { Url = "" , Rating = 3};
blog.Posts = new List<Post>(){
new Post(){Title = "t1",Context ="hello,t1"}, new Post(){Title = "t2", Context = "hello,t2"}
};
db.Blogs.Add(blog);
db.saveChanges();//将内容更新到数据库
}
// 删除数据
using(var context = new BloggingContext()){
var post = context.Posts.FirstOrDefault(p => p.PostId == 1);
if(post != null){
context.Posts.Remove(post);
context.SaveChanges();
}
}
//修改数据
using(var context = new BloggingContext()){
var post1 = context.Posts.FirstOrdefault(p => p.PostId = 1);
if(post != null){
post.Comment = "changed!";
context.SaveChanges();
}
}
使用Entry State进行数据修改,增添,删除
using (var db = new BloggingContext()){
var blog = new Blog{Url = "",Rating = 3};
blog.Posts = new List<Post>(){
new Post(){Title = "t1",Content = "hello"},
new Post(){Title = "t2",Content = "hello"}
};
//给Id =1 的博客增加一个新帖子
var post = new Post(){Title ="t3",Context = "hello", BlogId = 1};
db.Entry(post).State = EntityState.Added;
db.SaveChanges();
// 删除ID = 1的博客及帖子
var blog = context.Blogs.Include("Posts").FirstOrDefault(p=> p.BlogId == 1);
db.Entry(blog).State = EntityState.Deleted;
db.SaveChanges();
}
Lesson 12: Web开发
安装
2.安装postMan
3.
4.
知识速览
基础知识
名称 | 解释 |
---|---|
startup类 | 配置应用所需的服务 |
依赖关系注入(Dependence Input) | 将配置服务的代码添加到Startup.ConfigureServices |
中间件 | 在Startup.Configure中调用Use…扩展方法,向管道中添加中间件组件 |
主机(HostBuilder) | ASP.NET Core 应用在启动时构建主机,主机封装应用的所有资源 |
Kestrel | 跨平台 Web 服务器 |
路由 | 映射到处理程序的 URL 模式,(猜测)也就是控制URL的格式 |
内容根目录 | 指向以下内容的基路径: 已执行文件,已编译程序集 |
Web 根目录 | wwwroot文件夹, |
curl | 命令行或脚本中用于传输数据 |
补充知识
样貌 | 含义 |
---|---|
[HttpGet] | 表示设置此函数的参数为路径参数,就是说参数在url上获取,如: localhost:5001/api/todo?id=1 |
[HttpGet("{id}")] | 简化url,如:localhost:5001/api/todo/1 |
.Net Core Web API
目标:
1.创建 Web API 项目
2.添加模型类和数据库上下文
3.使用 CRUD 方法构建控制器
4.配置路由、URL 路径和返回值
5.使用 Postman 调用 Web API
步骤:
创建 Web API步骤
自定义函数
调用Web API 方法:
C# , JavaScript , Axios 调用API
报错解决方案
PostMan 401 unauthorization:
把LaunchSettings中的 anonymousAuthentication设置位true即可