三、Echart图表 之 X轴(xAxis)与 Y轴(yAxis)配置项大全_echart yaxis-CSDN博客

mikel阅读(413)

来源: 三、Echart图表 之 X轴(xAxis)与 Y轴(yAxis)配置项大全_echart yaxis-CSDN博客

参考文章:Echarts X轴(xAxis)

xAxis与yAxis中有很多配置项,下面我以xAxis进行详解,yAxis参考xAxis即可

nameTextStyle:坐标轴名称的文字样式

axisLine:坐标轴轴线相关设置。

axisTick:坐标轴刻度相关设置。

axisLabel:坐标轴刻度标签的相关设置。

splitLine: 坐标轴在 grid 区域中的分隔线设置。

splitArea :坐标轴在 grid 区域中的分隔区域,默认不显示。

上述xAxis与yAxis中的配置项,其中也会含有很多属性,具体使用请参考一下内容。

xAxis: {
id: ”,
show: true, //是否显示x轴
gridIndex: 0, //轴所在grid索引,默认位于第一个grid
alignTicks: false, //在多个轴为数值轴的时候,可以开启该配置项自动对齐刻度。只对’value’和’log’类型的轴有效
position: ‘top’, //轴的位置(top/bottom)
offset: 0, //轴相对于默认位置的偏移,在相同的position上有多个轴时有用
type: ‘category’, //坐标轴类型,值category/value,与y轴呼应,若x轴配置category则y轴配置value
name: ”, //坐标轴名称
nameLocation: ‘end’, //坐标轴名称显示位置,可选值start/middle[或者center]/end
nameTextStyle: {}, //一般样式也很少直接修改且内容过多待更新… …
nameGap: 15, //坐标轴名称与轴线间距离
nameRotate: 10, //坐标轴名字旋转,角度值
inverse: false, //是否是反向坐标轴
boundaryGap: [‘20%’, ‘20%’], // 坐标轴两边留白策略,也可以使用布尔值,true居中
min: ”, //刻度最小值
max: ”, //刻度最大值
scale: false, //只在数值轴中type: ‘value’有效, 设置min和max后无效。是否是脱离 0 值比例。设置成true后坐标刻度不会强制包含零刻度。在双数值轴的散点图中较有用
splitNumber: 5, //坐标轴的分割段数(预估值)
minInterval: 0, //自动计算坐标轴最小间隔,例:设置成1,刻度没有小数
maxInterval: ”, //自动计算坐标轴最大间隔
axisLine: {
show: true, // 是否显示坐标轴轴线
symbol: [‘none’, ‘arrow’], // 轴线两端箭头,两个值,none表示没有箭头,arrow表示有箭头
symbolSize: [10, 15], // 轴线两端箭头大小,数值一表示宽度,数值二表示高度
lineStyle: {
color: ‘#333’, // 坐标轴线线的颜色
width: ‘5’, // 坐标轴线线宽
type: ‘solid’, // 坐标轴线线的类型(solid实线类型;dashed虚线类型;dotted点状类型)
},
},
axisTick: {
show: true, // 是否显示坐标轴刻度
inside: true, // 坐标轴刻度是否朝内,默认朝外
length: 5, //坐标轴刻度的长度
lineStyle: {
color: ‘#FFF’, //刻度线的颜色
width: 10, //坐标轴刻度线宽
type: ‘solid’, //坐标轴线线的类型(solid实线类型;dashed虚线类型;dotted点状类型)
},
},
axisLabel: {
show: true, //是否显示刻度标签
interval: ‘0’, //坐标轴刻度标签的显示间隔,在类目轴中有效.0显示所有
inside: true, //刻度标签是否朝内,默认朝外
rotate: 90, //刻度标签旋转的角度,在类目轴的类目标签显示不下的时候可以通过旋转防止标签之间重叠;旋转的角度从-90度到90度
margin: 10, //刻度标签与轴线之间的距离
// formatter 刻度标签的内容格式器,支持字符串模板和回调函数两种形式
color: ‘#FFF’, // 刻度标签文字的颜色
fontStyle: ‘normal’, // 字体的风格(normal无样式;italic斜体;oblique倾斜字体)
fontWeight: ‘normal’, // 字体的粗细(normal无样式;bold加粗;bolder加粗再加粗;lighter变细;数字定义粗细也可以取值范围100至700)
fontSize: ’20’, //文字字体大小
align: ‘left’, // 文字水平对齐方式,默认自动(left/center/right)
verticalAlign: ‘left’, // 文字垂直对齐方式,默认自动(top/middle/bottom)
lineHeight: ’50’, // 行高
backgroundColor: ‘red’, // 文字块背景色,例:#123234, red, rgba(0,23,11,0.3)
},
splitLine: {
show: true, // 是否显示分隔线。默认数值轴显示,类目轴不显示
interval: ‘0’, // 坐标轴刻度标签的显示间隔,在类目轴中有效.0显示所有
color: [‘#ccc’], //color分隔线颜色,可设置单个颜色,也可设置颜色数组,分隔线会按数组中颜色顺序依次循环设置颜色
width: 3, // 分隔线线宽
type: ‘solid’, // 坐标轴线线的类型(solid实线类型;dashed虚线类型;dotted点状类型)
},
splitArea: {
show: true, // 是否显示分隔区域
interval: ‘0’, // 坐标轴刻度标签的显示间隔,在类目轴中有效.0显示所有
areaStyle: {
color: [‘rgba(250,250,250,0.3)’,’rgba(200,200,200,0.3)’], //color分隔区域颜色。分隔区会按数组中颜色顺序依次循环设置颜色。默认是一个深浅的间隔色
opacity: 1, // 图形透明度。支持从0到1的数字,为0时不绘制该图形
},
},
data: {
textStyle: {
color: ‘#FFF’, // 文字的颜色
fontStyle: ‘normal’, // 文字字体的风格(normal无样式;italic斜体;oblique倾斜字体)
fontWeight: ‘normal’, //字体的粗细(normal无样式;bold加粗;bolder加粗再加粗;lighter变细;数字定义粗细也可以取值范围100至700)
fontSize: ’20’, // 文字字体大小
align: ‘left’, // 文字水平对齐方式,默认自动(left/center/right)
verticalAlign: ‘left’, // 文字垂直对齐方式,默认自动(top/middle/bottom)
lineHeight: ’50’, // 行高
backgroundColor: ‘red’, // 文字块背景色,例:#123234, red, rgba(0,23,11,0.3)
}
}
}

👉推荐相关文章:Echart图表 之 基本使用及配置项

👉推荐相关文章:Echart图表 之 title配置项大全

👉推荐相关文章:Echart图表 之 颜色color配置项大全

👉推荐相关文章:Echart图表 之 legend图例组件配置项大全

👉推荐相关文章:Echart图表 之 tooltip提示框组件配置项大全

👉推荐相关文章:Echart图表 之 toolbox工具栏组件配置项大全
————————————————
版权声明:本文为CSDN博主「仙女不下凡」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_55181759/article/details/124835864

【精选】教你从零开始画echarts地图_cRack_cLick的博客-CSDN博客

mikel阅读(290)

来源: 【精选】教你从零开始画echarts地图_cRack_cLick的博客-CSDN博客

echarts地图制作
离线地图下载地址https://datav.aliyun.com/tools/atlas/index.html

echarts文档地址https://echarts.apache.org/zh/option.html

基于VUE编写,其他框架请自行转换,大同小异

基础配置
先让地图内容出来,npm安装步骤省略,请参考官方文档,创建的div必须设置宽度和高度,关于图表的宽高自适应,参考我的另一篇文章

<template>
<div class=”content”>
<div ref=”charts” style=”width: 1000px; height: 800px”></div>
</div>
</template>

<script>
import * as echarts from “echarts”;
import zhongguo from “@/assets/mapJson/data-city.json”
export default {
created () {
this.$nextTick(() => {
this.initCharts();
})
},
methods: {
initCharts() {
const charts = echarts.init(this.$refs[“charts”]);
const option = {
// 背景颜色
backgroundColor: “#404a59”,
// 提示浮窗样式
tooltip: {
show: true,
trigger: “item”,
alwaysShowContent: false,
backgroundColor: “#0C121C”,
borderColor: “rgba(0, 0, 0, 0.16);”,
hideDelay: 100,
triggerOn: “mousemove”,
enterable: true,
textStyle: {
color: “#DADADA”,
fontSize: “12”,
width: 20,
height: 30,
overflow: “break”,
},
showDelay: 100
},
// 地图配置
geo: {
map: “china”,
label: {
// 通常状态下的样式
normal: {
show: true,
textStyle: {
color: “#fff”,
},
},
// 鼠标放上去的样式
emphasis: {
textStyle: {
color: “#fff”,
},
},
},
// 地图区域的样式设置
itemStyle: {
normal: {
borderColor: “rgba(147, 235, 248, 1)”,
borderWidth: 1,
areaColor: {
type: “radial”,
x: 0.5,
y: 0.5,
r: 0.8,
colorStops: [
{
offset: 0,
color: “rgba(147, 235, 248, 0)”, // 0% 处的颜色
},
{
offset: 1,
color: “rgba(147, 235, 248, .2)”, // 100% 处的颜色
},
],
globalCoord: false, // 缺省为 false
},
shadowColor: “rgba(128, 217, 248, 1)”,
shadowOffsetX: -2,
shadowOffsetY: 2,
shadowBlur: 10,
},
// 鼠标放上去高亮的样式
emphasis: {
areaColor: “#389BB7”,
borderWidth: 0,
},
},
},
};
// 地图注册,第一个参数的名字必须和option.geo.map一致
echarts.registerMap(“china”,zhongguo)

charts.setOption(option);
},
},
};
</script>

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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
这是最基础的配置,外加了一些我自己写的样式,使地图美观一些,如果你完全的复制,并且china.json文件也引入了,那么你会看到如下的内容

 

这其中比较有意思的是,如果你注册地图时,还有option.geo.map的名字用的是china,南海诸岛会如上图以缩略图展示,但是以此之外来命名地图,则不会展示缩略图。

再次声明,如果二者名字不一致,将会导致异常,致使地图无法显示

数据渲染
实际开发中,往往需要将后台的数据渲染到地图里,我们在option里添加series属性,以下是我的两个示例,仅做参考:

series: [
{
type: “scatter”,
coordinateSystem: “geo”,
symbol: “pin”,
legendHoverLink: true,
symbolSize: [60, 60],
// 这里渲染标志里的内容以及样式
label: {
show: true,
formatter(value) {
return value.data.value[2];
},
color: “#fff”,
},
// 标志的样式
itemStyle: {
normal: {
color: “rgba(255,0,0,.7)”,
shadowBlur: 2,
shadowColor: “D8BC37”,
},
},
// 数据格式,其中name,value是必要的,value的前两个值是数据点的经纬度,其他的数据格式可以自定义
// 至于如何展示,完全是靠上面的formatter来自己定义的
data: [
{ name: “西藏”, value: [91.23, 29.5, 2333] },
{ name: “黑龙江”, value: [128.03, 47.01, 1007] },
],
showEffectOn: “render”,
rippleEffect: {
brushType: “stroke”,
},
hoverAnimation: true,
zlevel: 1,
},
// {
// type: “effectScatter”,
// coordinateSystem: “geo”,
// effectType: “ripple”,
// showEffectOn: “render”,
// rippleEffect: {
// period: 10,
// scale: 10,
// brushType: “fill”,
// },

// hoverAnimation: true,
// itemStyle: {
// normal: {
// color: “rgba(255, 235, 59, .7)”,
// shadowBlur: 10,
// shadowColor: “#333”,
// },
// },
// zlevel: 1,
// data: [
// { name: “西藏”, value: [91.23, 29.5, 2333] },
// { name: “黑龙江”, value: [128.03, 47.01, 1007] },
// ],
// },
],

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
57
58
59
60
61
62
两种渲染方式如下:

 

 

使用备注的部分时,需要在option.tooltip里添加formatter属性,我写的如下:

const option = {
// …
tooltip: {
// …
formatter(params) {
return `地区:${params.name}</br>数值:${params.value[2]}`;
}
}
}
1
2
3
4
5
6
7
8
9
更多的方式还要自己多试验,这是个费时且需要耐心的活,你甚至可以将柱状图放上去。有更花里胡哨的效果,也请分享给我。

嵌入文字
使用option.graphic可以实现简单的水印效果

const option = {
// …
graphic:{
// 水印类型
type: ‘text’,
// 相对于容器的位置
left:’10%’,
top: ‘10%’,
// 样式设置
style: {
// 文本内容
text: “create by cRack_cLick”,
// 字体粗细、大小、字体
font: ‘bolder 1.5rem “Microsoft YaHei”, sans-serif’,
// 字体颜色
fill: “#fff”
}
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
效果如下:

 

利用graphic的type=“group”,还可以组合出一些有意思的效果(抄官方文档的效果):

graphic: {
type: “group”,
rotation: Math.PI / 4,
bounding: “raw”,
left: 110,
top: 110,
z: 100,
children: [
{
type: “rect”,
left: “center”,
top: “center”,
z: 100,
shape: {
width: 400,
height: 50,
},
style: {
fill: “rgba(0,0,0,0.3)”,
},
},
{
type: “text”,
left: “center”,
top: “center”,
z: 100,
style: {
fill: “#ddd”,
text: “create by cRack_cLick”,
font: ‘bolder 1.5rem “Microsoft YaHei”, sans-serif’,
},
},
],
},

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

地图下钻
往往还有一种需求,在我们点击一个省的时候,需要切换到这个省的详细地图,甚至还可以下钻到市、县等等。

为了试下点击下钻,我们需要先了解echarts中的点击事件,文档参考

以目前的功能来说,我们暂时不需要加入其它的业务逻辑以及省级的数据渲染,仅仅只做地图的切换,所以点击事件里我们需要实现获取点击的省份名称,然后根据省份名称,来选择地图的JSON文件,最后重新渲染echarts图表,下面是我的简单示例:

// 新增加北京的地图JSON文件
import beijing from “@/assets/mapJson/data-beijing.json”;
// …

initCharts(){
const charts = echarts.init(this.$refs[“charts”]);
// …
// 注意这里是echarts的实例对象,而不是echarts组件本身。
charts.on(‘click’, ({name}) => {
if (name === “北京”) {
// 修改option的配置,可以继续自定义
option.geo.zoom = 0.8
// 就像上面提到的,这里必须要和注册地图时的名字一致
option.geo.map = “beijing”
// 注册地图
echarts.registerMap(“beijing”, beijing)
// 重新渲染
charts.setOption(option, true)
}
})
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
需要注意的是,在重新setOption的时候,我们加入了第二个参数,按照官方文档的说法:

参数:

调用方式:

chart.setOption(option, notMerge, lazyUpdate);
1
或者

chart.setOption(option, {
notMerge: …,
lazyUpdate: …,
silent: …
});
1
2
3
4
5
option

图表的配置项和数据。

notMerge

可选,是否不跟之前设置的 option 进行合并,默认为 false,即合并。

lazyUpdate

可选,在设置完 option 后是否不立即更新图表,默认为 false,即立即更新。

silent

可选,阻止调用 setOption 时抛出事件,默认为 false,即抛出事件。

第二个从参数设置为true来让图表重新渲染,而不合并配置,当然,这一点具体需要看你显示开发的需求,我在这里仅是为了演示。绝不是偷懒

另外在echarts v3.x的版本里,切换地图默认是有过渡动画的,而v4.x和v5.x的版本里则没有过渡动画,如果知道怎么加上的,可以私信我。

上面虽然可以实现地图切换,但很显然开发中这么写要被打死。下钻三十多个地图要写三十多个if,显然是一种不理智的开发方式。一种方式我们可以通过axios或者ajax异步请求,但是这样需要你在生产环境和运维协商好,否则会导致请求不到JSON文件。

下面是我在前端写的一个简单的工具方法,仅供参考:

import zhongguo from “@/assets/mapJson/data-city.json”;
import neimenggu from “@/assets/mapJson/data-neimenggu.json”;
import beijing from “@/assets/mapJson/data-beijing.json”;
// …

const mapDict = {
“北京”: “beijing”,
“内蒙古”: “neimenggu”,
// …
}

const mapData = {
beijing,
neimenggu,
// …
}

export function getMap(mapName) {
const cityName = mapDict[mapName]
if(cityName){
return [cityName, mapData[cityName]]
}
return [‘china’, zhongguo]
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
需要建立两个字典,一个是汉字和拼音的对照映射,一个是拼音和JSON文件的映射,这个可灵活配置,并非唯一。

优化一下上面的的代码:

// 删除地图json文件的引用,修改为上面的工具方法
import { getMap } from “./maputil”;

methods: {
initCharts() {
const charts = echarts.init(this.$refs[“charts”]);
const option = {
// …
};
// 不传name默认会返回中国地图
const [mapName, mapJson] = getMap();
option.geo.map = mapName;
// 地图注册,第一个参数的名字必须和option.geo.map一致
echarts.registerMap(mapName, mapJson);

charts.setOption(option);

charts.on(“click”, ({ name }) => {
// 这里和上面一样,其实还可以再优化一下。为了方便阅读,这里不再封装。
const [mapName, mapJson] = getMap(name);
option.geo.zoom = 0.8;
option.geo.map = mapName;
echarts.registerMap(mapName, mapJson);
charts.setOption(option, true);
});
}
}
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
效果如下:

 

结合水印制作级联效果
现在的地图可以下钻了,但是似乎操作起来还有些别扭。

我们现在想要的效果是:我们需要每下钻一层,水印部分就会加上当前地区的名称。点击水印地区的名称,就会跳转到当前地区的地图,我们要来改造一下echarts示例的click事件。

首先option.graphic的默认值修改为中国地图,这里为了方便阅读,仅使用text格式演示:

// …
graphic: [
{
type: “text”,
left: “10%”,
top: “10%”,
style: {
text: “中国”,
font: ‘bolder 1.5rem “Microsoft YaHei”, sans-serif’,
fill: “#fff”,
},
},
],
1
2
3
4
5
6
7
8
9
10
11
12
13
以数组的形势编写后,思路就明显了,只要在click事件的时候,将下钻地图的信息push进来,并且为了防止重合,稍微移动一下定位即可,我的示例如下:

charts.on(“click”, ({ name }) => {
const [mapName, mapJson] = getMap(name);
option.geo.zoom = 0.8;
option.geo.map = mapName;
// 为了重新定位,这里使用了length
const idx = option.graphic.length + 1;
option.graphic.push({
type: “text”,
left: `${idx * 10}%`,
top: “10%”,
style: {
text: name,
font: ‘bolder 1.5rem “Microsoft YaHei”, sans-serif’,
fill: “#fff”,
},
});
echarts.registerMap(mapName, mapJson);
charts.setOption(option, true);
});

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
点击后效果如下:

 

现在还有问题,就是点击地区名字没有响应,所以我们还要为option.graphic子元素加上click事件

这个click事件功能也类似,获取地图名称,获取地图数据,重新渲染。但是这个click事件需要注意,比如我点击了北京,那么在数组里是需要将密云区的元素删除掉的,同理,点击中国,则后面的元素都要删除。在这里我就不把相同的部分抽离出来了:

// 防止graph里频繁添加click事件,在添加click事件之前先全部清空掉。
charts.off()
charts.on(“click”, ({name}) => {
// 如果option.graphic里已经有了城市名称,则不进行任何操作,防止频繁点击
const index = option.graphic.findIndex(i => i.style.text === name);
if (!name || index !== -1) return
const [mapName, mapJson] = getMap(name);
option.geo.zoom = 0.8;
option.geo.map = mapName;
// 为了重新定位,这里使用了length
const idx = option.graphic.length + 1;
option.graphic.push({
type: “text”,
left: `${idx * 10}%`,
top: “10%”,
style: {
text: name,
font: ‘bolder 1.5rem “Microsoft YaHei”, sans-serif’,
fill: “#fff”,
},
onclick: () => {
// 利用函数的作用域,可以直接拿上面的name来用
const [grahpName, graphJson] = getMap(name);
const index = option.graphic.findIndex(i => i.style.text === name);
// 点击元素之后的所有元素全部删除
option.graphic.splice(index + 1);
// 很多操作重复了,你可以将公共部分抽离出来
option.geo.map = mapName;
echarts.registerMap(grahpName, graphJson);
charts.setOption(option, true);
},
});
echarts.registerMap(mapName, mapJson);
charts.setOption(option, true);
});
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
这里会有个坑,在给graph添加click事件后,点击时会同时触发我们上面charts.on的click事件,想了很久也没有找到好一点的方式来解决这个事件冲突,最后只好判断了一下name是否为空来暂时解决。如果有更好的办法,也请留言。最终效果如下:

 

至此绘制地图已经完毕,更多是依靠自己的业务需求来进行更灵活的配置和渲染,它的API没有什么太复杂的,只是我们缺少了一点耐心去实验。

visualMap
首先来看效果

 

增加visualMap来让地图的数据渲染更有层次感,实现起来也很简单,只需要在option里增加visualMap配置即可:

const option = {
// …
visualMap: {
// 是否展示左下角,即是是false,也仅是不显示,不影响数据的映射
show: true,
// 上下端文字
text: [“高”, “低”],
// 最小值和最大值,必须指定
min: 0,
max: 6000,
// 位置
left: “10%”,
bottom: “10%”,
// 是否展示滑块
calculable: true,
// 指定映射的数据,对应的是option.series,这里根据自己的实际需要进行配置
seriesIndex: [0],
// 从下到上的颜色
inRange: {
color: [‘#00467F’, ‘#A5CC82’],
},
//字体颜色
textStyle: {
color: “#fff”,
map: “china”,
},
}
}

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
如果你的代码是跟着我从上面一直写下来的,那么此时你应该发现只是定位的图标变了,相应的地图区域并未变色,所以我们还要把地图的数据映射上去,所以在option.series里再加一个元素,使其type=“map”,内容与geo一致即可,但是要多加data属性,渲染的数据和定位图标一致。并将seriesIndex的索引做好映射,即可实现。

const option = {
// …
visualMap: {
// 是否展示左下角,即是是false,也仅是不显示,不影响数据的映射
show: true,
// 上下端文字
text: [“高”, “低”],
// 最小值和最大值,必须指定
min: 0,
max: 6000,
// 位置
left: “10%”,
bottom: “10%”,
// 是否展示滑块
calculable: true,
// 指定映射的数据,对应的是option.series,这里根据自己的实际需要进行配置
seriesIndex: [0],
// 从下到上的颜色
inRange: {
color: [‘#00467F’, ‘#A5CC82’],
},
//字体颜色
textStyle: {
color: “#fff”,
map: “china”,
},
}
}
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
如果你的代码是跟着我从上面一直写下来的,那么此时你应该发现只是定位的图标变了,相应的地图区域并未变色,所以我们还要把地图的数据映射上去,所以在option.series里再加一个元素,使其type=“map”,内容与geo一致即可,但是要多加data属性,渲染的数据和定位图标一致。并将seriesIndex的索引做好映射,即可实现。

如果出现了缩放重影,说明生成了两个地图组件,需要在新的series里加上geoIndex属性,值是geo里的索引,这样就只会共享一个组件,不会出现缩放重影的问题了

以下为源文档:

默认情况下,map series 会自己生成内部专用的 geo 组件。但是也可以用这个 geoIndex 指定一个 geo组件。这样的话,map 和 其他 series(例如散点图)就可以共享一个 geo组件了。并且,geo组件的颜色也可以被这个 map series 控制,从而用 visualMap来更改。

当设定了 geoIndex 后,series-map.map属性,以及 series-map.itemStyle 等样式配置不再起作用,而是采用 geo中的相应属性。

很多人找我要源码,博主的本意是希望大家可以自己实验,加深印象。就我个人来说,我拿到源码后就懒得去研究了,总觉得自己已经会了。但介于可能很多人还是刚刚接触,对很多内容并不熟悉,所以在这里贴一下源码,只不过因为时间久远,已经找不到写文章时的代码了,这里贴的是从项目里抽出来的代码,可能会有一些冗余,所以大家尽量参考文章来理解:源码地址
————————————————
版权声明:本文为CSDN博主「cRack_cLick」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/SkelleBest/article/details/121204097

利用html 5 websocket做个山寨版web聊天室(手写C#服务器) - 谦行 - 博客园

mikel阅读(288)

来源: 利用html 5 websocket做个山寨版web聊天室(手写C#服务器) – 谦行 – 博客园

之前的博客中提到过看到html5 的websocket后很感兴趣,终于可以摆脱长轮询(websocket之前的实现方式可以看看Developer Works上的一篇文章,有简单提到,同时也说了websocket基本概念)等方式做一个山寨版的web聊天室。

什么是websocket

WebSocket 协议是html5引入的一种新的协议,其目的在于实现了浏览器与服务器全双工通信。看了上面链接的同学肯定对过去怎么低效率高消耗(轮询或comet)的做此事已经有所了解了,而在websocket API,浏览器和服务器只需要要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。同时这么做有两个好处

1.通信传输字节减少:比起以前使用http传输数据,websocket传输的额外信息很少,据百度说只有2k

2.服务器可以主动向客户端推送消息,而不用客户端去查询

关于概念和好处,网上到处都是,不再赘述,简单看看其原理,然后动手写一个web版聊天室吧

握手

除了TCP连接的三次握手,websocket协议中客户端与服务器想建立连接需要一次额外的握手动作,在最新版的协议中是这个样子的

客户端向服务器发送请求

复制代码
GET / HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: 127.0.0.1:8080
Origin: http://test.com
Pragma: no-cache
Cache-Control: no-cache
Sec-WebSocket-Key: OtZtd55qBhJF2XLNDRgUMg==
Sec-WebSocket-Version: 13
Sec-WebSocket-Extensions: x-webkit-deflate-frame
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36
复制代码

服务器给出响应

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: xsOSgr30aKL2GNZKNHKmeT1qYjA=

在请求中的“Sec-WebSocket-Key”是随机的,服务器端会用这些数据来构造出一个SHA-1的信息摘要。把“Sec-WebSocket-Key”加上一个魔幻字符串“258EAFA5-E914-47DA-95CA-C5AB0DC85B11”。使用 SHA-1 加密,之后进行 BASE-64编码,将结果做为 “Sec-WebSocket-Accept” 头的值,返回给客户端(来自维基百科)。

websocket API

经过握手之后浏览器与服务器建立连接,两者就可以互相通信了。websocket的API真心很简单,看看W3C的定义

复制代码
enum BinaryType { "blob", "arraybuffer" };
[Constructor(DOMString url, optional (DOMString or DOMString[]) protocols)]
interface WebSocket : EventTarget {
  readonly attribute DOMString url;

  // ready state
  const unsigned short CONNECTING = 0;
  const unsigned short OPEN = 1;
  const unsigned short CLOSING = 2;
  const unsigned short CLOSED = 3;
  readonly attribute unsigned short readyState;
  readonly attribute unsigned long bufferedAmount;

  // networking
           attribute EventHandler onopen;
           attribute EventHandler onerror;
           attribute EventHandler onclose;
  readonly attribute DOMString extensions;
  readonly attribute DOMString protocol;
  void close([Clamp] optional unsigned short code, optional DOMString reason);

  // messaging
           attribute EventHandler onmessage;
           attribute BinaryType binaryType;
  void send(DOMString data);
  void send(Blob data);
  void send(ArrayBuffer data);
  void send(ArrayBufferView data);
};
复制代码

创建websocket

ws=new WebSocket(address); //ws://127.0.0.1:8080

 

调用其构造函数,传入地址,就可以创建一个websocket了,值得注意的是地址协议得是ws/wss

关闭socket

ws.close();

 

调用webservice实例的close()方法就可以关闭webservice,当然也可以传入一个code和string说明为什么关了

几个回调函数句柄

由于其异步执行,回调函数自然少不了,有四个重要的

  • onopen:连接创建后调用
  • onmessage:接收到服务器消息后调用
  • onerror:出错时调用
  • onclose:关闭连接的时候调用

看名字就知道是干什么的了,每个回调函数都会传入一个Event对象,可以通过event.data访问消息

使用API

我们可以在创建socket成功后为其回调函数赋值

复制代码
ws=new WebSocket(address);
            ws.onopen=function(e){
                var msg=document.createElement('div');
                msg.style.color='#0f0';
                msg.innerHTML="Server > connection open.";
                msgContainer.appendChild(msg);
                ws.send('{<'+document.getElementById('name').value+'>}');
            };
复制代码

也可以通过事件绑定的方式

复制代码
ws=new WebSocket(address);
            ws.addEventListener('open',function(e){
                var msg=document.createElement('div');
                msg.style.color='#0f0';
                msg.innerHTML="Server > connection open.";
                msgContainer.appendChild(msg);
                ws.send('{<'+document.getElementById('name').value+'>}');
            });
复制代码

 客户端实现

其实客户端的实现比较简单,除了websocket相关的几句就是一些自动focus、回车键事件处理、消息框自动定位到底部等简单功能,不一一说明了

复制代码
 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <title>Web Socket Client</title>
 5 </head>
 6 <body style="padding:10px;">
 7     <h1>Web Socket Chating Room</h1>
 8     <div style="margin:5px 0px;">
 9         Address:
10         <div><input id="address" type="text" value="ws://127.0.0.1:8080" style="width:400px;"/></div>
11     </div>
12     <div style="margin:5px 0px;">
13         Name:
14         <div><input id="name" type="text" value="Byron" style="width:400px;"/></div>
15     </div>
16     <div>
17         <button id="connect" onclick="connect();">connect server</button> &nbsp;&nbsp;
18         <button id="disconnect" onclick="quit();">disconnect</button>&nbsp;&nbsp;
19         <button id="clear" onclick="clearMsg();">clear</button>
20     </div>
21     <h5 style="margin:4px 0px;">Message:</h5>
22     <div id="message" style="border:solid 1px #333; padding:4px; width:400px; overflow:auto;
23          background-color:#404040; height:300px; margin-bottom:8px; font-size:14px;">
24     </div>
25     <input id="text" type="text" onkeypress="enter(event);" style="width:340px"/> &nbsp;&nbsp;
26     <button id="send" onclick="send();">send</button>
27 
28     <script type="text/javascript">
29         var name=document.getElementById('name').value;
30         var msgContainer=document.getElementById('message');
31         var text=document.getElementById('text');
32 
33         function connect () {
34             var address=document.getElementById('address').value;
35             
36             ws=new WebSocket(address);
37             ws.onopen=function(e){
38                 var msg=document.createElement('div');
39                 msg.style.color='#0f0';
40                 msg.innerHTML="Server > connection open.";
41                 msgContainer.appendChild(msg);
42                 ws.send('{<'+document.getElementById('name').value+'>}');
43             };
44             ws.onmessage=function(e){
45                 var msg=document.createElement('div');
46                 msg.style.color='#fff';
47                 msg.innerHTML=e.data;
48                 msgContainer.appendChild(msg);
49             };
50             ws.onerror=function(e){
51                 var msg=document.createElement('div');
52                 msg.style.color='#0f0';
53                 msg.innerHTML='Server > '+e.data;
54                 msgContainer.appendChild(msg);
55             };
56             ws.onclose=function(e){
57                 var msg=document.createElement('div');
58                 msg.style.color='#0f0';
59                 msg.innerHTML="Server > connection closed by server.";
60                 msgContainer.appendChild(msg);
61             };
62             text.focus();
63         }
64 
65         function quit(){
66             if(ws){
67                 ws.close();
68                 var msg=document.createElement('div');
69                 msg.style.color='#0f0';
70                 msg.innerHTML='Server > connection closed.';
71                 msgContainer.appendChild(msg);
72                 ws=null;
73             }
74         }
75 
76         function send(){
77             ws.send(text.value);
78             setTimeout(function(){
79                 msgContainer.scrollTop=msgContainer.getBoundingClientRect().height;
80             },100);
81             text.value='';
82             text.focus();
83         }
84 
85         function clearMsg(){
86             msgContainer.innerHTML="";
87         }
88 
89         function enter(event){
90             if(event.keyCode==13){
91              send(); 
92             } 
93         }
94     </script>
95 </body>
96 </html>
复制代码

 

服务器支持

其实websocket的服务器实现网上可以找到很多了,向维基百科中列出的

但是我希望加入一些自己特定的处理,比如用户名识别啊、广播啊什么的,所以自己实现了一个简单的C#版本

websocket 聊天室C#服务器实现

我上篇博客C# socket编程实践——支持广播的简单socket服务器中介绍了怎么实现一个简单的C#广播服务器,在这上面扩展两个部分就能作为websocket服务器了

1.wecsocket握手处理

当服务器收到浏览器握手请求后,首先需要识别握手信息,我简单粗暴的使用了包含字符串处理,当然加入了是否握过手的判断

复制代码
client.BeginReceive (buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback (Recieve), client);
                string msg = Encoding.UTF8.GetString (buffer, 0, length);

                if (!clientPool [client].IsHandShaked && msg.Contains ("Sec-WebSocket-Key")) {
                    client.Send (PackageHandShakeData (buffer, length));
                    clientPool [client].IsHandShaked = true;
                    return;
                }
复制代码

 

识别成功后就可以生成服务器响应发送给客户端了

复制代码
/// <summary>
        /// 打包服务器握手数据
        /// </summary>
        /// <returns>The hand shake data.</returns>
        /// <param name="handShakeBytes">Hand shake bytes.</param>
        /// <param name="length">Length.</param>
        private byte[] PackageHandShakeData (byte[] handShakeBytes, int length)
        {
            string handShakeText = Encoding.UTF8.GetString (handShakeBytes, 0, length);
            string key = string.Empty;
            Regex reg = new Regex (@"Sec\-WebSocket\-Key:(.*?)\r\n");
            Match m = reg.Match (handShakeText);
            if (m.Value != "") {
                key = Regex.Replace (m.Value, @"Sec\-WebSocket\-Key:(.*?)\r\n", "$1").Trim ();
            }

            byte[] secKeyBytes = SHA1.Create ().ComputeHash (
                                     Encoding.ASCII.GetBytes (key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"));
            string secKey = Convert.ToBase64String (secKeyBytes);

            var responseBuilder = new StringBuilder ();
            responseBuilder.Append ("HTTP/1.1 101 Switching Protocols" + "\r\n");
            responseBuilder.Append ("Upgrade: websocket" + "\r\n");
            responseBuilder.Append ("Connection: Upgrade" + "\r\n");
            responseBuilder.Append ("Sec-WebSocket-Accept: " + secKey + "\r\n\r\n");

            return Encoding.UTF8.GetBytes (responseBuilder.ToString ());
        }
复制代码

 

2.解析浏览器消息和处理发送给浏览器的消息

websocket中数据并不完全是消息本身,我们需要做一些处理

复制代码
/// <summary>
        /// 解析客户端发送来的数据
        /// </summary>
        /// <returns>The data.</returns>
        /// <param name="recBytes">Rec bytes.</param>
        /// <param name="length">Length.</param>
        private string AnalyzeClientData (byte[] recBytes, int length)
        {
            if (length < 2) {
                return string.Empty;
            }

            bool fin = (recBytes [0] & 0x80) == 0x80; // 1bit,1表示最后一帧  
            if (!fin) {
                return string.Empty;// 超过一帧暂不处理 
            }

            bool mask_flag = (recBytes [1] & 0x80) == 0x80; // 是否包含掩码  
            if (!mask_flag) {
                return string.Empty;// 不包含掩码的暂不处理
            }

            int payload_len = recBytes [1] & 0x7F; // 数据长度  

            byte[] masks = new byte[4];
            byte[] payload_data;

            if (payload_len == 126) {
                Array.Copy (recBytes, 4, masks, 0, 4);
                payload_len = (UInt16)(recBytes [2] << 8 | recBytes [3]);
                payload_data = new byte[payload_len];
                Array.Copy (recBytes, 8, payload_data, 0, payload_len);

            } else if (payload_len == 127) {
                Array.Copy (recBytes, 10, masks, 0, 4);
                byte[] uInt64Bytes = new byte[8];
                for (int i = 0; i < 8; i++) {
                    uInt64Bytes [i] = recBytes [9 - i];
                }
                UInt64 len = BitConverter.ToUInt64 (uInt64Bytes, 0);

                payload_data = new byte[len];
                for (UInt64 i = 0; i < len; i++) {
                    payload_data [i] = recBytes [i + 14];
                }
            } else {
                Array.Copy (recBytes, 2, masks, 0, 4);
                payload_data = new byte[payload_len];
                Array.Copy (recBytes, 6, payload_data, 0, payload_len);

            }

            for (var i = 0; i < payload_len; i++) {
                payload_data [i] = (byte)(payload_data [i] ^ masks [i % 4]);
            }

            return Encoding.UTF8.GetString (payload_data);
        }
复制代码

 

复制代码
/// <summary>
        /// 把客户端消息打包处理(拼接上谁什么时候发的什么消息)
        /// </summary>
        /// <returns>The data.</returns>
        /// <param name="message">Message.</param>
        private byte[] PackageServerData (SocketMessage sm)
        {
            StringBuilder msg = new StringBuilder ();
            if (!sm.isLoginMessage) { //消息是login信息
                msg.AppendFormat ("{0} @ {1}:\r\n    ", sm.Client.Name, sm.Time.ToShortTimeString ());
                msg.Append (sm.Message);
            } else { //处理普通消息
                msg.AppendFormat ("{0} login @ {1}", sm.Client.Name, sm.Time.ToShortTimeString ());
            }


            byte[] content = null;
            byte[] temp = Encoding.UTF8.GetBytes (msg.ToString ());

            if (temp.Length < 126) {
                content = new byte[temp.Length + 2];
                content [0] = 0x81;
                content [1] = (byte)temp.Length;
                Array.Copy (temp, 0, content, 2, temp.Length);
            } else if (temp.Length < 0xFFFF) {
                content = new byte[temp.Length + 4];
                content [0] = 0x81;
                content [1] = 126;
                content [2] = (byte)(temp.Length & 0xFF);
                content [3] = (byte)(temp.Length >> 8 & 0xFF);
                Array.Copy (temp, 0, content, 4, temp.Length);
            } else {
                // 暂不处理超长内容  
            }

            return content;
        }
复制代码

 

如果看不懂我在写什么,并且对C#版websocket感兴趣的同学需要看一下我上一篇博客了

有图有真相

看看我们写的聊天室运行效果吧

 服务器

 客户端——Byron

客户端——Casper

源代码

源代码我已经上传到了我的github上,对github不感冒的同学可以直接点击这里下载,有兴趣的同学可以下载看看,因为我在Mac+mono+Xamarin Studio上开发的(忽然感觉自己好极品在Mac上用C#。。。),所以代码编码和Windows可能会有些差距,改成Windows兼容的应该就可以直接运行了,不过没在Window下跑过,如果有错误还请大家多多指正。

C#版Websocket实例 - Seaurl - 博客园

mikel阅读(290)

来源: C#版Websocket实例 – Seaurl – 博客园

Demo地址:www.awbeci.xyz

websocket有java、nodejs、python,Php等等版本,我使用的是C#版本,服务器端是Fleck,github地址:https://github.com/statianzo/Fleck

这篇博客就是引用上面的一个例子教你如何使用客户端和服务器端来使用websocket的,对于英文还不错的同学,直接看上面 的源代码就可以了,下面开始讲解如何使用:

在说之前我们先来看看哪些浏览器支持websocket:

WebSocket客户端支持
浏览器 支持情况
Chrome Chrome version 4+支持
Firefox Firefox version 5+支持
IE IE version 10+支持
Safari IOS 5+支持
Android Brower Android 4.5+支持
WebSocket 服务端支持
厂商 应用服务器 备注
IBM WebSphere WebSphere 8.0 以上版本支持,7.X 之前版本结合 MQTT 支持类似的 HTTP 长连接
甲骨文 WebLogic WebLogic 12c 支持,11g 及 10g 版本通过 HTTP Publish 支持类似的 HTTP 长连接
微软 IIS IIS 7.0+支持
Apache Tomcat Tomcat 7.0.5+支持,7.0.2X 及 7.0.3X 通过自定义 API 支持
Jetty Jetty 7.0+支持

 

现在我们看看浏览器端如何实现:

复制代码
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
    <title>websocket client</title>
    <script type="text/javascript">
        var start = function () {
            var inc = document.getElementById('incomming');
            var wsImpl = window.WebSocket || window.MozWebSocket;
            var form = document.getElementById('sendForm');
            var input = document.getElementById('sendText');

            inc.innerHTML += "connecting to server ..<br/>";

            // create a new websocket and connect
            window.ws = new wsImpl('ws://localhost:7181/');

            // when data is comming from the server, this metod is called
            ws.onmessage = function (evt) {
                inc.innerHTML += evt.data + '<br/>';
            };

            // when the connection is established, this method is called
            ws.onopen = function () {
                inc.innerHTML += '.. connection open<br/>';
            };

            // when the connection is closed, this method is called
            ws.onclose = function () {
                inc.innerHTML += '.. connection closed<br/>';
            }

            form.addEventListener('submit', function (e) {
                e.preventDefault();
                var val = input.value;
                ws.send(val);
                input.value = "";
            });

        }
        window.onload = start;
    </script>
</head>
<body>
    <form id="sendForm">
        <input id="sendText" placeholder="Text to send" />
    </form>
    <pre id="incomming"></pre>
</body>
</html>
复制代码

服务器端我使用的是C#的控制台程序:

复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Fleck;

namespace TopoWebSocket
{
    class Program
    {
        static void Main(string[] args)
        {
            FleckLog.Level = LogLevel.Debug;
            var allSockets = new List<IWebSocketConnection>();
            var server = new WebSocketServer("ws://0.0.0.0:7181");
            server.Start(socket =>
            {
                socket.OnOpen = () =>
                {
                    Console.WriteLine("Open!");
                    allSockets.Add(socket);
                };
                socket.OnClose = () =>
                {
                    Console.WriteLine("Close!");
                    allSockets.Remove(socket);
                };
                socket.OnMessage = message =>
                {
                    Console.WriteLine(message);
                    allSockets.ToList().ForEach(s => s.Send("Echo: " + message));
                };
            });


            var input = Console.ReadLine();
            while (input != "exit")
            {
                foreach (var socket in allSockets.ToList())
                {
                    socket.Send(input);
                }
                input = Console.ReadLine();
            }
        }
    }
}
复制代码

注意要引用:using Fleck;这个在上面的链接里面已经提供了。代码都写好后,我们来运行下服务器端吧,客户端也可以,总之两个都运行好:如图:

现在你可以客户端向服务器端发送消息了,我们试试,输入你好世界:

然后我们再在服务器端发送消息给浏览器试试,如,HelloWorld

也成功了,呵呵,是不是很神奇,现在可以添加自己 的业务逻辑在里面了,是不是 觉得前后台通信变得简单了。

总结:

1、这个例子不是我写的,我只不过在github上拿过来给你们介绍,你们可以通过上面的例子fork下源代码,自己看看分析分析别人是怎么用的

2、如果有问题可以加群问我:464696550

3、参考资料:http://www.ibm.com/developerworks/cn/java/j-lo-WebSocket/

一文读懂什么是iPaaS - 知乎

mikel阅读(390)

来源: 一文读懂什么是iPaaS – 知乎

#什么是iPaaS?

iPaaS是Intergration Platform as a Service(集成平台即服务)的简写。Gartner将其定义为“促进开发、执行和集成流治理同任何本地以及基于云的流程、服务、应用和数据连接的一套云服务,可以在独立的或者多个交叉的组织中进行”。简而言之,iPaaS是一种基于云的自助服务解决方案,将应用程序的集成方式标准化。从而使开发人员、技术顾问甚至非技术人员都可以使用iPaaS快速构建集成流程,实现企业内数据互联互通

#企业集成方式的演变

伴随着企业信息化程度的提升,导致集成方式的不断变化,最终演变出了iPaaS。起初,企业通过自开发或采购第三方系统实现公司相关业务业务。然而各系统彼此孤立,用途单一,如同一根根独立的烟囱,无法联通。而后,企业通过自定义编程去实现点对点的应用打通。这种方式简单有效,但随着应用系统的增多,点对点的集成会形成蛛网般的布局,从而导致运维压力剧增,且集成能力无法复用。由此,集成方式便走向了ESB(企业服务总线)。各业务应用系统通过ESB提供的适配器,实现内部数据的服务化进行集成,不仅解决了“烟囱”式系统之间的数据孤岛问题,同时还能够集中企业所有的服务资产,在统一的平台中进行管理和监控,大幅提高了服务的共享和复用,避免了点对点集成方式中由于系统之间的集成情况不清晰,导致重复投入集成资源“重复造轮子”的事情发生,降低了集成成本的同时,基于ESB提供的开箱即用的应用适配器,也提高了集成服务的开发效率,一定程度上解决了企业集成运维的压力;随着微服务技术和云应用的推广,企业数字化进程中,系统架构和环境在不断演变,传统的内部网路环境应用系统之间的集成基本是能够通过ESB满足,但是企业网络边界不断在扩大,比如云上云下集成、混合云集成、多工厂/多基地集成等,传统ESB采用的集中式的部署架构则已经无法满足了。为了满足企业多基地多工厂的数据联通要求,集成方式又演化出分布式集成。分布式集成基于企业内网,实现跨集团与分子公司间对接。然而这种方式,也会产生较高的安全风险,给IT团队带来巨大的管理成本。时至今日,随着微服务架构以及容器技术的推出,由一系列基于微服务架构的轻量级API集成组件、数据集成组件、消息集成组件、业务系统链接器等组成的iPaaS平台便应运而生。

#iPaaS的工作原理

iPaaS为企业中所有应用程序之间的数据集成提供了一套工具和统一的流程,无论这些数据存储在本地还是云端,企业仅需要选择所需的工具和服务进行可视化的配置即可。iPaaS作为一种云计算技术,主要由平台服务、工具和服务三部分组成,可以让企业轻松地在云中部署、运行和管理应用程序集成解决方案。

#iPaaS的优点

使用iPaaS有很多好处,包括:

1.开箱即用:

运营&开发人员可以自助使用iPaaS,通过可视化的编排页面,快速实现不同应用程序的集成。iPaaS提供省时省力的的智能工具,帮助用户在短时间内完成丰富的集成场景,大大提高了企业IT部门的生产力。

可视化流程编排

2.高扩展性:

iPaaS可以根据企业的集成需求的增长,轻松实现扩展。iPaaS支持企业自定义应用服务连接,帮助企业实现应用系统的快速接入。

技术与应用连接器

3.更高的灵活性:

iPaaS平台使得企业应用集成具有了更高的灵活性,可以快速适应企业不断变化的业务需求。从需求提出到开发完成,测试上线,原先以日为单位的开发周期缩短至以小时或分钟为单位,IT部门可以根据业务的调整,随时随地调整应用间的集成方式。

API在线调试
API一键发布

4.更高效的运维监控:

iPaaS通常除了提供应用集成相关服务外,也会提供相应的运维服务,例如接口预警、接口日志等基础功能,可以帮助运维人员随时了解集成情况,出问题后也可以快速定位并予以解决。

调用日志
预警策略

5.更高的安全性:

iPaaS可以通过提供对数据源和数据仓库访问的集中控制,搭配网关服务,可以针对集成接口进行认证、流量、频次、加密等限制,来帮助IT部门保护数据。

API策略
网关插件

6.更低的集成成本:

iPaaS解决方案通常比自定义集成、基于消息集成和企业集成项目(如ESB的实施)便宜得多。大多采用订阅式的服务,从而使得采购成本大幅降低,使得越来越多的小型企业进行应用集成成为可能。

#iPaaS产品如何选择

企业选择iPaaS产品时,需要考虑以下因素:

  • 易用性:该iPaaS是否易于使用,提供的应用连接器是否丰富,是否使用低代码平台或无代码方式,从而无需专业技术人员便可以实现应用集成。
  • 安全性:该iPaaS平台是否支持对数据访问进行集中控制,提供数据加密、身份认证等一系列网关插件,从而有效保护企业数据资产。
  • 稳定性:iPaaS底层架构是否高可用,能否支持企业所需数量级的数据请求,是否具有完善的容灾备份机制。从而防止影响企业正常生产运营。
  • 需要的功能:企业需要明确自己所必需的功能,如数据转换、工作流自动化或SaaS应用接入等功能是否支持。
  • 预算和运维支持:企业需要根据自己的预算,选择合适的iPaaS平台于付费模式,与此同时,需要考察iPaaS供应商提供的运维支持服务,是否包含技术支持、使用培训、协助推广和业务咨询等。

#得帆云DeFusion iPaaS融合集成平台

得帆云DeFusion iPaaS融合集成平台是融合了企业集成常用的数据集成ETL、应用集成ESB、能力开放API三个核心引擎为一体的企业级集成平台,基于分布式架构和云原生特性,DeFusion iPaaS融合集成平台可以帮助企业轻松实现云应用的集成打通,在其高安全的统一集成能力保障下,让企业无需担心不同环境下网络连通的安全风险,通过得帆云DeFusion iPaaS融合集成平台提供的丰富且可以自定义扩展的应用连接器,简单快速且高效地实现混合环境应用之间的数据共享和功能集成。

得帆云DeFusion iPaaS融合集成平台对企业集成API的资产汇总、全生命周期管理、监控预警通知、API门户等企业集成管理必须的功能也做了增强,全面提高企业集成管理能力;再结合得帆丰富的集成实施经验和实施能力,为企业提供产品+实施的全方位集成解决方案。得帆云DeFusion iPaaS融合集成平台产品旨在为企业重新定义集成,降低企业集成成本,提升集成效率,在企业面临强大内外部压力下而不得不支撑快速变化的前端需求时,为企业构建底层支撑能力。

(1)海量系统协议接入:得帆云DeFusion iPaaS融合集成平台提供300+的应用系统、技术协议层面的连接器,为企业提供各类业务系统、各类底层协议的连接支撑。连接器可以通过Java+图形化方式进行快速扩展,方便企业根据自身需求进行定制,全面提升企业对接能力,从应用(Application)、企业(Business)、云(Cloud)、设备(Device)几个领域,将企业需要的能力集中接入,集中管理。

(2)云应用集成:随着企业系统上云、各类SaaS应用的使用,企业的网络环境变得多样化,得帆云DeFusion iPaaS融合平台能够让企业轻松应用云上环境、云下本地环境、私有化环境、同城异地机房等环境的下应用的集成,同时提供了各类SaaS原生的连接器,实现SaaS应用之间、以及SaaS与本地应用系统之间的集成,并有效保障不同云应用之间的集成数据一致性。

(3)API资产全面管理:得帆云DeFusion iPaaS融合平台致力于承载企业全域集成API管理,对于不是通过平台运行的API类型,如:EDI、其他企业级ESB平台API等,也可以将API资产接入,实现资产的大融合。通过平台的API信息管理、分类、标签等能力,全面维护API资产,同时也可以通过报表工具展示企业全量的系统集成关系,方便企业API资产的留存和关系的梳理。

(4)运行过程深度监控:所有在平台中运行的API ,都经过平台的全面深度监控 ,对于API运行的步骤、传输时间、报文大小、报文详情等均可以进行监控查看,为企业提供一站式仲裁平台,杜绝系统对接时的“推诿“行为,明确指出集成问题所在,快速定位,提升IT效率。同时,平台也提供预警通知功能,对于API、平台、系统层级的问题,及时给出全渠道的预警或通知,进一步提升集成的稳定性和对接效率。

(5)企业能力开放支撑:平台提供完整的企业级API网关能力,以支撑企业的能力开放,对底层业务系统提供功能和数据的API化;对数据中台、数据仓库以及其他数据的汇聚中心,平台也提供数据即服务(DaaS)功能,将数据模型、数据对象快速生成API。API接入API网关后,可以基于网关进行API的限制和保护,提供安全验证、鉴权、限流、熔断等相关管理功能。

(6)清晰便捷API门户:得帆云DeFusion iPaaS融合平台为企业提供强大的API门户功能,对接入平台的API,可以开放在API门户中,通过API门户实现对API的外网开放。企业可以基于API门户,实现对API的开放同时,也实现对API的权限管控(流程化审批授权),计数计费,文档发布等管理和监控。全面降低企业与合作伙伴、C端用户的对接成本,提升对接效率。

 

 

docker入门加实战—Docker镜像和Dockerfile语法 - 随机的未知 - 博客园

mikel阅读(300)

来源: docker入门加实战—Docker镜像和Dockerfile语法 – 随机的未知 – 博客园

镜像

镜像就是包含了应用程序、程序运行的系统函数库、运行配置等文件的文件包。构建镜像的过程其实就是把上述文件打包的过程。

镜像结构

我们要从0部署一个Java应用,大概流程是这样:

  1. 准备Linux运行环境(java项目并不需要完整的操作系统,仅仅是基础运行环境即可)
  2. 安装并配置JDK
  3. 拷贝jar包
  4. 配置启动脚本

镜像文件不是随意堆放的,而是按照操作的步骤分层叠加而成,每一层形成的文件都会单独打包并标记一个唯一id,称为Layer)。这样,如果我们构建时用到的某些层其他人已经制作过,就可以直接拷贝使用这些层,而不用重复制作。

例如,第一步中需要的Linux运行环境,通用性就很强,所以Docker官方就制作了这样的只包含Linux运行环境的镜像。我们在制作java镜像时,就无需重复制作,直接使用Docker官方提供的CentOS或Ubuntu镜像作为基础镜像。然后再搭建其它层即可,这样逐层搭建,最终整个Java项目的镜像结构如图所示:

Java项目镜像结构

Dockerfile

由于制作镜像的过程中,需要逐层处理和打包,比较复杂,所以Docker就提供了自动打包镜像的功能。我们只需要将打包的过程,每一层要做的事情用固定的语法写下来,交给Docker去执行即可。

而这种记录镜像结构的文件就称为Dockerfile,其对应的语法可以参考官方文档:

https://docs.docker.com/engine/reference/builder/

其中的语法比较多,比较常用的有:

指令 说明 示例
FROM 指定基础镜像 FROM centos:7
ENV 设置环境变量,可在后面指令使用 ENV key value
COPY 拷贝本地文件到镜像的指定目录 COPY ./xx.jar /tmp/app.jar
RUN 执行Linux的shell命令,一般是安装过程的命令 RUN yum install gcc
EXPOSE 指定容器运行时监听的端口,是给镜像使用者看的;
但是后面还是需要-p来做端口映射的
EXPOSE 8080
ENTRYPOINT 镜像中应用的启动命令,容器运行时调用 ENTRYPOINT java -jar xx.jar

例如,要基于Ubuntu镜像来构建一个Java应用,其Dockerfile内容如下:

# 指定基础镜像
FROM ubuntu:16.04
# 配置环境变量,JDK的安装目录、容器内时区
ENV JAVA_DIR=/usr/local
ENV TZ=Asia/Shanghai
# 拷贝jdk和java项目的包
COPY ./jdk8.tar.gz $JAVA_DIR/
COPY ./docker-demo.jar /tmp/app.jar
# 设定时区
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# 安装JDK
RUN cd $JAVA_DIR \
 && tar -xf ./jdk8.tar.gz \
 && mv ./jdk1.8.0_144 ./java8
# 配置环境变量
ENV JAVA_HOME=$JAVA_DIR/java8
ENV PATH=$PATH:$JAVA_HOME/bin
# 指定项目监听的端口
EXPOSE 8080
# 入口,java项目的启动命令
ENTRYPOINT ["java", "-jar", "/app.jar"]

以后我们会有很多java项目需要打包为镜像,它们都需要Linux系统环境、JDK环境这两层,只有上面的3层不同(因为jar包不同)。如果每次制作java镜像都重复制作前两层镜像,是很麻烦的。

所以,就有人提供了基础的系统加JDK环境,我们在此基础上制作java镜像,就可以省去JDK的配置了:

FROM openjdk:11.0-jre-buster
# 设定时区
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# 拷贝jar包
COPY docker-demo.jar /app.jar
# 入口
ENTRYPOINT ["java", "-jar", "/app.jar"]

构建镜像

在/root/demo目录下有如下两个文件

目录文件

其中docker-demo.jar是Java项目;Dockerfile如下:

# 基础镜像
FROM openjdk:11.0-jre-buster
# 设定时区
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# 拷贝jar包
COPY docker-demo.jar /app.jar
# 入口
ENTRYPOINT ["java", "-jar", "/app.jar"]

然后,执行命令,构建镜像:

# 进入镜像目录
cd /root/demo
# 开始构建
docker build -t docker-demo:1.0 .
  • docker build: 就是构建一个docker镜像
  • -t docker-demo:1.0 :-t参数是指定镜像的名称(repositorytag
  • . : 最后的点是指构建时Dockerfile所在路径,由于我们进入了demo目录,所以指定的是.代表当前目录,也可以直接指定Dockerfile目录:
    • # 直接指定Dockerfile目录
      docker build -t docker-demo:1.0 /root/demo
      

如果没有指定Dockerfile的名字,就必须叫Dockerfile。

运行结果如下:

运行结果

查看镜像

查看镜像

可以看到多了一个docker-demo的镜像;

尝试运行:

docker run -d --name dd -p 8080:8080 docker-demo:1.0

运行结果

.Net核心级的性能优化(GC篇) - 江湖评谈 - 博客园

mikel阅读(315)

来源: .Net核心级的性能优化(GC篇) – 江湖评谈 – 博客园

1.前言

大部分人对于.Net性能优化,都停留在业务层面。或者简单的.Net框架配置层面。本篇来看下.Net核心部分GC垃圾回收配置:保留VM,大对象,独立GC,节省内存等.Net8里面有很多的各种GC配置,用以帮助你的程序进行最大程度性能提升和优化。

文章分为两部分,第一个是GC有哪些动作可以性能最大的优化,第二部分就是如何配置这些动作以便让你的程序达到这些性能效果。

2.GC动作

1.节省内存:System.GC.ConserveMemory(默认值为0)

2.独立GC:DOTNET_GCName(默认值,coreclr.dll的默认GC)

3.大型对象堆阈值DOTNET_GCLOHThreshold(默认值85000字节,指定值必须大于其)

4.允许大型对象DOTNET_gcAllowVeryLargeObjects(默认垃圾回收大于2GB的数组,也就是默认值为1)

5.大型页面DOTNET_GCLargePages,指示堆硬限制是否使用大型页面 DOTNET_GCLargePages(默认值为0,也即是不使用)

6.保留VM,DOTNET_GCRetainVM。意思是把一个经过垃圾回收,没有对象的段是被系统回收还是放在堆段列表,以便下次使用.(默认值false,被系统回收)

7.高内存百分比DOTNET_GCHighMemPercent,当物理内存负载大于其指定的值的时候,进行完整的垃圾回收,以便腾出跟多空间(默认负载阈值介于90%到 97%之间)

8.对象堆限制百分比

DOTNET_GCHeapHardLimitSOHPercent,DOTNET_GCHeapHardLimitLOHPercent,DOTNET_GCHeapHardLimitPOHPercent.分别为小对象堆,大对象堆,固定堆的限制百分比。

9.对象堆限制

DOTNET_GCHeapHardLimitSOH,DOTNET_GCHeapHardLimitLOH,DOTNET_GCHeapHardLimitPOH可以根据每个对象堆指定 GC 的允许堆使用量

10.堆限制百分比DOTNET_GCHeapHardLimitPercent默认值(仅在某些情况下适用)是20MB或容器内存限制的75%(以较大者为准)

11.堆限制DOTNET_GCHeapHardLimit默认值(仅在某些情况下适用)是20MB或容器内存限制的75%(以较大者为准)

12.关联DOTNET_GCNoAffinitize指定是否将垃圾回收线程与处理器关联。若要关联一个 GC 线程,则意味着它只能在其特定的 CPU 上运行。为每个 GC 线程创建一个堆。

13.CPU组DOTNET_GCCpuGroup,配置垃圾回收器是否使用CPU组。(默认值为0,表示不会跨CPU组)

14.关联范围DOTNET_GCHeapAffinitizeRanges,指定用于垃圾回收器线程的处理器列表。以逗号分隔的处理器编号列表或处理器编号范围。
Unix 示例:“1-10,12,50-52,70”
Windows 示例:“0:1-10,0:12,1:50-52,1:70”

15.关联掩码DOTNET_GCHeapAffinitizeMask,指定垃圾回收器线程应使用的确切处理器数。

16.堆计数DOTNET_GCHeapCount限制通过垃圾回收器创建的堆数。

3.配置

配置这些GC设置,主要是可以从三个方面来配置。
其一:运行时配置文件runtimeconfig.json
以保留VM为例:

{
   "runtimeOptions": {
      "configProperties": {
         "System.GC.RetainVM": true
      }
   }
}

其二:MSbuild配置
以保留VM为例:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <RetainVMGarbageCollection>true</RetainVMGarbageCollection>
  </PropertyGroup>
</Project>

其三:环境变量配置

Windows CMD:
set DOTNET_GCRetainVM=true

Windows Powershell:
$env:DOTNET_GCRetainVM=true

Unix/Linux
export DOTNET_GCRetainVM=true
BASH 复制 全屏

以上16个GC动作均可以通过这个三个配置方式来进行配置。

参考微软官网:

https://learn.microsoft.com/en-us/dotnet/core/runtime-config/garbage-collector

c# - 目录不存在 - 参数名称 : directoryVirtualPath - IT工具网

mikel阅读(288)

来源: c# – 目录不存在 – 参数名称 : directoryVirtualPath – IT工具网

我正在使用 Visual Studio Express 2012 RC。

  • 如果创建一个空白的“hello world MVC 4.5 项目”
  • 我将它降级到 4.0,以便它与我的主机 (Arvixe) 兼容
  • 我将它发布给主机。

然后我收到此错误消息,我可以在网上找到有关它的任何信息。

Server Error in '/' Application.

Directory does not exist.
Parameter name: directoryVirtualPath

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 

Exception Details: System.ArgumentException: Directory does not exist.
Parameter name: directoryVirtualPath

Source Error: 

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Stack Trace: 


[ArgumentException: Directory does not exist.
Parameter name: directoryVirtualPath]
   System.Web.Optimization.Bundle.IncludeDirectory(String directoryVirtualPath, String searchPattern, Boolean searchSubdirectories) +357
   System.Web.Optimization.Bundle.Include(String[] virtualPaths) +287
   IconBench.BundleConfig.RegisterBundles(BundleCollection bundles) +75
   IconBench.MvcApplication.Application_Start() +128

[HttpException (0x80004005): Directory does not exist.
Parameter name: directoryVirtualPath]
   System.Web.HttpApplicationFactory.EnsureAppStartCalledForIntegratedMode(HttpContext context, HttpApplication app) +9160125
   System.Web.HttpApplication.RegisterEventSubscriptionsWithIIS(IntPtr appContext, HttpContext context, MethodInfo[] handlers) +131
   System.Web.HttpApplication.InitSpecial(HttpApplicationState state, MethodInfo[] handlers, IntPtr appContext, HttpContext context) +194
   System.Web.HttpApplicationFactory.GetSpecialApplicationInstance(IntPtr appContext, HttpContext context) +339
   System.Web.Hosting.PipelineRuntime.InitializeApplication(IntPtr appContext) +253

[HttpException (0x80004005): Directory does not exist.
Parameter name: directoryVirtualPath]
   System.Web.HttpRuntime.FirstRequestInit(HttpContext context) +9079228
   System.Web.HttpRuntime.EnsureFirstRequestInit(HttpContext context) +97
   System.Web.HttpRuntime.ProcessRequestNotificationPrivate(IIS7WorkerRequest wr, HttpContext context) +256

Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.0.30319.237

这是什么意思?

按要求编码^^

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return Content("Hello world");
    }
}

那是我添加的唯一代码。

application_start代码

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();

        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
    }
}

路由配置

public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );

        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        );
    }
}

过滤配置

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute());
    }
}

捆绑配置

 public class BundleConfig
{
    public static void RegisterBundles(BundleCollection bundles)
    {
        bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
                    "~/Scripts/jquery-1.*"));

        bundles.Add(new ScriptBundle("~/bundles/jqueryui").Include(
                    "~/Scripts/jquery-ui*"));

        bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
                    "~/Scripts/jquery.unobtrusive*",
                    "~/Scripts/jquery.validate*"));

        bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(
                    "~/Scripts/modernizr-*"));

        bundles.Add(new StyleBundle("~/Content/css").Include("~/Content/site.css"));

        bundles.Add(new StyleBundle("~/Content/themes/base/css").Include(
                    "~/Content/themes/base/jquery.ui.core.css",
                    "~/Content/themes/base/jquery.ui.resizable.css",
                    "~/Content/themes/base/jquery.ui.selectable.css",
                    "~/Content/themes/base/jquery.ui.accordion.css",
                    "~/Content/themes/base/jquery.ui.autocomplete.css",
                    "~/Content/themes/base/jquery.ui.button.css",
                    "~/Content/themes/base/jquery.ui.dialog.css",
                    "~/Content/themes/base/jquery.ui.slider.css",
                    "~/Content/themes/base/jquery.ui.tabs.css",
                    "~/Content/themes/base/jquery.ui.datepicker.css",
                    "~/Content/themes/base/jquery.ui.progressbar.css",
                    "~/Content/themes/base/jquery.ui.theme.css"));
    }
}

 

