searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

用springboot搭建http转发服务器

2024-01-02 02:13:55
112
0

说到http转发,很容易想到用Nginx,因为Nginx性能好,配置简单。但如果希望能动态配置路由规则,或者在转发时增加自己的逻辑,使用Nginx就比较难实现。

本文将讲述如何使用springboot来搭建一个http转发服务器,实现http请求的动态转发。

实现http转发服务器,需要两个东西:

1)请求转发器,负责接收请求以及转发请求

2)转发规则

 

先说转发规则,转发规则可以用一张表(proxy_route)来储存,主要字段如下:

1)prefix,请求前缀

2)targetUrl,转发地址

3)description,规则说明

 

录好转发规则后,后面直接上转发器的代码

@RestController
@RequestMapping(ProxyController.PROXY_PREFIX)
public class ProxyController {
	public final static String PROXY_PREFIX = "/proxy";

	@Autowired
	private ServletContext servletContext;

	@Autowired
	private IProxyService proxyService;

        // 待转发的请求总入口
	@RequestMapping(value = "/**")
	public ResponseEntity<?> catchAll(HttpServletRequest request, HttpServletResponse response) {
		// String tenantId = 从session中获取租户ID;

		String prefix = "/" + servletContext.getContextPath() + "/" + PROXY_PREFIX;
		prefix = prefix.replaceAll("/+", "/");
		// 获取 /** 内容
		String uri = request.getRequestURI();
		String targetPath = uri.substring(prefix.length());
		return proxyService.redirect(tenantId, request, response, targetPath);
	}
}

 

@Service
public class ProxyServiceImpl implements IProxyService {

	private RestTemplate restTemplate;

	@PostConstruct
	private void init() {
		restTemplate = new RestTemplate();
		restTemplate.getMessageConverters().add(new ByteArrayHttpMessageConverter());
	}

	@Override
	public ResponseEntity<?> redirect(String tenantId, HttpServletRequest request, HttpServletResponse response,
			String targetPath) {
		String redirectUrl = null;
		try {
			// build up the redirect URL
			redirectUrl = createRedictUrl(tenantId, request, targetPath);
			RequestEntity<?> requestEntity = createRequestEntity(request, redirectUrl);
			return route(requestEntity);
		} catch (ProxyRouteNotFoundException e) {
			log.error("找不到符合的转发路由:" + targetPath, e);
			return new ResponseEntity<String>("Proxy Route Not Found", HttpStatus.BAD_REQUEST);
		} catch (Exception e) {
			log.error("请求转发[" + targetPath + "]失败", e);
			return new ResponseEntity<String>("REDIRECT ERROR: " + e.getMessage() + ", URL: " + redirectUrl,
					HttpStatus.INTERNAL_SERVER_ERROR);
		}
	}

	private String createRedictUrl(String tenantId, HttpServletRequest request, String targetPath) {
		String queryString = request.getQueryString();
		String targetUrl = getTargetUrl(tenantId, targetPath);
		return targetUrl + (queryString != null ? "?" + queryString : "");
	}

	private String getTargetUrl(String tenantId, String targetPath) {
		// 寻找匹配的转发规则
	}

	private RequestEntity<?> createRequestEntity(HttpServletRequest request, String url)
			throws URISyntaxException, IOException {
		String method = request.getMethod();
		HttpMethod httpMethod = HttpMethod.resolve(method);
		MultiValueMap<String, String> headers = parseRequestHeader(request);
		Object body = parseRequestBody(request);
		return new RequestEntity<>(body, headers, httpMethod, new URI(url));
	}

	private ResponseEntity<?> route(RequestEntity<?> requestEntity) {
		RestTemplate restTemplate = new RestTemplate();
		ResponseEntity<byte[]> response = restTemplate.exchange(requestEntity, byte[].class);
		return response;
	}

	private Object parseRequestBody(HttpServletRequest request) throws IOException {
		InputStream inputStream = request.getInputStream();
		return StreamUtils.copyToByteArray(inputStream);
	}

	private MultiValueMap<String, String> parseRequestHeader(HttpServletRequest request) throws IOException {
		HttpHeaders headers = new HttpHeaders();
		List<String> headerNames = Collections.list(request.getHeaderNames());
		// 保留旧的请求头
		for (String headerName : headerNames) {
			List<String> headerValues = Collections.list(request.getHeaders(headerName));
			for (String headerValue : headerValues) {
				headers.add(headerName, headerValue);
			}
		}
		return headers;
	}
}

 

如果有上传文件的请求转发需求,则需要修改下,其中MultipartFileResource类是spring 5.1以上才有,5.1以下的项目可以直接把这个类拷到自己项目中使用。

        private Object parseRequestBody(HttpServletRequest request) throws IOException {
		// 支持文件上传
		if (request instanceof StandardMultipartHttpServletRequest) {
			StandardMultipartHttpServletRequest fileReq = (StandardMultipartHttpServletRequest) request;
			MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>();
			for (String key : fileReq.getFileMap().keySet()) {
				parts.add(key, new MultipartFileResource(fileReq.getFile(key)));
			}
			return parts;
		}
		InputStream inputStream = request.getInputStream();
		return StreamUtils.copyToByteArray(inputStream);
	}

 

上述http请求转发器,虽然性能比不上Nginx,但可以动态实现http请求的转发,也方便加入自己的业务逻辑。

0条评论
0 / 1000