深度优先

这个家伙好懒,除了文章什么都没留下

0%

转自:https://www.cnblogs.com/landeanfen/p/4636102.html

前言:xml的操作方式有多种,但要论使用频繁程度,博主用得最多的还是Linq to xml的方式,觉得它使用起来很方便,就用那么几个方法就能完成简单xml的读写。之前做的一个项目有一个很变态的需求:C#项目调用不知道是什么语言写的一个WebService,然后添加服务引用总是失败,通过代理的方式动态调用也总是报错,最后没办法,通过发送原始的WebRequest请求直接得到对方返回的一个xml文件。注意过webservice的wsdl文件的朋友应该知道这个是系统生成的xml文件,有点复杂,研究了半天终于能正常读写了。今天在这里和大家分享下。

1、介绍之前,首先回顾下Linq to xml的方式读写简单xml的方法

(1)读取xml

1
2
3
4
5
<?xml version="1.0" encoding="utf-8"?>
<BizADsList>
<adData aid="1" image="baidu.jpg" link="www.baidu.com" title="百度"/>
<adData aid="2" image="qq.jpg" link="www.qq.com" title="腾讯"/>
</BizADsList>
1
2
3
4
5
6
7
8
9
10
11
12
var strPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"data\test.xml");
XDocument adList = XDocument.Load(strPath);
var ad = from a in adList.Descendants("BizADsList").Elements("adData")
select new
{
image = a.Attribute("image").Value,
link = a.Attribute("link").Value,
title = a.Attribute("title").Value
};
string s = "";
foreach (var a in ad)
s += a.image;

(2)写xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/// <summary>
/// 根据得到的Document集合生成XML
/// </summary>
/// <param name="lstDocumentBD"></param>
/// <param name="docNE"></param>
/// <param name="strSpiderTime"></param>
/// <param name="strNewRate"></param>
private static void SaveXmlByLstDocument(List<Document> lstDocumentBD, Document docNE, string strSpiderTime, string strNewRate)
{
try
{
XDocument xDoc = new XDocument();
XElement xRoot = new XElement(CSpiderConst.XML_ELE_ROOT);

//1.构造Device节点
XElement xDevice = new XElement(CSpiderConst.XML_ELE_DEVICE);

//2.构造NE节点
XElement xNE = new XElement(CSpiderConst.XML_ELE_NE);
foreach (var oDocNE in docNE)
{
XElement xItem = new XElement(CSpiderConst.XML_ELE_ITEM, new XAttribute(CSpiderConst.XML_PROP_NAME, oDocNE.Key), oDocNE.Value);
xNE.Add(xItem);
}
//这里增加一个<Item name='NewRate'>和<Item name='SpiderTimeEx'>节点用来保存当前这次的利用率和当次的采集时间
AddNewRateAndSpiderTime(strSpiderTime, strNewRate, xNE);
xDevice.Add(xNE);

//3.循环构造BD节点并添加到Device节点中
foreach (var oDocument in lstDocumentBD)
{
XElement xBD = new XElement(CSpiderConst.XML_ELE_BD);
foreach (var oDocBD in oDocument)
{
XElement xItem = new XElement(CSpiderConst.XML_ELE_ITEM, new XAttribute(CSpiderConst.XML_PROP_NAME, oDocBD.Key), oDocBD.Value);
xBD.Add(xItem);
}
AddNewRateAndSpiderTime(strSpiderTime, strNewRate, xBD);
xDevice.Add(xBD);
}
xRoot.Add(xDevice);
xDoc.Add(xRoot);

//4.保存到采集器本地,以服务器的时间和网元的AssetID来命名
var strDirectoryPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ReportFailed\\");
if (!Directory.Exists(strDirectoryPath))
{
Directory.CreateDirectory(strDirectoryPath);
}
xDoc.Save(strDirectoryPath + docNE[TBLDeviceLCBB.PROP_ASSETID] + "_" + strSpiderTime.Replace(":", "_") + ".xml");
}
catch
{
CLogService.Instance.Debug("保存XML失败");
}
}