最佳答案

 

似乎这个错误源于“不包括您应用程序目录中的所需文件”或将您的代码上传到未配置为虚拟目录的目录中, 甚至在不正确的目录中。

 

关于C# – 目录不存在 – 参数名称 : directoryVirtualPath,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11333137/

localStorage使用总结_读取localstorage时按照写入顺序全部读出-CSDN博客

mikel阅读(357)

来源: localStorage使用总结_读取localstorage时按照写入顺序全部读出-CSDN博客

localStorage使用总结

一、什么是localStorage、sessionStorage

HTML5中,新加入了一个localStorage特性,这个特性主要是用来作为本地存储来使用的,解决了cookie存储空间不足的问题(cookie中每条cookie的存储空间为4k),localStorage中一般浏览器支持的是5M大小,这个在不同的浏览器中localStorage会有所不同。

 

二、localStorage的优势与局限

localStorage的优势

1、localStorage拓展了cookie的4K限制

2、localStorage会可以将第一次请求的数据直接存储到本地,这个相当于一个5M大小的针对于前端页面的数据库,相比于cookie可以节约带宽,但是这个却是只有在高版本的浏览器中才支持的

localStorage的局限

1、浏览器的大小不统一,并且在IE8以上的IE版本才支持localStorage这个属性

2、目前所有的浏览器中都会把localStorage的值类型限定为string类型,这个在对我们日常比较常见的JSON对象类型需要一些转换

