- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章SpringCloud Finchley Gateway 缓存请求Body和Form表单的实现由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
在接入spring-cloud-gateway时,可能有需求进行缓存json-body数据或者form-urlencoded数据的情况.
由于spring-cloud-gateway是以webflux为基础的响应式架构设计,所以在原有zuul基础上迁移过来的过程中,传统的编程思路,并不适合于reactor stream的开发.
网络上有许多缓存案例,但是在测试过程中出现各种bug问题,在缓存body时,需要考虑整体的响应式操作,才能更合理的缓存数据 。
下面提供缓存json-body数据或者form-urlencoded数据的具体实现方案,该方案经测试,满足各方面需求,以及避免了网络上其他缓存方案所出现的问题 。
定义一个gatewaycontext类,用于存储请求中缓存的数据 。
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
|
import
lombok.getter;
import
lombok.setter;
import
lombok.tostring;
import
org.springframework.util.linkedmultivaluemap;
import
org.springframework.util.multivaluemap;
@getter
@setter
@tostring
public
class
gatewaycontext {
public
static
final
string cache_gateway_context =
"cachegatewaycontext"
;
/**
* cache json body
*/
private
string cachebody;
/**
* cache formdata
*/
private
multivaluemap<string, string> formdata;
/**
* cache reqeust path
*/
private
string path;
}
|
实现globalfilter和ordered接口用于缓存请求数据 。
1 . 该示例只支持缓存下面3种mediatype 。
2 . 经验总结
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
|
import
com.choice.cloud.architect.usergate.option.filterorderenum;
import
com.choice.cloud.architect.usergate.support.gatewaycontext;
import
io.netty.buffer.bytebufallocator;
import
lombok.extern.slf4j.slf4j;
import
org.springframework.cloud.gateway.filter.gatewayfilterchain;
import
org.springframework.cloud.gateway.filter.globalfilter;
import
org.springframework.core.ordered;
import
org.springframework.core.io.bytearrayresource;
import
org.springframework.core.io.buffer.databuffer;
import
org.springframework.core.io.buffer.databufferutils;
import
org.springframework.core.io.buffer.nettydatabufferfactory;
import
org.springframework.http.httpheaders;
import
org.springframework.http.mediatype;
import
org.springframework.http.codec.httpmessagereader;
import
org.springframework.http.server.reactive.serverhttprequest;
import
org.springframework.http.server.reactive.serverhttprequestdecorator;
import
org.springframework.util.multivaluemap;
import
org.springframework.web.reactive.function.server.handlerstrategies;
import
org.springframework.web.reactive.function.server.serverrequest;
import
org.springframework.web.server.serverwebexchange;
import
reactor.core.publisher.flux;
import
reactor.core.publisher.mono;
import
java.io.unsupportedencodingexception;
import
java.net.urlencoder;
import
java.nio.charset.charset;
import
java.nio.charset.standardcharsets;
import
java.util.list;
import
java.util.map;
@slf4j
public
class
gatewaycontextfilter
implements
globalfilter, ordered {
/**
* default httpmessagereader
*/
private
static
final
list<httpmessagereader<?>> messagereaders = handlerstrategies.withdefaults().messagereaders();
@override
public
mono<
void
> filter(serverwebexchange exchange, gatewayfilterchain chain) {
/**
* save request path and serviceid into gateway context
*/
serverhttprequest request = exchange.getrequest();
string path = request.getpath().pathwithinapplication().value();
gatewaycontext gatewaycontext =
new
gatewaycontext();
gatewaycontext.getallrequestdata().addall(request.getqueryparams());
gatewaycontext.setpath(path);
/**
* save gateway context into exchange
*/
exchange.getattributes().put(gatewaycontext.cache_gateway_context,gatewaycontext);
httpheaders headers = request.getheaders();
mediatype contenttype = headers.getcontenttype();
long
contentlength = headers.getcontentlength();
if
(contentlength>
0
){
if
(mediatype.application_json.equals(contenttype) || mediatype.application_json_utf8.equals(contenttype)){
return
readbody(exchange, chain,gatewaycontext);
}
if
(mediatype.application_form_urlencoded.equals(contenttype)){
return
readformdata(exchange, chain,gatewaycontext);
}
}
log.debug(
"[gatewaycontext]contenttype:{},gateway context is set with {}"
,contenttype, gatewaycontext);
return
chain.filter(exchange);
}
@override
public
int
getorder() {
return
integer.min_value;
}
/**
* readformdata
* @param exchange
* @param chain
* @return
*/
private
mono<
void
> readformdata(serverwebexchange exchange,gatewayfilterchain chain,gatewaycontext gatewaycontext){
httpheaders headers = exchange.getrequest().getheaders();
return
exchange.getformdata()
.doonnext(multivaluemap -> {
gatewaycontext.setformdata(multivaluemap);
log.debug(
"[gatewaycontext]read formdata:{}"
,multivaluemap);
})
.then(mono.defer(() -> {
charset charset = headers.getcontenttype().getcharset();
charset = charset ==
null
? standardcharsets.utf_8:charset;
string charsetname = charset.name();
multivaluemap<string, string> formdata = gatewaycontext.getformdata();
/**
* formdata is empty just return
*/
if
(
null
== formdata || formdata.isempty()){
return
chain.filter(exchange);
}
stringbuilder formdatabodybuilder =
new
stringbuilder();
string entrykey;
list<string> entryvalue;
try
{
/**
* remove system param ,repackage form data
*/
for
(map.entry<string, list<string>> entry : formdata.entryset()) {
entrykey = entry.getkey();
entryvalue = entry.getvalue();
if
(entryvalue.size() >
1
) {
for
(string value : entryvalue){
formdatabodybuilder.append(entrykey).append(
"="
).append(urlencoder.encode(value, charsetname)).append(
"&"
);
}
}
else
{
formdatabodybuilder.append(entrykey).append(
"="
).append(urlencoder.encode(entryvalue.get(
0
), charsetname)).append(
"&"
);
}
}
}
catch
(unsupportedencodingexception e){
//ignore urlencode exception
}
/**
* substring with the last char '&'
*/
string formdatabodystring =
""
;
if
(formdatabodybuilder.length()>
0
){
formdatabodystring = formdatabodybuilder.substring(
0
, formdatabodybuilder.length() -
1
);
}
/**
* get data bytes
*/
byte
[] bodybytes = formdatabodystring.getbytes(charset);
int
contentlength = bodybytes.length;
serverhttprequestdecorator decorator =
new
serverhttprequestdecorator(
exchange.getrequest()) {
/**
* change content-length
* @return
*/
@override
public
httpheaders getheaders() {
httpheaders httpheaders =
new
httpheaders();
httpheaders.putall(
super
.getheaders());
if
(contentlength >
0
) {
httpheaders.setcontentlength(contentlength);
}
else
{
httpheaders.set(httpheaders.transfer_encoding,
"chunked"
);
}
return
httpheaders;
}
/**
* read bytes to flux<databuffer>
* @return
*/
@override
public
flux<databuffer> getbody() {
return
databufferutils.read(
new
bytearrayresource(bodybytes),
new
nettydatabufferfactory(bytebufallocator.
default
),contentlength);
}
};
serverwebexchange mutateexchange = exchange.mutate().request(decorator).build();
log.debug(
"[gatewaycontext]rewrite form data :{}"
,formdatabodystring);
return
chain.filter(mutateexchange);
}));
}
/**
* readjsonbody
* @param exchange
* @param chain
* @return
*/
private
mono<
void
> readbody(serverwebexchange exchange,gatewayfilterchain chain,gatewaycontext gatewaycontext){
/**
* join the body
*/
return
databufferutils.join(exchange.getrequest().getbody())
.flatmap(databuffer -> {
/**
* read the body flux<databuffer>
*/
databufferutils.retain(databuffer);
flux<databuffer> cachedflux = flux.defer(() -> flux.just(databuffer.slice(
0
, databuffer.readablebytecount())));
/**
* repackage serverhttprequest
*/
serverhttprequest mutatedrequest =
new
serverhttprequestdecorator(exchange.getrequest()) {
@override
public
flux<databuffer> getbody() {
return
cachedflux;
}
};
/**
* mutate exchage with new serverhttprequest
*/
serverwebexchange mutatedexchange = exchange.mutate().request(mutatedrequest).build();
/**
* read body string with default messagereaders
*/
return
serverrequest.create(mutatedexchange, messagereaders)
.bodytomono(string.
class
)
.doonnext(objectvalue -> {
gatewaycontext.setcachebody(objectvalue);
log.debug(
"[gatewaycontext]read jsonbody:{}"
,objectvalue);
}).then(chain.filter(mutatedexchange));
});
}
}
|
在后续filter中,可以直接从serverexchange中获取gatewaycontext,就可以获取到缓存的数据,如果需要缓存其他数据,则可以根据自己的需求,添加到gatewaycontext中即可 。
。
。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我.
原文链接:https://segmentfault.com/a/1190000017898354 。
最后此篇关于SpringCloud Finchley Gateway 缓存请求Body和Form表单的实现的文章就讲到这里了,如果你想了解更多关于SpringCloud Finchley Gateway 缓存请求Body和Form表单的实现的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我在 mongodb 中的玩家和锦标赛之间存在多对多关系。 我希望能够一次将许多玩家添加到锦标赛中。如果没有 ajax,这很简单,但我们有一个包含数千名玩家的数据库,因此表单选择变得巨大。 我们想为此
这个问题已经有答案了: When should I use html's and when spring's in Spring MVC web app? (3 个回答) 已关闭 6 年前。 我正
我正在 C++ Builder XE4 上使用 VCL。 我有以下组件。 FormMain 具有 TButton *B_select; FormSelect(或DialogSelect)具有 TCom
如何在不影响表单控件的情况下更改表单的 alphablend? 德尔福XE7 最佳答案 此问题的一个解决方案是使用多设备应用程序(如果无法使用VCL)。 如果您需要保留透明的TForm,只需更改属性T
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
我正在尝试扩展 Django 注册以包含我自己的注册表单。原则上这是相当简单的。我只需要编写自己的表单( CustomRegistrationForm ),它是原始表单( RegistrationFo
我正在尝试为我的网站实现聊天功能。为了做到这一点,我遵循了以下教程:https://channels.readthedocs.io/en/latest/tutorial/ 然后我稍微更改了代码以实现它
有一个问题,我需要用一个 html 表单提交两个相互关联的模型表单。我知道如何提交两个单独的表格,但是在相关模型表格的情况下外键让我发疯。 问题是,第二个表单应该用外键填充字段到第一个表单的实例。 在
我正在创建一个工具,允许某人输入食谱,然后将其保存为 XML 文件,我已经创建了 XSD,但我想知道如何在我的网页上制作一个表单以允许用户输入他们的食谱并遵守模式。我一直在研究 Ajax 和 Jque
在 .net win 表单(如 asp.net web 表单)中是否有可用的验证控件? 因为很难为我的每个控件设置正确的条件,所以我的表单中也有很多重复的代码。 正确的做法是什么? 最佳答案 看看这个
我有一个简短的问题。我正在学习如何使用 javascript 制作注册表,发现此链接非常有用。 http://www.w3resource.com/javascript/form/javascript
我正在开发一个项目,该项目将使用循环将许多表单添加到 mysql 数据库中。在 javascript 部分中,我无法让 var i 在函数 updatesum() 中工作。有人可以帮我吗? 我试图避免
在我的应用程序上有一个包含 2 个字段和一个保存按钮的表单。 在我的 onClick 结束时我需要什么来将光标返回到第一个字段。 我有这个来清除它们 txtData.setText("
这个问题不太可能帮助任何 future 的访问者;它只与一个小的地理区域、一个特定的时间点或一个非常狭窄的情况有关,这些情况并不普遍适用于互联网的全局受众。为了帮助使这个问题更广泛地适用,visit
<input type="text" name="textfield" onKeyPress="javascript:alert(event.
我正在构建的网站有一个登录表单,作为所有其他模板扩展的 base.html 模板的一部分;因此,我需要以某种方式处理每个页面上的登录/注销逻辑。 目前每个页面都在单独的 View 中加载,那么实现它的
我有一个表单类,看起来像.. #forms.py class ExampleForm(forms.Form): color = forms.CharField(max_length=25)
有没有办法在表单定义中给表单一个特殊的错误渲染函数?在 customizing-the-error-list-format 下的文档中它展示了如何为表单提供特殊的错误呈现函数,但似乎您必须在实例化表单
我正在处理由多个页面组成的表单,我想解决验证问题。 当我点击提交按钮时,当前页面上的所有字段都会在下方显示错误消息,但是如果我更改页面,那么我需要再次点击提交,因为这些字段未设置为已触摸。 如果我可以
是否可以附加到继承表单的 exclude 或 widgets 变量? 到目前为止,我有以下设置。 class AddPropertyForm(forms.ModelForm): num_mon
我是一名优秀的程序员,十分优秀!