通过XDocument、XElement对象和Element()、Elements()两个方法能完成大部分xml文件的操作。

2、进入今天的正题:读写带命名空间的xml文件。

首先来看一段xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 <?xml version="1.0" encoding="utf-8" ?>
<DataSet xmlns="http://WebXml.com.cn/">
<xs:schema xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="getRegion">
<msdata:aa>
test
</msdata:aa>
<xs:element name="getRegion" msdata:IsDataSet="true" msdata:UseCurrentLocale="true">
<xs:element name="Province">
<xs:sequence>
<xs:element name="RegionID" type="xs:string" minOccurs="0"/>
<xs:element name="RegionName" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:element>
</xs:element>
</xs:schema>
</DataSet>

第一次看到这个文件确实让人萌神了,比如需要取一个msdata:IsDataSet=”true”这个属性,该怎么取…

解析之前,先来分析下这段xml,这段里面有一个xmlns属性,这个属性是每一个标签自带的属性,不信你可以新建一个xml文件,然后在任何一个标签里面输入xmlns属性,后面都会出来很多的系统自带的命名空间。这个属性表示所属标签在哪个命名空间下面,所以在取的时候要带上这个命名空间。

先来看看解析的代码:

1
2
3
4
5
6
7
8
9
10
11
12
var strPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"data\test.xml");
var oRoot = XDocument.Load(strPath);
//取DataSet标签
var oDataSet = oRoot.Element(XName.Get("DataSet", "http://WebXml.com.cn/"));
//取schema标签
var oSchema = oDataSet.Element(XName.Get("schema", "http://www.w3.org/2001/XMLSchema"));
//取element标签
var oElement = oSchema.Element(XName.Get("element", "http://www.w3.org/2001/XMLSchema"));//这两个节点都是以xs打头,所以命名空间都是xs的命名空间
//取element标签下面的IsDataSet属性
var oElementValue = oElement.Attribute(XName.Get("IsDataSet", "urn:schemas-microsoft-com:xml-msdata"));
//取aa标签
var oAA = oSchema.Element(XName.Get("aa", "urn:schemas-microsoft-com:xml-msdata"));

我们来解析下几个关键的地方:

(1)我们来解析下

1
<xs:schema xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="getRegion">

这一句,最前面的”xs”表示标签所属命名空间的变量,xmlns:xs=”http://www.w3.org/2001/XMLSchema"这个表示xs这个命名空间的值。所以要得到schema这个标签需要带上命名空间var oSchema = oDataSet.Element(XName.Get(“schema”, “http://www.w3.org/2001/XMLSchema"));这个标签还定义了另一个命名空间xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"。

(2)再来看看aa标签

1
2
3
<msdata:aa>
test
</msdata:aa>

msdata就是上面schema标签里面定义的另一个命名空间,表示aa标签属于msdata命名空间下面。

(3)再看来看属性的取法:

1
<xs:element name="getRegion" msdata:IsDataSet="true" msdata:UseCurrentLocale="true">

如果要取msdata:IsDataSet=”true”,由于这个属性也带有命名空间,所以取属性时也要加上命名空间了。所以需要这样取。

1
var oElementValue = oElement.Attribute(XName.Get("IsDataSet", "urn:schemas-microsoft-com:xml-msdata"));

现在大伙们应该对这种xml有一个更加清晰的认识了吧。其实一般情况下这种场景比较少见,因为这么复杂的xml一般是由引用服务时代理对象去解析的。但如果真的有这么变态的需求我们也不用担心了。在此记录下,以后如果大家遇到希望能节约大伙的时间。

Demo 读取 xml:

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
XmlDocument xmldc = new XmlDocument();

XmlNamespaceManager xnm = new XmlNamespaceManager(xmldc.NameTable);
xnm.AddNamespace("xx", "http://xml.sunhotels.net/schemas/Places_1.5.0.xsd");