3、localStorage在浏览器的隐私模式下面是不可读取的

4、localStorage本质上是对字符串的读取,如果存储内容多的话会消耗内存空间,会导致页面变卡

5、localStorage不能被爬虫抓取到

localStorage与sessionStorage的唯一一点区别就是localStorage属于永久性存储,而sessionStorage属于当会话结束的时候,sessionStorage中的键值对会被清空

这里我们以localStorage来分析

 

三、localStorage的使用

localStorage的浏览器支持情况:

这里要特别声明一下,如果是使用IE浏览器的话,那么就要UserData来作为存储,这里主要讲解的是localStorage的内容,所以userData不做过多的解释,而且以博主个人的看法,也是没有必要去学习UserData的使用来的,因为目前的IE6/IE7属于淘汰的位置上,而且在如今的很多页面开发都会涉及到HTML5\CSS3等新兴的技术,所以在使用上面一般我们不会去对其进行兼容

首先在使用localStorage的时候,我们需要判断浏览器是否支持localStorage这个属性

if(!window.localStorage){
            alert("浏览器支持localstorage");
            return false;
        }else{
            //主逻辑业务
        }

 

localStorage的写入,localStorage的写入有三种方法,这里就一一介绍一下

if(!window.localStorage){
            alert("浏览器支持localstorage");
            return false;
        }else{
            var storage=window.localStorage;
            //写入a字段
            storage["a"]=1;
            //写入b字段
            storage.a=1;
            //写入c字段
            storage.setItem("c",3);
            console.log(typeof storage["a"]);
            console.log(typeof storage["b"]);
            console.log(typeof storage["c"]);
        }

 

