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.