This is the seventh day of my participation in the Challenge. For details, see:More article challenges
Set up a Flag and write something every day and stick to it.
Custom gateways in the framework
Background:
Some company projects use their own project framework, gateway for some custom implementation. How to implement a custom gateway, and what functions? This article will cover some superficial explanations.
Set up a project hand-gateway and register with the NACOS registry
Project depend on
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
Copy the code
Configure the RouteInterceptor
Route mappings can be loaded from the configuration file, configuration center, or database, translated, and forwarded through the RestTemplate, which uses loadBalance to balance the load. The following code to achieve the Post, GET method request, JSON request has not yet been implemented, interested in can try.
@Slf4j
@Component
public class RouteInterceptor implements HandlerInterceptor {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DiscoveryClient discoveryClient;
@Override
public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpHeaders httpHeaders = new HttpHeaders();
String requestUrl = request.getRequestURI();
if(StrUtil.isNotEmpty(request.getQueryString())){
String query = URLDecoder.decode(request.getQueryString(),"UTF-8");
requestUrl =requestUrl+"?"+query;
}
log.info("requestUrl {}",requestUrl);
// The registry gets registered services
// List
instances = discoveryClient.getInstances("paw-dogs-sky-service");
// String ipAddr = instances.get(0).getUri().toString();
// Load route mapping such as configuration file, configuration center, Redis, database
Map<String,String> routerMap = new HashMap<>(16);
routerMap.put("/sky-api"."paw-dogs-sky-service");
String routerKey = requestUrl;
if(requestUrl.indexOf("/".1) >0){
routerKey = requestUrl.substring(0,requestUrl.indexOf("/".1));
}
if(routerMap.containsKey(routerKey)){
String serverName = routerMap.get(routerKey);
// Do address mapping
String targetUrl = "http://"+requestUrl.replaceFirst(routerKey,serverName);
log.info("requestUrl {} ==> targetUrl {}",requestUrl, targetUrl);
ResponseEntity<String> responseEntity;
if("POST".equalsIgnoreCase(request.getMethod())){
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
Map<String, String[]> paramMap = request.getParameterMap();
for (String paramName: paramMap.keySet()) {
params.addAll(paramName, Arrays.asList(paramMap.get(paramName)));
}
httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<Object> requestEntity = new HttpEntity<>(params, httpHeaders);
responseEntity = restTemplate.postForEntity(targetUrl, requestEntity, String.class);
}else{
responseEntity = restTemplate.getForEntity(targetUrl, String.class);
}
try {
response.setStatus(responseEntity.getStatusCodeValue());
String responseBody = responseEntity.getBody();
response.setContentType("application/json; charset=UTF-8");
PrintWriter out = response.getWriter();
out.print(responseBody);
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
return true;
}
@Override
public void postHandle (HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion (HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex); }}Copy the code
Add a permission verification interceptor TokenInterceptor. After obtaining the token, it verifies the permission according to the authentication mode JWT or Redis, and puts some information of the user into the header for further propagation.
@Slf4j
@Component
public class TokenInterceptor implements HandlerInterceptor {
public static final String TOKEN_NAME = "token";
@Override
public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("TokenInterceptor requestUrl "+request.getRequestURI());
String token = request.getHeader(TOKEN_NAME);
if(StrUtil.isEmpty(token)){
token = request.getParameter(TOKEN_NAME);
}
// Check token JWT or Redis
if(StrUtil.isNotEmpty(token)){
return true;
}
try {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.setContentType("application/json; charset=UTF-8");
PrintWriter out = response.getWriter();
out.print(HttpStatus.UNAUTHORIZED.getReasonPhrase());
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
return false; }}Copy the code
To configure services, run the RestTemplate command to add @LoadBalanced annotation for load balancing, and add interceptors in the correct order.
@Configuration
public class WebConfiguration implements WebMvcConfigurer {
@Autowired
private TokenInterceptor tokenInterceptor;
@Autowired
private RouteInterceptor routeInterceptor;
@Bean
@LoadBalanced
@ConditionalOnClass(RestTemplate.class)
public RestTemplate restTemplate(a){
return new RestTemplate();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(tokenInterceptor).addPathPatterns("/ * *").excludePathPatterns("/anonymous/**");
registry.addInterceptor(routeInterceptor).addPathPatterns("/ * *");
}
Copy the code
This completes the simple gateway project.
Set up the application service Sky-service and register it with nacOS registry
The service name
spring:
application:
name: paw-dogs-sky-service
Copy the code
Service interface, add port view load balancing, example to achieve the GET POST method
@RestController
public class SkyController {
@Value("${spring.application.name:sky-service}")
private String applicationName;
@Value("${server.port:8080}")
private Integer serverPort;
@GetMapping("/sky")
public String sky (@RequestParam(required = false) String name) {
String msg = "this is " + applicationName + " port " + serverPort + " Time: " + DateUtil.now();
if (StrUtil.isNotEmpty(name)) {
msg = msg + " name: " + name;
}
return msg;
}
@PostMapping("/deliver")
public String deliver (String packageBox) {
String msg = "this is " + applicationName + " port " + serverPort + " Time: " + DateUtil.now();
return msg + " delivered packageBox: "+ packageBox; }}Copy the code
Set VM options to -dserver. port=8081
To access the gateway project through Postman, the header carries the token
http://127.0.0.1:8080/sky-api/sky?name=fly
Returns the result
this is paw-dogs-sky-service port 8082 Time: 2021-06-17 16: 45: 05 name: fly
To access a Post request, header carries a token, form-urlencoded parameter packageBox=cake
http://127.0.0.1:8080/sky-api/deliver
Returns the result
this is paw-dogs-sky-service port 8082 Time: 2021-06-17 16: 11: 23 delivered packageBox: cake
If you access the port for several times, you will find that the port is switched between 8081 and 8082, and load balancing is implemented.
At this point a custom gateway project completed a prototype, can according to the project actual needs custom other content, increase the interceptor to implement different business, such as add signature validation, routing map can also do more complex business, whether you need such as interface signature verification, unified content such as increased business again in the header, the current limit increase interface, etc.
Conclusion:
The custom gateway is a Spring-boot – Web project. It performs route forwarding based on RestTemplate, implements load balancing through loadBalance, and implements route mapping, token permission verification and other business content through interceptor.