运行后的结果如下:

这里要特别说明一下localStorage的使用也是遵循同源策略的,所以不同的网站直接是不能共用相同的localStorage

最后在控制台上面打印出来的结果是:

不知道各位读者有没有注意到,刚刚存储进去的是int类型,但是打印出来却是string类型,这个与localStorage本身的特点有关,localStorage只支持string类型的存储。

localStorage的读取

if(!window.localStorage){
            alert("浏览器支持localstorage");
        }else{
            var storage=window.localStorage;
            //写入a字段
            storage["a"]=1;
            //写入b字段
            storage.a=1;
            //写入c字段
            storage.setItem("c",3);
            console.log(typeof storage["a"]);
            console.log(typeof storage["b"]);
            console.log(typeof storage["c"]);
            //第一种方法读取
            var a=storage.a;
            console.log(a);
            //第二种方法读取
            var b=storage["b"];
            console.log(b);
            //第三种方法读取
            var c=storage.getItem("c");
            console.log(c);
        }

 

这里面是三种对localStorage的读取,其中官方推荐的是getItem\setItem这两种方法对其进行存取,不要问我这个为什么,因为这个我也不知道

我之前说过localStorage就是相当于一个前端的数据库的东西,数据库主要是增删查改这四个步骤,这里的读取和写入就相当于增、查的这两个步骤