using (XmlReader reader = XmlReader.Create(@"Places_en.xml"))
{
xmldc.Load(reader);
var places1 = xmldc.DocumentElement.SelectNodes("/xx:sunhotels_cache_file/xx:places/xx:place", xnm);
foreach (var place1 in places1)
{
//第一层
var id1 = ((XmlNode)place1).SelectSingleNode("xx:id", xnm).InnerText;
var description1 = ((XmlNode)place1).SelectSingleNode("xx:description", xnm).InnerText;
Console.WriteLine($"{id1} --- {description1}");


var places2 = ((XmlNode)place1).SelectNodes("xx:places/xx:place", xnm);
foreach (var place2 in places2)
{
//第er层
var id2 = ((XmlNode)place2).SelectSingleNode("xx:id", xnm).InnerText;
var description2 = ((XmlNode)place2).SelectSingleNode("xx:description", xnm).InnerText;
Console.WriteLine($"\t{id2} --- {description2}");


var places3 = ((XmlNode)place2).SelectNodes("xx:places/xx:place", xnm);
foreach (var place3 in places3)
{
//第三层
var id3 = ((XmlNode)place3).SelectSingleNode("xx:id", xnm).InnerText;
var description3 = ((XmlNode)place3).SelectSingleNode("xx:description", xnm).InnerText;
Console.WriteLine($"\t\t{id3} --- {description3}");
}
}
}
}

作者:hoxis
链接:https://www.jianshu.com/p/d7b9c468f20d
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

一. 创建用户

命令:

1
CREATE USER 'username'@'host' IDENTIFIED BY 'password';

说明:

  • username:你将创建的用户名
  • host:指定该用户在哪个主机上可以登陆,如果是本地用户可用localhost,如果想让该用户可以从任意远程主机登陆,可以使用通配符 %
  • password:该用户的登陆密码,密码可以为空,如果为空则该用户可以不需要密码登陆服务器

例子:

1
2
3
4
5
CREATE USER 'dog'@'localhost' IDENTIFIED BY '123456';
CREATE USER 'pig'@'192.168.1.101_' IDENDIFIED BY '123456';
CREATE USER 'pig'@'%' IDENTIFIED BY '123456';
CREATE USER 'pig'@'%' IDENTIFIED BY '';
CREATE USER 'pig'@'%';

二. 授权:

命令:

1
GRANT privileges ON databasename.tablename TO 'username'@'host'

说明:

  • privileges:用户的操作权限,如 SELECTINSERTUPDATE 等,如果要授予所的权限则使用 ALL
  • databasename:数据库名
  • tablename:表名,如果要授予该用户对所有数据库和表的相应操作权限则可用 * 表示,如 *.*

    例子:

1
2
GRANT SELECT, INSERT ON test.user TO 'pig'@'%';
GRANT ALL ON *.* TO 'pig'@'%';

注意:

用以上命令授权的用户不能给其它用户授权,如果想让该用户可以授权,用以下命令:

1
GRANT privileges ON databasename.tablename TO 'username'@'host' WITH GRANT OPTION;

三.设置与更改用户密码

命令:

1
SET PASSWORD FOR 'username'@'host' = PASSWORD('newpassword');

如果是当前登陆用户用:

1
SET PASSWORD = PASSWORD("newpassword");

例子:

1
SET PASSWORD FOR 'pig'@'%' = PASSWORD("123456");

四. 撤销用户权限

命令:

1
REVOKE privilege ON databasename.tablename FROM 'username'@'host';

说明:

privilege, databasename, tablename:同授权部分

例子:

1
REVOKE SELECT ON *.* FROM 'pig'@'%';

注意:

假如你在给用户 'pig'@'%' 授权的时候是这样的(或类似的): GRANT SELECT ON test.user TO 'pig'@'%' ,则在使用 REVOKE SELECT ON *.* FROM 'pig'@'%'; 命令并不能撤销该用户对test数据库中user表的 SELECT 操作。相反,如果授权使用的是 GRANT SELECT ON *.* TO 'pig'@'%';REVOKE SELECT ON test.user FROM 'pig'@'%'; 命令也不能撤销该用户对test数据库中user表的 Select 权限。

