春季云网关 - 未找到路由时自定义404错误消息
我有一个基本的Spring Cloud Gateway应用程序,该应用程序配置为YAML:
server:
port: 8080
logging:
level:
reactor:
netty: INFO
org:
springframework:
cloud:
gateway: TRACE
spring:
cloud:
gateway:
httpclient:
wiretap: true
httpserver:
wiretap: true
routes:
- id: test-route-1
uri: https://www.google.com
predicates:
- Path=/
- id: test-route-2
uri: http://1.1.1.1:1111
predicates:
- Path=/common/**
这是输入点:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
我遇到的问题是,当我尝试使用Postman的基本get请求达到不存在的路由定义时,例如,
localhost:8080/asd
我可以回来Postman中的基本404错误:
{
"timestamp": "2022-04-28T09:27:36.647+00:00",
"path": "/asd",
"status": 404,
"error": "Not Found",
"message": "",
"requestId": "86bc902a-8"
}
我想修改此响应并添加有意义的消息,例如“未找到路由定义”。执行我的网关配置。当我执行有效的请求时,控制台的外观是:
2022-04-19 13:21:48.577 INFO 64240 --- [ main] com.test.apigateway.Application : Started Application in 11.143 seconds (JVM running for 11.902)
2022-04-19 13:21:50.125 TRACE 64240 --- [ctor-http-nio-3] o.s.c.g.f.WeightCalculatorWebFilter : Weights attr: {}
2022-04-19 13:21:50.145 TRACE 64240 --- [ctor-http-nio-3] o.s.c.g.h.p.PathRoutePredicateFactory : Pattern "/" matches against value "/"
2022-04-19 13:21:50.146 DEBUG 64240 --- [ctor-http-nio-3] o.s.c.g.h.RoutePredicateHandlerMapping : Route matched: test-route-1
2022-04-19 13:21:50.146 DEBUG 64240 --- [ctor-http-nio-3] o.s.c.g.h.RoutePredicateHandlerMapping : Mapping [Exchange: GET http://localhost:8080/] to Route{id='test-route-1', uri=https://www.google.com:443, order=0, predicate=Paths: [/], match trailing slash: true, gatewayFilters=[], metadata={}}
2022-04-19 13:21:50.146 DEBUG 64240 --- [ctor-http-nio-3] o.s.c.g.h.RoutePredicateHandlerMapping : [9b8150f7-1] Mapped to org.springframework.cloud.gateway.handler.FilteringWebHandler@5bf45d2c
2022-04-19 13:21:50.148 DEBUG 64240 --- [ctor-http-nio-3] o.s.c.g.handler.FilteringWebHandler : Sorted gatewayFilterFactories: [[GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.RemoveCachedBodyFilter@1e40fbb3}, order = -2147483648], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.AdaptCachedBodyGlobalFilter@7ec08115}, order = -2147482648], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.NettyWriteResponseFilter@6d5f4900}, order = -1], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.ForwardPathFilter@1e6060f1}, order = 0], [GatewayFilterAdapter{delegate=com.fadata.apigateway.filters.AuthenticationFilter@5885a768}, order = 1], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter@1b560eb0}, order = 10000], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.config.GatewayNoLoadBalancerClientAutoConfiguration$NoLoadBalancerClientFilter@2c6c302f}, order = 10150], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.WebsocketRoutingFilter@7e49ded}, order = 2147483646], GatewayFilterAdapter{delegate=com.fadata.apigateway.Application$LoggingGlobalFiltersConfigurations$$Lambda$525/0x00000008004f6840@51ba952e}, [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.NettyRoutingFilter@2416c658}, order = 2147483647], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.ForwardRoutingFilter@9e02f84}, order = 2147483647]]
2022-04-19 13:21:50.156 INFO 64240 --- [ctor-http-nio-3] c.f.a.filters.AuthenticationFilter : 1 - Global Pre Filter executed [AuthenticationFilter]
2022-04-19 13:21:50.157 TRACE 64240 --- [ctor-http-nio-3] o.s.c.g.filter.RouteToRequestUrlFilter : RouteToRequestUrlFilter start
2022-04-19 13:21:51.145 TRACE 64240 --- [ctor-http-nio-3] o.s.c.gateway.filter.NettyRoutingFilter : outbound route: 154ff117, inbound: [9b8150f7-1]
2022-04-19 13:21:51.243 INFO 64240 --- [ctor-http-nio-3] ation$LoggingGlobalFiltersConfigurations : Global Post Filter executed
2022-04-19 13:21:51.243 TRACE 64240 --- [ctor-http-nio-3] o.s.c.g.filter.NettyWriteResponseFilter : NettyWriteResponseFilter start inbound: 154ff117, outbound: [9b8150f7-1]
在响应击中客户端之前,它清楚地执行了我的全局预滤波器和邮政过滤器。但是,如果我将请求发送到不存在的路线,这就是我所看到的:
2022-04-19 13:23:16.926 TRACE 64240 --- [ctor-http-nio-3] o.s.c.g.f.WeightCalculatorWebFilter : Weights attr: {}
2022-04-19 13:23:16.926 TRACE 64240 --- [ctor-http-nio-3] o.s.c.g.h.p.PathRoutePredicateFactory : Pattern "[/]" does not match against value "/asd"
2022-04-19 13:23:16.927 TRACE 64240 --- [ctor-http-nio-3] o.s.c.g.h.p.PathRoutePredicateFactory : Pattern "[/common/**]" does not match against value "/asd"
2022-04-19 13:23:16.927 TRACE 64240 --- [ctor-http-nio-3] o.s.c.g.h.RoutePredicateHandlerMapping : No RouteDefinition found for [Exchange: GET http://localhost:8080/asd]
似乎它甚至没有执行任何过滤器,因此我无法拦截响应并以任何方式进行检查或修改。
我已经在源代码中搜索了,发现未找到路由定义错误正在记录,但我无法在任何地方拦截响应。
只是为了澄清 - 我根本不需要渲染HTML页面,我只想在使用404响应发送给客户端之前修改JSON对象,并向该JSON添加消息。
有什么想法,我该怎么做?谢谢!
I have a basic Spring Cloud Gateway app that is configured with YAML:
server:
port: 8080
logging:
level:
reactor:
netty: INFO
org:
springframework:
cloud:
gateway: TRACE
spring:
cloud:
gateway:
httpclient:
wiretap: true
httpserver:
wiretap: true
routes:
- id: test-route-1
uri: https://www.google.com
predicates:
- Path=/
- id: test-route-2
uri: http://1.1.1.1:1111
predicates:
- Path=/common/**
Here's the entry point:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
The issue I have is that when I try to hit a non-existent route definition with a basic GET request through Postman, for example
localhost:8080/asd
I get back a basic 404 error in Postman:
{
"timestamp": "2022-04-28T09:27:36.647+00:00",
"path": "/asd",
"status": 404,
"error": "Not Found",
"message": "",
"requestId": "86bc902a-8"
}
I want to modify this response and add a meaningful message, such as "Route definition not found.", but it seems like this response is sent even before any of the filters in my gateway configuration are executed. Here's how the console looks when I execute a valid request:
2022-04-19 13:21:48.577 INFO 64240 --- [ main] com.test.apigateway.Application : Started Application in 11.143 seconds (JVM running for 11.902)
2022-04-19 13:21:50.125 TRACE 64240 --- [ctor-http-nio-3] o.s.c.g.f.WeightCalculatorWebFilter : Weights attr: {}
2022-04-19 13:21:50.145 TRACE 64240 --- [ctor-http-nio-3] o.s.c.g.h.p.PathRoutePredicateFactory : Pattern "/" matches against value "/"
2022-04-19 13:21:50.146 DEBUG 64240 --- [ctor-http-nio-3] o.s.c.g.h.RoutePredicateHandlerMapping : Route matched: test-route-1
2022-04-19 13:21:50.146 DEBUG 64240 --- [ctor-http-nio-3] o.s.c.g.h.RoutePredicateHandlerMapping : Mapping [Exchange: GET http://localhost:8080/] to Route{id='test-route-1', uri=https://www.google.com:443, order=0, predicate=Paths: [/], match trailing slash: true, gatewayFilters=[], metadata={}}
2022-04-19 13:21:50.146 DEBUG 64240 --- [ctor-http-nio-3] o.s.c.g.h.RoutePredicateHandlerMapping : [9b8150f7-1] Mapped to org.springframework.cloud.gateway.handler.FilteringWebHandler@5bf45d2c
2022-04-19 13:21:50.148 DEBUG 64240 --- [ctor-http-nio-3] o.s.c.g.handler.FilteringWebHandler : Sorted gatewayFilterFactories: [[GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.RemoveCachedBodyFilter@1e40fbb3}, order = -2147483648], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.AdaptCachedBodyGlobalFilter@7ec08115}, order = -2147482648], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.NettyWriteResponseFilter@6d5f4900}, order = -1], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.ForwardPathFilter@1e6060f1}, order = 0], [GatewayFilterAdapter{delegate=com.fadata.apigateway.filters.AuthenticationFilter@5885a768}, order = 1], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter@1b560eb0}, order = 10000], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.config.GatewayNoLoadBalancerClientAutoConfiguration$NoLoadBalancerClientFilter@2c6c302f}, order = 10150], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.WebsocketRoutingFilter@7e49ded}, order = 2147483646], GatewayFilterAdapter{delegate=com.fadata.apigateway.Application$LoggingGlobalFiltersConfigurations$Lambda$525/0x00000008004f6840@51ba952e}, [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.NettyRoutingFilter@2416c658}, order = 2147483647], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.ForwardRoutingFilter@9e02f84}, order = 2147483647]]
2022-04-19 13:21:50.156 INFO 64240 --- [ctor-http-nio-3] c.f.a.filters.AuthenticationFilter : 1 - Global Pre Filter executed [AuthenticationFilter]
2022-04-19 13:21:50.157 TRACE 64240 --- [ctor-http-nio-3] o.s.c.g.filter.RouteToRequestUrlFilter : RouteToRequestUrlFilter start
2022-04-19 13:21:51.145 TRACE 64240 --- [ctor-http-nio-3] o.s.c.gateway.filter.NettyRoutingFilter : outbound route: 154ff117, inbound: [9b8150f7-1]
2022-04-19 13:21:51.243 INFO 64240 --- [ctor-http-nio-3] ation$LoggingGlobalFiltersConfigurations : Global Post Filter executed
2022-04-19 13:21:51.243 TRACE 64240 --- [ctor-http-nio-3] o.s.c.g.filter.NettyWriteResponseFilter : NettyWriteResponseFilter start inbound: 154ff117, outbound: [9b8150f7-1]
It clearly executes both my global pre and post filters before the response hits the client. However, if I send a request to a non-existent route, here's what I see:
2022-04-19 13:23:16.926 TRACE 64240 --- [ctor-http-nio-3] o.s.c.g.f.WeightCalculatorWebFilter : Weights attr: {}
2022-04-19 13:23:16.926 TRACE 64240 --- [ctor-http-nio-3] o.s.c.g.h.p.PathRoutePredicateFactory : Pattern "[/]" does not match against value "/asd"
2022-04-19 13:23:16.927 TRACE 64240 --- [ctor-http-nio-3] o.s.c.g.h.p.PathRoutePredicateFactory : Pattern "[/common/**]" does not match against value "/asd"
2022-04-19 13:23:16.927 TRACE 64240 --- [ctor-http-nio-3] o.s.c.g.h.RoutePredicateHandlerMapping : No RouteDefinition found for [Exchange: GET http://localhost:8080/asd]
It seems like it doesn't even execute any filters, so I can't intercept the response and check or modify it in any way.
I've searched within the source code and found where the No RouteDefinition found error is being logged, but I can't intercept the response anywhere it seems.
Just to clarify - I don't need to render an HTML page at all, I just want to modify the JSON object before it is sent back to the client with the 404 response, and add a message to that JSON.
Any ideas how I can do that? Thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我找到了解决这个问题的临时解决方法,但是它很具体,在所有情况下都不会起作用。从本质上讲,我已经完成的是,我已经制作了一个
@component
类,该类扩展了defaulterrorattributes
,并将其实现为其getErrorattributes
方法。这样,我可以访问响应,以防API网关中遇到任何错误。方法是该方法的外观:现在,在我的情况下,隐藏在网关后面的所有服务,毫无例外,如果发生异常,则具有特定而均匀的响应主体,例如击中不存在的端点或提供不正确的查询参数但是
,当击中不正确的路由时,错误来自网关本身,默认情况下,正体包含一个称为
request> requestID
的属性。我的任何基础服务的响应体中都不存在此属性,因此我认为我可以利用它来发挥自己的优势。在我的
populatemapwitherrormessage
方法中,我检查错误是否是由未找到路由定义引起的,如果是的,我会用必要的字符串填充该消息:因此,每当我点击不存在的路由时, ,我得到了一个看起来像这样的身体:
这正是我所需要的。在该自定义解决方法之前,
消息
属性为null
。即使这应该暂时使用,我仍然想了解如何通过检测未找到路由定义引起的错误直接操纵响应。必须有一种更简单的方法,如果有人知道,如果他们能分享这一点,我将不胜感激。
这是整个课程以供参考:
I have found a temporary workaround to deal with this issue, but it is very specific and will not work in all cases. Essentially what I have done is I've made a
@Component
class that extends theDefaultErrorAttributes
, and have given implementation to itsgetErrorAttributes
method. This way, I gain access to the response in case any error is encountered in the API Gateway. Here's how the method looks:Now, in my case all of the services hidden behind the gateway, without exception, have a particular and uniform response body in case an exception occurs, such as hitting a non-existent endpoint, or supplying incorrect query parameters, etc.
However, when an incorrect route is hit, the error comes from the gateway itself, and the body, by default, contains an attribute called
requestId
. This attribute doesn't exist in the response bodies of any of my underlying services, so I figured I could use that to my advantage.In my
populateMapWithErrorMessage
method I check if the error is caused by no route definition found or not, and if it is, I populate the message with the necessary string:So now whenever I hit a non-existent route, I get back a body that looks like this:
Which is exactly what I needed. Before that custom workaround, the
message
attribute wasnull
.Even though this should work for now, I still want to understand how I can manipulate the response directly by detecting if the error is caused by no route definition found. There has to be an easier way, and if anyone knows it I'll be grateful if they could share that.
Here's the entire class for reference:
很简单,您可以将自定义的HTML文件创建到 Resources/public/error/404.html 中,并在其中添加自定义错误页面。
这是文档参考。
https://docs.spring.io/spring-boot/docs/2.0.0.m6/reference/reference/html/boot-features/boot-features-debevering-debevelop-web--web-webevending-web-developing-web-developing-webeveloping-webevepend-webeveloping-webeveloping-webeveloping-webevelawing-webevelod-web-developing-web-develops- applications.html#boot-features-webflux-error处理custom-error页面
It's pretty simple, you can create a custom html file into resources/public/error/404.html and add your custom error page in there.
Here's the documentation reference.
https://docs.spring.io/spring-boot/docs/2.0.0.M6/reference/html/boot-features-developing-web-applications.html#boot-features-webflux-error-handling-custom-error-pages