下面我们就来说一说localStorage的删、改这两个步骤

改这个步骤比较好理解,思路跟重新更改全局变量的值一样,这里我们就以一个为例来简单的说明一下

 

if(!window.localStorage){
            alert("浏览器支持localstorage");
        }else{
            var storage=window.localStorage;
            //写入a字段
            storage["a"]=1;
            //写入b字段
            storage.b=1;
            //写入c字段
            storage.setItem("c",3);
            console.log(storage.a);
            // console.log(typeof storage["a"]);
            // console.log(typeof storage["b"]);
            // console.log(typeof storage["c"]);
            /*分割线*/
            storage.a=4;
            console.log(storage.a);
        }

这个在控制台上面我们就可以看到已经a键已经被更改为4了

localStorage的删除

1、将localStorage的所有内容清除

var storage=window.localStorage;
            storage.a=1;
            storage.setItem("c",3);
            console.log(storage);
            storage.clear();
            console.log(storage);

 

 

2、 将localStorage中的某个键值对删除

 

var storage=window.localStorage;
            storage.a=1;
            storage.setItem("c",3);
            console.log(storage);
            storage.removeItem("a");
            console.log(storage.a);

 

控制台查看结果

localStorage的键获取

var storage=window.localStorage;
            storage.a=1;
            storage.setItem("c",3);
            for(var i=0;i<storage.length;i++){
                var key=storage.key(i);
                console.log(key);
            }

 

