Zuul Api 网关映射错误服务 url(字符集)?
Zuul Api Gateway mapped wrong service url (character set)?
我正在尝试创建微服务,但 zuul api 网关服务映射错误 url。似乎与编码问题有关。
祖尔application.properties
spring.application.name=zuul-api-gateway
server.port=8765
eureka.client.service-url.default-zone=http://localhost:8761/eureka
服务application.yml
spring:
application:
name: car-service
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3307/car_service_db?useSSL=false
username: root
password: admin
profiles:
active: local
server:
port: 8080
eureka:
client:
service-url:
default-zone: http://localhost:8761/eureka
management:
security:
enabled: false
其他服务application.yml
spring:
application:
name: company-service
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3307/company_service_db?useSSL=false
username: root
password: admin
server:
port: 8081
eureka:
client:
service-url:
default-zone: http://localhost:8761/eureka
management:
security:
enabled: false
尤里卡日志:
2018-02-11 00:02:41.204 INFO 22672 --- [nio-8761-exec-2] c.n.e.registry.AbstractInstanceRegistry : Registered instance CAR-SERVICE/localhost:car-service:8080 with status UP (replication=false)
2018-02-11 00:02:58.469 INFO 22672 --- [a-EvictionTimer] c.n.e.registry.AbstractInstanceRegistry : Running the evict task with compensationTime 0ms
2018-02-11 00:03:06.468 INFO 22672 --- [nio-8761-exec-4] c.n.e.registry.AbstractInstanceRegistry : Registered instance COMPANY-SERVICE/localhost:company-service:8081 with status UP (replication=false)
2018-02-11 00:03:26.139 INFO 22672 --- [nio-8761-exec-9] c.n.e.registry.AbstractInstanceRegistry : Registered instance ZUUL-API-GATEWAY/localhost:zuul-api-gateway:8765 with status UP (replication=false)
和 zuul 日志
2018-02-11 00:03:35.037 INFO [zuul-api-gateway,6944cfba5180a640,6944cfba5180a640,false] 2132 --- [nio-8765-exec-1] o.s.c.n.zuul.web.ZuulHandlerMapping : Mapped URL path [/car-servıce/**] onto handler of type [class org.springframework.cloud.netflix.zuul.web.ZuulController]
2018-02-11 00:05:06.872 INFO [zuul-api-gateway,773aa5fc779af5e6,773aa5fc779af5e6,false] 2132 --- [nio-8765-exec-3] o.s.c.n.zuul.web.ZuulHandlerMapping : Mapped URL path [/company-servıce/**] onto handler of type [class org.springframework.cloud.netflix.zuul.web.ZuulController]
2018-02-11 00:05:06.873 INFO [zuul-api-gateway,773aa5fc779af5e6,773aa5fc779af5e6,false] 2132 --- [nio-8765-exec-3] o.s.c.n.zuul.web.ZuulHandlerMapping : Mapped URL path [/zuul-apı-gateway/**] onto handler of type [class org.springframework.cloud.netflix.zuul.web.ZuulController]
在 zuul 日志中,所有服务都由带有“ı”的特殊字母映射,这是土耳其字符而不是 'i',我无法通过 zuul api 网关访问我的服务。
我也尝试在这个例子中覆盖 Zuul 配置:
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Bean
FormBodyWrapperFilter formBodyWrapperFilter() {
return new FormBodyWrapperFilter(new MyFormHttpMessageConverter());
}
private class MyFormHttpMessageConverter extends FormHttpMessageConverter {
private byte[] getAsciiBytes(String name) {
try {
// THIS IS THE ONLY MODIFICATION:
return name.getBytes("UTF-8");
} catch (UnsupportedEncodingException ex) {
// Should not happen - US-ASCII is always supported.
throw new IllegalStateException(ex);
}
}
}
}
所以当我尝试通过 Zuul api 网关调用服务时,returns 404 未找到。
我正在使用 spring-boot 2.0.0.M3,spring-cloud Finchley.M2 版本
当您使用 Spring 启动时,我建议您为 zuul 网关使用注释并为 zuul 创建自定义过滤器,它将处理每一件事。
请找到下面的代码,看看它是否适合您:
Main Class
@EnableZuulProxy
@SpringBootApplication
@EnableScheduling
@EnableFeignClients(basePackages = { Constants.FEIGN_BASE_PACKAGE })
@ComponentScan(basePackages = { Constants.BASE_PACKAGE, Constants.LOGGING_PACKAGE })
@EntityScan(basePackages = { Constants.ENTITY_BASE_PACKAGE })
@EnableDiscoveryClient
public class GatewayInitializer {
/**
* The main method.
*
* @param args the arguments
*/
public static void main(String[] args) {
Security.setProperty("networkaddress.cache.ttl", "30");
ConfigurableApplicationContext context = SpringApplication.run(Initializer.class, args);
}
ZUUL Filter
package com.sxm.aota.gateway.filters;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.util.ReflectionUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
/**
* This CustomErrorFilter involve to handle ZuulException.
*/
public class CustomErrorFilter extends ZuulFilter {
/** The logger. */
private static final Logger logger = LoggerFactory.getLogger(CustomErrorFilter.class);
/** The message source. */
@Autowired
private MessageSource messageSource;
/** The properties. */
@Autowired
private Properties properties;
/*
* (non-Javadoc)
*
* @see com.netflix.zuul.ZuulFilter#filterType()
*/
@Override
public String filterType() {
return "post";
}
/*
* (non-Javadoc)
*
* @see com.netflix.zuul.ZuulFilter#filterOrder()
*/
@Override
public int filterOrder() {
return -1; // Needs to run before SendErrorFilter which has filterOrder == 0
}
/*
* (non-Javadoc)
*
* @see com.netflix.zuul.IZuulFilter#shouldFilter()
*/
@Override
public boolean shouldFilter() {
// only forward to errorPath if it hasn't been forwarded to already
return RequestContext.getCurrentContext().containsKey("error.status_code");
}
/*
* (non-Javadoc)
*
* @see com.netflix.zuul.IZuulFilter#run()
*/
@Override
public Object run() {
try {
RequestContext ctx = RequestContext.getCurrentContext();
Object e = ctx.get("error.exception");
if (e != null && e instanceof ZuulException) {
ZuulException zuulException = (ZuulException) e;
logger.error("Zuul failure detected: " + zuulException.getMessage(), zuulException);
// Remove error code to prevent further error handling in follow up filters
ctx.remove("error.status_code");
} else {
error.setMessage(messageSource.getMessage(Constants.REQUESTED_SERVICE_UNAVAILABLE, new Object[] { zuulException.getCause() }, properties.getCurrentLocale()));
ctx.setResponseBody(mapper.writeValueAsString(error));
ctx.getResponse().setContentType("application/json");
ctx.setResponseStatusCode(500); // Can set any error code as excepted
}
}
} catch (Exception ex) {
logger.error("Exception filtering in custom error filter", ex);
ReflectionUtils.rethrowRuntimeException(ex);
}
return null;
}
}
我通过为 toLowerCase(Locale.ROOT) 选项创建扩展的 EurekaDiscoveryClient 解决了这个问题。
import com.netflix.appinfo.EurekaInstanceConfig;
import com.netflix.discovery.EurekaClient;
import com.netflix.discovery.shared.Application;
import com.netflix.discovery.shared.Applications;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.netflix.eureka.EurekaDiscoveryClient;
import org.springframework.stereotype.Component;
import java.util.*;
@Component
public class CustomEurekaDiscoveryClient extends EurekaDiscoveryClient implements DiscoveryClient {
private final EurekaInstanceConfig config;
private final EurekaClient eurekaClient;
public CustomEurekaDiscoveryClient(EurekaInstanceConfig config, @Qualifier("eurekaClient") EurekaClient eurekaClient) {
super(config, eurekaClient);
this.config = config;
this.eurekaClient = eurekaClient;
}
@Override
public List<String> getServices() {
Applications applications = this.eurekaClient.getApplications();
if (applications == null) {
return Collections.emptyList();
}
List<Application> registered = applications.getRegisteredApplications();
List<String> names = new ArrayList<>();
for (Application app : registered) {
if (app.getInstances().isEmpty()) {
continue;
}
names.add(app.getName().toLowerCase(Locale.ROOT));
}
return names;
}
}
大写 'I' 转换为小写 'ı' 而不是土耳其 OS 和 jvm 中的 'i' 这就是为什么我需要覆盖 public 列出 getServices() 方法。
如果 toLowerCase(Locale.ROOT) 方法调用时没有 Locale.ROOT java 将字符转换为 'ı' 但使用 Locale.ROOT 选项,方法转换为 'i' 并且项目工作正常。
可以在Zuul应用的main方法中为英文添加Locale.setDefault
public static void main(String[] args) {
Locale.setDefault(new Locale("en", "US"));
SpringApplication.run(ServerApplication.class, args);
}
它也解决了你的问题。
我正在尝试创建微服务,但 zuul api 网关服务映射错误 url。似乎与编码问题有关。
祖尔application.properties
spring.application.name=zuul-api-gateway
server.port=8765
eureka.client.service-url.default-zone=http://localhost:8761/eureka
服务application.yml
spring:
application:
name: car-service
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3307/car_service_db?useSSL=false
username: root
password: admin
profiles:
active: local
server:
port: 8080
eureka:
client:
service-url:
default-zone: http://localhost:8761/eureka
management:
security:
enabled: false
其他服务application.yml
spring:
application:
name: company-service
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3307/company_service_db?useSSL=false
username: root
password: admin
server:
port: 8081
eureka:
client:
service-url:
default-zone: http://localhost:8761/eureka
management:
security:
enabled: false
尤里卡日志:
2018-02-11 00:02:41.204 INFO 22672 --- [nio-8761-exec-2] c.n.e.registry.AbstractInstanceRegistry : Registered instance CAR-SERVICE/localhost:car-service:8080 with status UP (replication=false)
2018-02-11 00:02:58.469 INFO 22672 --- [a-EvictionTimer] c.n.e.registry.AbstractInstanceRegistry : Running the evict task with compensationTime 0ms
2018-02-11 00:03:06.468 INFO 22672 --- [nio-8761-exec-4] c.n.e.registry.AbstractInstanceRegistry : Registered instance COMPANY-SERVICE/localhost:company-service:8081 with status UP (replication=false)
2018-02-11 00:03:26.139 INFO 22672 --- [nio-8761-exec-9] c.n.e.registry.AbstractInstanceRegistry : Registered instance ZUUL-API-GATEWAY/localhost:zuul-api-gateway:8765 with status UP (replication=false)
和 zuul 日志
2018-02-11 00:03:35.037 INFO [zuul-api-gateway,6944cfba5180a640,6944cfba5180a640,false] 2132 --- [nio-8765-exec-1] o.s.c.n.zuul.web.ZuulHandlerMapping : Mapped URL path [/car-servıce/**] onto handler of type [class org.springframework.cloud.netflix.zuul.web.ZuulController]
2018-02-11 00:05:06.872 INFO [zuul-api-gateway,773aa5fc779af5e6,773aa5fc779af5e6,false] 2132 --- [nio-8765-exec-3] o.s.c.n.zuul.web.ZuulHandlerMapping : Mapped URL path [/company-servıce/**] onto handler of type [class org.springframework.cloud.netflix.zuul.web.ZuulController]
2018-02-11 00:05:06.873 INFO [zuul-api-gateway,773aa5fc779af5e6,773aa5fc779af5e6,false] 2132 --- [nio-8765-exec-3] o.s.c.n.zuul.web.ZuulHandlerMapping : Mapped URL path [/zuul-apı-gateway/**] onto handler of type [class org.springframework.cloud.netflix.zuul.web.ZuulController]
在 zuul 日志中,所有服务都由带有“ı”的特殊字母映射,这是土耳其字符而不是 'i',我无法通过 zuul api 网关访问我的服务。
我也尝试在这个例子中覆盖 Zuul 配置:
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Bean
FormBodyWrapperFilter formBodyWrapperFilter() {
return new FormBodyWrapperFilter(new MyFormHttpMessageConverter());
}
private class MyFormHttpMessageConverter extends FormHttpMessageConverter {
private byte[] getAsciiBytes(String name) {
try {
// THIS IS THE ONLY MODIFICATION:
return name.getBytes("UTF-8");
} catch (UnsupportedEncodingException ex) {
// Should not happen - US-ASCII is always supported.
throw new IllegalStateException(ex);
}
}
}
}
所以当我尝试通过 Zuul api 网关调用服务时,returns 404 未找到。
我正在使用 spring-boot 2.0.0.M3,spring-cloud Finchley.M2 版本
当您使用 Spring 启动时,我建议您为 zuul 网关使用注释并为 zuul 创建自定义过滤器,它将处理每一件事。
请找到下面的代码,看看它是否适合您:
Main Class
@EnableZuulProxy
@SpringBootApplication
@EnableScheduling
@EnableFeignClients(basePackages = { Constants.FEIGN_BASE_PACKAGE })
@ComponentScan(basePackages = { Constants.BASE_PACKAGE, Constants.LOGGING_PACKAGE })
@EntityScan(basePackages = { Constants.ENTITY_BASE_PACKAGE })
@EnableDiscoveryClient
public class GatewayInitializer {
/**
* The main method.
*
* @param args the arguments
*/
public static void main(String[] args) {
Security.setProperty("networkaddress.cache.ttl", "30");
ConfigurableApplicationContext context = SpringApplication.run(Initializer.class, args);
}
ZUUL Filter
package com.sxm.aota.gateway.filters;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.util.ReflectionUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
/**
* This CustomErrorFilter involve to handle ZuulException.
*/
public class CustomErrorFilter extends ZuulFilter {
/** The logger. */
private static final Logger logger = LoggerFactory.getLogger(CustomErrorFilter.class);
/** The message source. */
@Autowired
private MessageSource messageSource;
/** The properties. */
@Autowired
private Properties properties;
/*
* (non-Javadoc)
*
* @see com.netflix.zuul.ZuulFilter#filterType()
*/
@Override
public String filterType() {
return "post";
}
/*
* (non-Javadoc)
*
* @see com.netflix.zuul.ZuulFilter#filterOrder()
*/
@Override
public int filterOrder() {
return -1; // Needs to run before SendErrorFilter which has filterOrder == 0
}
/*
* (non-Javadoc)
*
* @see com.netflix.zuul.IZuulFilter#shouldFilter()
*/
@Override
public boolean shouldFilter() {
// only forward to errorPath if it hasn't been forwarded to already
return RequestContext.getCurrentContext().containsKey("error.status_code");
}
/*
* (non-Javadoc)
*
* @see com.netflix.zuul.IZuulFilter#run()
*/
@Override
public Object run() {
try {
RequestContext ctx = RequestContext.getCurrentContext();
Object e = ctx.get("error.exception");
if (e != null && e instanceof ZuulException) {
ZuulException zuulException = (ZuulException) e;
logger.error("Zuul failure detected: " + zuulException.getMessage(), zuulException);
// Remove error code to prevent further error handling in follow up filters
ctx.remove("error.status_code");
} else {
error.setMessage(messageSource.getMessage(Constants.REQUESTED_SERVICE_UNAVAILABLE, new Object[] { zuulException.getCause() }, properties.getCurrentLocale()));
ctx.setResponseBody(mapper.writeValueAsString(error));
ctx.getResponse().setContentType("application/json");
ctx.setResponseStatusCode(500); // Can set any error code as excepted
}
}
} catch (Exception ex) {
logger.error("Exception filtering in custom error filter", ex);
ReflectionUtils.rethrowRuntimeException(ex);
}
return null;
}
}
我通过为 toLowerCase(Locale.ROOT) 选项创建扩展的 EurekaDiscoveryClient 解决了这个问题。
import com.netflix.appinfo.EurekaInstanceConfig;
import com.netflix.discovery.EurekaClient;
import com.netflix.discovery.shared.Application;
import com.netflix.discovery.shared.Applications;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.netflix.eureka.EurekaDiscoveryClient;
import org.springframework.stereotype.Component;
import java.util.*;
@Component
public class CustomEurekaDiscoveryClient extends EurekaDiscoveryClient implements DiscoveryClient {
private final EurekaInstanceConfig config;
private final EurekaClient eurekaClient;
public CustomEurekaDiscoveryClient(EurekaInstanceConfig config, @Qualifier("eurekaClient") EurekaClient eurekaClient) {
super(config, eurekaClient);
this.config = config;
this.eurekaClient = eurekaClient;
}
@Override
public List<String> getServices() {
Applications applications = this.eurekaClient.getApplications();
if (applications == null) {
return Collections.emptyList();
}
List<Application> registered = applications.getRegisteredApplications();
List<String> names = new ArrayList<>();
for (Application app : registered) {
if (app.getInstances().isEmpty()) {
continue;
}
names.add(app.getName().toLowerCase(Locale.ROOT));
}
return names;
}
}
大写 'I' 转换为小写 'ı' 而不是土耳其 OS 和 jvm 中的 'i' 这就是为什么我需要覆盖 public 列出 getServices() 方法。
如果 toLowerCase(Locale.ROOT) 方法调用时没有 Locale.ROOT java 将字符转换为 'ı' 但使用 Locale.ROOT 选项,方法转换为 'i' 并且项目工作正常。
可以在Zuul应用的main方法中为英文添加Locale.setDefault
public static void main(String[] args) {
Locale.setDefault(new Locale("en", "US"));
SpringApplication.run(ServerApplication.class, args);
}
它也解决了你的问题。