具体信息可以用命令 SHOW GRANTS FOR 'pig'@'%'; 查看。

五.删除用户

命令:

1
DROP USER 'username'@'host';

六.刷新

1
2
3
支持远程连接:
ALTER user 'root'@'%' IDENTIFIED WITH mysql_native_password BY '***';
FLUSH PRIVILEGES;

在网站登陆注册时常常需要用到验证码,来防止站点被攻击。

大概这个样子:

现在框架是前后端分离的,angular+webapi弄个验证码感觉有点麻烦
然后就找到google的reCAPTCHA 还挺好使的,记录一下:
地址:https://www.google.com/recaptcha/admin/create
填写注册信息:

一个在前端配置,一个再后端配置:
可以参考这里:https://developers.google.com/recaptcha/docs/invisible

普通html配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<html>
<head>
<title>reCAPTCHA demo: Simple page</title>
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
<script>
function onSubmit(token) {
document.getElementById("demo-form").submit();
}
</script>
</head>
<body>
<form id='demo-form' action="?" method="POST">
<button class="g-recaptcha" data-sitekey="your_site_key" data-callback='onSubmit'>Submit</button>
<br/>
</form>
</body>
</html>

angular中使用:
安装ng-recaptcha

1
npm i ng-recaptcha --save

在表单中配置:

1
2
3
4
<re-captcha class="grecaptcha" #captchaRef="reCaptcha" siteKey="YOUR_KEY" size="invisible" (resolved)="$event && _submitForm($event,validateForm.value)"></re-captcha>
<button nz-button (click)="captchaRef.execute()" (click)="savereCaptcha(captchaRef)" nzType="primary">登录</button>


ts代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
_submitForm(recaptchaId, value) {
for (const i in this.validateForm.controls) {
if (this.validateForm.controls.hasOwnProperty(i)) {
this.validateForm.controls[i].markAsDirty();
}
}

this.isLoading = true;
this.authenticationService.login(value.userName, value.password, recaptchaId)
.subscribe(
data => {
this.isLoading = false;
this.grecaptcha.reset(); // 刷新验证码
this.router.navigate([this.returnUrl]);
},
error => {
this.isLoading = false;
this.error = true;
this.grecaptcha.reset(); // 刷新验证码
});
}
savereCaptcha(captchaRef) {
this.grecaptcha = captchaRef.grecaptcha;
}

.Net Core 后台校验,在登陆或注册的地方:

1
2
3
4
5
// 生产环境才校验验证码 公司网络有毒 ping www.recaptcha.net 经常不通
if (_env.IsProduction() && !await CheckReCAPTCHA(auth.recaptchaToken))
{
return NotFound("Google reCaptcha validation failed");
}

检验方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private async Task<bool> CheckReCAPTCHA(string recaptchaToken)
{
string baseUrl = _config["reCAPTCHA:baseUrl"];
string secretKey = _config["reCAPTCHA:secretKey"];

var values = new Dictionary<string, string>
{
{ "secret", secretKey },
{ "response",recaptchaToken }
};
var content = new FormUrlEncodedContent(values);
HttpClient httpClient = new HttpClient();

var response = await httpClient.PostAsync(baseUrl, content);
var responseString = await response.Content.ReadAsStringAsync();

var obj = JObject.Parse(responseString);
var status = (bool)obj.SelectToken("success");
return status;
}

由于国内访问Google受限制,需要讲api地址改下:

将前端中JS的src

1
https://www.google.com/recaptcha/api.js

修改为

1
https://www.recaptcha.net/recaptcha/api.js

将后端的API地址从

1
https://www.google.com/recaptcha/api/siteverify

换为

1
https://www.recaptcha.net/recaptcha/api/siteverify

参考:https://131.re/marchives/110

Google Api后挺好使的,效果也是超乎想象的