使用key()方法,向其中出入索引即可获取对应的键

 

四、localStorage其他注意事项

一般我们会将JSON存入localStorage中,但是在localStorage会自动将localStorage转换成为字符串形式

这个时候我们可以使用JSON.stringify()这个方法,来将JSON转换成为JSON字符串

示例:

if(!window.localStorage){
            alert("浏览器支持localstorage");
        }else{
            var storage=window.localStorage;
            var data={
                name:'xiecanyong',
                sex:'man',
                hobby:'program'
            };
            var d=JSON.stringify(data);
            storage.setItem("data",d);
            console.log(storage.data);
        }

 

读取之后要将JSON字符串转换成为JSON对象,使用JSON.parse()方法

var storage=window.localStorage;
            var data={
                name:'xiecanyong',
                sex:'man',
                hobby:'program'
            };
            var d=JSON.stringify(data);
            storage.setItem("data",d);
            //将JSON字符串转换成为JSON对象输出
            var json=storage.getItem("data");
            var jsonObj=JSON.parse(json);
            console.log(typeof jsonObj);

打印出来是Object对象

另外还有一点要注意的是,其他类型读取出来也要进行转换

解决"此请求已被阻止,因为当用在 GET 请求中时,会将敏感信息透漏给第三方网站"的问题 - 五粮液和 - 博客园

