version
spring-cloud 2021.0.1
spring-boot 2.6.3
spring-cloud-alibaba 2021.0.1.0
网关不是基于springmvc的,而是基于webflux去做的
SpringCloudGateway中Post请求参数只能读取一次
这是因为Gateway默认使用的是SpringWebflux,解决这个问题需要容重新构造一个request来替换原先的request
CacheBodyGlobalFilter这个全局过滤器把原有的request请求中的body内容读出来,并且使用ServerHttpRequestDecorator这个请求装饰器对request进行包装,重写getBody方法,并把包装后的请求放到过滤器链中传递下去。这样后面的过滤器中再使用exchange.getRequest().getBody()来获取body时,实际上就是调用的重载后的getBody方法,获取的最先已经缓存了的body数据。这样就能够实现body的多次读取了。
//过滤器的Ordered.HIGHEST_PRECEDENCE,即最高优先级的过滤器。优先级设置高的原因是某些系统内置的过滤器可能也会去读body。
@Configuration
public class RequestLogGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
URI uri = request.getURI();
//String path = request.getPath().value();
String path = request.getPath().pathWithinApplication().value();//打印请求路径
String requestUrl = this.getOriginalRequestUrl(exchange);//打印请求url
String method = request.getMethodValue();
//cors
HttpHeaders headers = request.getHeaders();
log.info("--> method: {} url: {} header: {}", method, requestUrl, headers);
if ("POST".equals(method)) {
return DataBufferUtils.join(exchange.getRequest().getBody())
.flatMap(dataBuffer -> {
byte[] bytes = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(bytes);
String bodyString = new String(bytes, StandardCharsets.UTF_8);
log.info("--> {}", bodyString);
//log.info("--> {}", formatStr(bodyString)); //formData
exchange.getAttributes().put("POST_BODY", bodyString);
DataBufferUtils.release(dataBuffer);
Flux<DataBuffer> cachedFlux = Flux.defer(() -> {
DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);
return Mono.just(buffer);
});
ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(request) {
@Override
public Flux<DataBuffer> getBody() {
return cachedFlux;
}
};
//log.info("****************************************************************************\n");
return chain.filter(exchange.mutate().request(mutatedRequest).build());
});
} else if ("GET".equals(method)) {
MultiValueMap<String, String> queryParams = request.getQueryParams();
log.info("请求参数:" + queryParams);
//log.info("****************************************************************************\n");
return chain.filter(exchange);
}
return chain.filter(exchange);
}
private String getOriginalRequestUrl(ServerWebExchange exchange) {
ServerHttpRequest req = exchange.getRequest();
LinkedHashSet<URI> uris = exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
URI requestUri = uris.stream().findFirst().orElse(req.getURI());
MultiValueMap<String, String> queryParams = req.getQueryParams();
//打印 /api/rest/feign/order/detail
// return UriComponentsBuilder.fromPath(requestUri.getRawPath()).queryParams(queryParams).build().toUriString();
return requestUri.toString(); // http://localhost:8091/api/rest/feign/order/detail
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
/**
* 去掉FormData 空格,换行和制表符
* @param str
* @return
*/
private static String formatStr(String str){
if (str != null && str.length() > 0) {
Pattern p = Pattern.compile("\\s*|\t|\r|\n");
Matcher m = p.matcher(str);
return m.replaceAll("");
}
return str;
}
}
@Configuration
public class ResponseLogGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
try {
ServerHttpResponse originalResponse = exchange.getResponse();
DataBufferFactory bufferFactory = originalResponse.bufferFactory();
HttpStatus statusCode = originalResponse.getStatusCode();
if(statusCode == HttpStatus.OK){
ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
@Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
//log.info("body instanceof Flux: {}", (body instanceof Flux));
if (body instanceof Flux) {
Flux<? extends DataBuffer> fluxBody = Flux.from(body);
//
return super.writeWith(fluxBody.map(dataBuffer -> {
byte[] content = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(content);
DataBufferUtils.release(dataBuffer);//释放掉内存
// 构建日志
StringBuilder sb2 = new StringBuilder(200);
sb2.append("<--- {} {} \n");
List<Object> rspArgs = new ArrayList<>();
rspArgs.add(originalResponse.getStatusCode());
//rspArgs.add(requestUrl);
String data = new String(content, StandardCharsets.UTF_8);//data
sb2.append(data);
log.info(sb2.toString(), rspArgs.toArray());//log.info("<-- {} {}\n", originalResponse.getStatusCode(), data);
return bufferFactory.wrap(content);
}));
} else {
log.error("<--- {} 响应code异常", getStatusCode());
}
return super.writeWith(body);
}
};
return chain.filter(exchange.mutate().response(decoratedResponse).build());
}
return chain.filter(exchange);//降级处理返回数据
}catch (Exception e){
log.error("gateway log exception.\n" + e);
return chain.filter(exchange);
}
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
Gateway跨域处理
https://blog.csdn.net/weixin_43730516/article/details/127040628
@Configuration
public class CorsWebFilterConfig {
@Bean
public CorsWebFilter corsWebFilter() {
CorsConfigurationSource corsConfigurationSource = new CorsConfigurationSource() {
@Override
public CorsConfiguration getCorsConfiguration(ServerWebExchange serverWebExchange) {
CorsConfiguration corsConfig = new CorsConfiguration();
corsConfig.addAllowedHeader("*");
corsConfig.addAllowedMethod("*");
corsConfig.addAllowedOriginPattern("*");
corsConfig.setMaxAge(1800L);
corsConfig.setAllowCredentials(true);
return corsConfig;
}
};
return new CorsWebFilter(corsConfigurationSource);
} }
参考资料
SpringCloud Gateway 打印请求和响应信息
https://blog.csdn.net/liben0429/article/details/126106528
Gateway网关日志
https://blog.csdn.net/small_to_large/article/details/125326498
原文链接:https://blog.csdn.net/sowei2009/article/details/126104934
https://www.jb51.net/article/240256.htm
https://blog.csdn.net/chaojunma/article/details/122595612
http://t.zoukankan.com/lifengdi-p-12524092.html
https://www.51ufo.cn/%E5%88%86%E5%B8%83%E5%BC%8F/2021/12/23/SpringCloud-Gateway%E7%BD%91%E5%85%B3%E6%94%B6%E9%9B%86%E8%AF%B7%E6%B1%82%E6%97%A5%E5%BF%97.html
乱码问题处理
https://blog.csdn.net/qq_38380025/article/details/100032490
https://www.icode9.com/content-4-1302983.html
globalcors
spring:
cloud:
gateway:
default-filters: #全局用于配置所有路由共享过滤器
- PreserveHostHeader #发送原主机头
globalcors:
cors-configurations:
'[/**]': # 匹配所有请求
allowedOrigins: "*" #跨域处理 允许所有的域
allowedMethods: # 支持的方法
- GET
- POST
- PUT
- DELETE
routes:
- id: product_route
uri: http://192.168.0.101:8080
order: 1
predicates:
- Path=/**

在我的Controller中,我通过以下方式在我的index方法中支持HTML和JSON:respond_todo|format|format.htmlformat.json{renderjson:@user}end在浏览器中拉起它时,它会自然地以HTML呈现。但是,当我对/user资源进行内容类型为application/json的curl调用时(因为它是索引方法),我仍然将HTML作为响应。如何获取JSON作为响应?我还需要说明什么? 最佳答案 您应该将.json附加到请求的url,提供的格式在routes.rb的路径中定义。这
我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R
我是Google云的新手,我正在尝试对其进行首次部署。我的第一个部署是RubyonRails项目。我基本上是在关注thisguideinthegoogleclouddocumentation.唯一的区别是我使用的是我自己的项目,而不是他们提供的“helloworld”项目。这是我的app.yaml文件runtime:customvm:trueentrypoint:bundleexecrackup-p8080-Eproductionconfig.ruresources:cpu:0.5memory_gb:1.3disk_size_gb:10当我转到我的项目目录并运行gcloudprevie
rails中是否有任何规定允许站点的所有AJAXPOST请求在没有authenticity_token的情况下通过?我有一个调用Controller方法的JqueryPOSTajax调用,但我没有在其中放置任何真实性代码,但调用成功。我的ApplicationController确实有'request_forgery_protection'并且我已经改变了config.action_controller.consider_all_requests_local在我的environments/development.rb中为false我还搜索了我的代码以确保我没有重载ajaxSend来发送
在我做的一些网络开发中,我有多个操作开始,比如对外部API的GET请求,我希望它们同时开始,因为一个不依赖另一个的结果。我希望事情能够在后台运行。我找到了concurrent-rubylibrary这似乎运作良好。通过将其混合到您创建的类中,该类的方法具有在后台线程上运行的异步版本。这导致我编写如下代码,其中FirstAsyncWorker和SecondAsyncWorker是我编写的类,我在其中混合了Concurrent::Async模块,并编写了一个名为“work”的方法来发送HTTP请求:defindexop1_result=FirstAsyncWorker.new.async.
我是Ruby的新手。我试过查看在线文档,但没有找到任何有效的方法。我想在以下HTTP请求botget_response()和get()中包含一个用户代理。有人可以指出我正确的方向吗?#PreliminarycheckthatProggitisupcheck=Net::HTTP.get_response(URI.parse(proggit_url))ifcheck.code!="200"puts"ErrorcontactingProggit"returnend#Attempttogetthejsonresponse=Net::HTTP.get(URI.parse(proggit_url)
classPacketdefinitialize(name,age,number,array)@name=name@age=age@number=number@neighbors=arrayendendp1=Packet.new("n1",5,2,[1,2,3,4])putsp1.name我有上面的代码,但是每当我执行puts语句时,我都会收到nameisnotamethod的错误。我不知道任何其他方式来打印p1的名称。如何打印姓名? 最佳答案 这里的问题是,虽然您拥有实例变量,但您并未使它们可访问。attr_reader:vari
我正在使用mechanize登录网站,然后检索页面。我遇到了一些问题,我怀疑这是由于cookie中的某些值造成的。当Mechanize登录网站时,我假设它存储了cookie。如何通过Mechanize打印出存储在cookie中的所有数据? 最佳答案 代理有一个cookie方法。agent=Mechanize.newpage=agent.get("http://www.google.com/")agent.cookiesagent.cookies.to_scookie返回一个Mechanize::Cookiesobject
在我的路线文件中我有:match'graphs/(:id(/:action))'=>'graphs#(:action)'如果是GET请求(工作)或POST请求(不工作),我想匹配它我知道我可以使用以下方法在资源中声明POST请求:post'/'=>:show,:on=>:member但是我怎样才能为比赛做到这一点呢?谢谢。 最佳答案 如果你同时想要POST和GETmatch'graphs/(:id(/:action))'=>'graphs#(:action)',:via=>[:get,:post]编辑默认值可以设置如下match'g
如何在出现异常时指定全局救援,如果您将Sinatra用于API或应用程序,您将如何处理日志记录? 最佳答案 404可以在not_found方法的帮助下处理,例如:not_founddo'Sitedoesnotexist.'end500s可以通过调用带有block的错误方法来处理,例如:errordo"Applicationerror.Plstrylater."end错误的详细信息可以通过request.env中的sinatra.error访问,如下所示:errordo'Anerroroccured:'+request.env['si