[Technical Application] Java implements third-party interface proxy

I. Introduction

We often encounter the service http interface proxy function in our daily work. We often use nginx for static resource proxy and http request proxy. However, in some business scenarios, when forwarding proxy http requests, request header settings will be added, such as setting to token鉴权request , used to request third-party interfaces;

On request请求the basis of , add request headers, convert parameters or convert return values ​​to implement third-party interface proxy functions while avoiding the need for coding for each interface;

2. Ideas

There are two ideas to deal with this problem:
1) Use the characteristics of the gateway to implement the proxy function, which is often involved in microservices. The most commonly used method is to implement gateway configuration through yml configuration files; in fact, it is implemented
through 拦截器filterrequests Intercept, re-encapsulate request请求and then forward;
2) Implement an action to obtain a certain 公用前缀, 所有请求re-encapsulate and then call a third-party request;

3. Preparation work

Simulate three-party http interface:

package com.example.test01;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;

@RestController
public class TestAction {
    
    

    @GetMapping("/test/abc")
    public void test(HttpServletRequest request){
    
    
        Enumeration<String> enumeration =  request.getHeaderNames();
        System.out.println(enumeration.nextElement());
        String auth = request.getHeader("token");
        System.out.println("========================token:"+auth);
        System.out.println("========================Authentication:"+request.getHeader("Authentication"));
        System.out.println("========================test:"+request.getHeader("test"));

    }

}

Agent pom.xmlfiles

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.6</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.sk</groupId>
    <artifactId>springTest</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springTest</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>2021.0.5</spring-cloud.version>
    </properties>
    <dependencies>
        <!--<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-web</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-web</artifactId>
                </exclusion>
            </exclusions>
        </dependency>-->

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!--<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>-->
        <!--<dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-test</artifactId>
            <scope>test</scope>
        </dependency>-->

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${
    
    spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

4. Gateway implements dynamic proxy (code example)

1. Static code configuration

Note: Dynamic request header cannot be set

spring:
  application:
    name: gateway-application
  cloud:
    # Spring Cloud Gateway 配置项,对应 GatewayProperties 类
    gateway:
      # 路由配置项,对应 RouteDefinition 数组
      routes:
        - id: test01# 路由的编号
          uri: https://127.0.0.1:8089 # 路由到的目标地址,只能到域名部分
          predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
            - Path=/test
          filters:
            - StripPrefix=1
            - AddRequestHeader=token, 1234567
            - setRequestHeader=Authentication, 321654

2. Gateway implements dynamic encapsulation of request requests

1) Example 1

package com.study;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class SpringTestApplication {
    
    

    @Bean
     public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
    
    
        RouteLocatorBuilder.Builder routes = builder.routes();
        routes.route("route-test",
                r -> r.path("/test/**").filters(f->f.addRequestHeader("token","123456")
                        .setRequestHeader("Authentication","9999999")
                        .addRequestHeader("test","232333")).
                        uri("http://127.0.0.1:8089")
        );
        // addRequestHeader和setRequestHeader区别
        return routes.build();
     }
    public static void main(String[] args) {
    
    
        SpringApplication.run(SpringTestApplication.class, args);
    }

}

Results of the:

========================token:123456
========================Authentication:9999999
========================test:232333

2) Example 2

RequestFilter class:

package com.study.config;

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
public class RequestFilter implements GatewayFilter, Ordered {
    
    

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    
    
        ServerHttpRequest request = exchange.getRequest().mutate()
                .header("test", "hahahaha")
                .header("token", "123456")
                .header("Authentication", "654321")
                .build();
        return chain.filter(exchange.mutate().request(request).build());
    }

    @Override
    public int getOrder() {
    
    
        return 0;
    }
}

RouteConfiguration class:

package com.study.config;

import lombok.RequiredArgsConstructor;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@RequiredArgsConstructor
@Configuration
public class RouteConfiguration {
    
    

    private final RequestFilter requestFilter;

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
    
    

        RouteLocatorBuilder.Builder routes = builder.routes();
        routes.route("route-test",
                r -> r.path("/test/**").filters(f->f.filters(requestFilter)).
                        uri("http://127.0.0.1:8089")
        );
        // addRequestHeader和setRequestHeader区别
        return routes.build();
    }

}

Results of the:

========================token:123456
========================Authentication:654321
========================test:hahahaha

5. http interface implementation (code example)

package com.study.controller;

import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

@RestController
public class ProxyController {
    
    

    private String targetAddr = "http://127.0.0.1:8089";

    /**
     * 代理所有请求
     *
     * @param request
     * @param response
     * @throws Exception
     */
    @RequestMapping(value = "/proxy/**", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public void proxy(HttpServletRequest request, HttpServletResponse response) throws IOException, URISyntaxException {
    
    
        // String url = URLDecoder.decode(request.getRequestURL().toString(), "UTF-8");
        URI uri = new URI(request.getRequestURI());
        String path = uri.getPath();
        String query = request.getQueryString();
        String target = targetAddr + path.replace("/proxy", "");
        if (query != null && !query.equals("") && !query.equals("null")) {
    
    
            target = target + "?" + query;
        }
        URI newUri = new URI(target);
        // 执行代理查询
        String methodName = request.getMethod();
        HttpMethod httpMethod = HttpMethod.resolve(methodName);
        if (httpMethod == null) {
    
    
            return;
        }
        ClientHttpRequest delegate = new SimpleClientHttpRequestFactory().createRequest(newUri, httpMethod);
        Enumeration<String> headerNames = request.getHeaderNames();
        // 设置请求头
        while (headerNames.hasMoreElements()) {
    
    
            String headerName = headerNames.nextElement();
            Enumeration<String> v = request.getHeaders(headerName);
            List<String> arr = new ArrayList<>();
            while (v.hasMoreElements()) {
    
    
                arr.add(v.nextElement());
            }
            delegate.getHeaders().addAll(headerName, arr);
        }

        delegate.getHeaders().add("Authentication","1234567");
        delegate.getHeaders().add("token","2222222222");
        delegate.getHeaders().add("test","3333333333333");

        StreamUtils.copy(request.getInputStream(), delegate.getBody());
        // 执行远程调用
        ClientHttpResponse clientHttpResponse = delegate.execute();
        response.setStatus(clientHttpResponse.getStatusCode().value());
        // 设置响应头
        clientHttpResponse.getHeaders().forEach((key, value) -> value.forEach(it -> {
    
    
            response.setHeader(key, it);
        }));
        StreamUtils.copy(clientHttpResponse.getBody(), response.getOutputStream());
    }

}

Results of the:

========================token:2222222222
========================Authentication:1234567
========================test:3333333333333

6. Summary

nginxA better component to implement the proxy function also depends on its use 多路复用功能. All Java implementations http接口代理功能can also be nettyimplemented using it. This will be implemented later~~~

Guess you like

Origin blog.csdn.net/weixin_37598243/article/details/128309580