mikel阅读(380)

来源: 解决”此请求已被阻止,因为当用在 GET 请求中时,会将敏感信息透漏给第三方网站”的问题 – 五粮液和 – 博客园

ASP.NET MVC项目中,使用AJAX向控制器发送GET请求获取JSON数据时,出现这个错误:”此请求已被阻止,因为当用在 GET 请求中时,会将敏感信息透漏给第三方网站。若要允许 GET 请求,请将 JsonRequestBehavior 设置为 AllowGet”。

错误截图:此请求已被阻止,因为当用在 GET 请求中时,会将敏感信息透漏给第三方网站

其实从返回的这个错误信息我们已经可以知道解决方法了,看这个信息:”因为当用在 GET 请求中时,会将敏感信息透漏给第三方网站“,说明我们只要使用POST请求就可以了。后面的 “若要允许 GET 请求,请将 JsonRequestBehavior 设置为 AllowGet”,这是提示第二种解决方法,就是设置JSON结果对象使其允许来自客户端的 HTTP GET 请求。以下为具体解决方法:

方法一 使用POST请求来调用控制器,从而获取JSON数据

原来发送ajax请求的前台JS代码如下:

    /*可以看到type 设置的是GET请求*/
    $.ajax({
        type:'GET',
        url: '/Home/AjaxGetJsonData',            
        success: function (data) {
            alert(data);
        },
        error: function (error) {                
            alert(error.responseText);
        }
    });

或者

    $.get('/Home/AjaxGetJsonData', null, function (data) {
        alert(data);
    });

那么我们只要将代码更改为下面两种任意一种就可以了:

    /*这里更改了ajax的参数 type 为POST ,发送POST请求就不会报错了*/
    $.ajax({
        type: 'POST',
        url: '/Home/AjaxGetJsonData',
        success: function (data) {
            alert(data);
        },
        error: function (error) {
            alert(error.responseText);
        }
    });

或者

    /*也可以直接使用$.post方法进行ajax调用*/
    $.post('/Home/AjaxGetJsonData', null, function (data) {
        alert(data);
    });

方法二 在控制器返回的JSON结果对象里,设置JsonRequestBehavior.AllowGet(允许来自客户端的 HTTP GET 请求)

原来控制器中的代码如下:

    public ActionResult AjaxGetJsonData()
    {
        string strData = "测试数据";
        return Json(strData);   
    }

更改后的代码如下 :

    public ActionResult AjaxGetJsonData()
    {
        string strData = "测试数据";
        //这里我们设置了第二个参数JsonRequestBehavior为AllowGet
        return Json(strData,JsonRequestBehavior.AllowGet);   
    }

我们可以看到在最后return Json(list, JsonRequestBehavior.AllowGet)中增加了第二个参数JsonRequestBehavior.AllowGet,默认是JsonRequestBehavior.DenyGet。之所以我们要在这里设置允许HTTP GET请求,是因为ASP.NET MVC为了预防一个网站信息泄漏的漏洞,所以默认是禁止客户端的HTTP GET 请求的。这是一个很出名的漏洞,名字是:JSON 劫持漏洞,所以我建议AJAX还是使用POST请求来获取数据,防止重要的信息被恶意攻击者窃取。

这里是MSDN文档的具体说明:允许 GET 请求可能会导致用户在某一网站中仍处于已登录状态时访问另一个网站。 这可能会生成导致信息泄漏的安全漏洞。有关此漏洞的信息,请参见 Phil Haack 的博客上的文章 JSON Hijacking,文章是英文的,我已经翻译,点击此链接:JSON劫持漏洞(详细讲解利用JSON从而进行数据劫持的漏洞攻防策略),另外还可以查看这篇文章:JSON劫持漏洞分析和攻防演练