写一个用于提取HTTP请求行中Query字符串内存储的参数的Java Function

该博客介绍了如何使用Java原生方法解析HTTP请求中的Query参数,包括有序和无序提取,并详细讲解了`computeIfAbsent`方法的用法。同时,展示了如何处理GET和POST请求中的Query数据,以及使用HttpServer创建HTTP处理器的示例。
摘要由CSDN通过智能技术生成

首先,一般普通导包

import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListSet;

其次,静态导包

import static java.net.URLDecoder.decode;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Arrays.stream;
import static java.util.List.of;

最后,2段不同功效的function

static Map<String, Set<String>> extractQueryParams(String queryString, boolean isOrdered) {
	final Map<String, Set<String>> paramsMap;
	if ( isOrdered ) {
		//这是用于有序串行提取query字符串参数的,其中的LinkedHashMap和LinkedHashSet内的元素皆为按序存储
		paramsMap = new LinkedHashMap<>(32);
		stream(queryString.split("&"))
				.map(param -> param.split("="))
				.forEachOrdered(pair -> paramsMap.computeIfAbsent(decode(pair[0], UTF_8),
						k -> new LinkedHashSet<>(8)).add((pair.length > 1) ? decode(pair[1], UTF_8) : "")
				);
	}else {
		//这是用于无序并行提取query字符串参数的,其中ConcurrentHashMap并发控制使用 synchronized 和 CAS 来操作,精细程度更高(Hashtable使用的是全表锁,效率更低)
		paramsMap = new ConcurrentHashMap<>(32);
		of(queryString.split("&"))
				.parallelStream()
				.map(param -> param.split("="))
				.forEach(pair -> paramsMap.computeIfAbsent(decode(pair[0], UTF_8),
						k -> new ConcurrentSkipListSet<>()).add((pair.length > 1) ? decode(pair[1], UTF_8) : "")
				);
	}
	return paramsMap;
}

以上代码皆是纯原生Java实现,没有使用任何组件库

最后再提一嘴:

Java想要直接提取请求行中的Query字符串其实相当的简单:只需要在com.sun.net.httpserver.HttpHandler方法块里面,通过传入的HttpExchange参数,调用其自带的.getRequestURI(),就可以获取相应的 java.net.URI 实例对象。然后当然是直接调用URI对象的 .getQuery() 方法就可以返回Query的 String 了。
例如:

final var server = HttpServer.create(new InetSocketAddress("localhost", 8001), 0, "/test", exchange -> {
	switch (exchange.getRequestMethod()) {
		case "GET" -> {
			final var uri = exchange.getRequestURI();
			var stringBuilder = new StringBuilder(256);
			stringBuilder.append("GET:").append(System.lineSeparator())
					.append("\tPath:").append(uri.getPath())
					.append(",\tPort:").append(uri.getPort())
					.append(",\tHost:").append(uri.getHost())
					.append(",\tQuery:").append(uri.getQuery()).append(System.lineSeparator())
					.append("\tUserInfo:").append(uri.getUserInfo())
					.append(",\tFragment:").append(uri.getFragment())
					.append(",\tScheme:").append(uri.getScheme())
					.append(",\tAuthority:").append(uri.getAuthority()).append(System.lineSeparator());
			if ( uri.getQuery() != null ) {
				extractQueryParams(uri.getQuery()).forEach(
						(k, v) -> stringBuilder.append('\t').append(k).append(':').append(System.lineSeparator()).append("\t\t").append(v).append(System.lineSeparator())
				);
			}
			responseInfoOut(stringBuilder, exchange);
			try (final var outs = new BufferedOutputStream(exchange.getResponseBody())) {
				outs.write(response.getBytes(UTF_8));
			}
			println(stringBuilder.toString());
		}
		case "POST" -> {
			var stringBuilder = new StringBuilder(1024);
			final var uri = exchange.getRequestURI();
			stringBuilder.append("POST:").append(System.lineSeparator())
					.append("\tPath:").append(uri.getPath())
					.append(",\tPort:").append(uri.getPort())
					.append(",\tHost:").append(uri.getHost())
					.append(",\tQuery:").append(uri.getQuery()).append(System.lineSeparator())
					.append("\tUserInfo:").append(uri.getUserInfo())
					.append(",\tFragment:").append(uri.getFragment())
					.append(",\tScheme:").append(uri.getScheme())
					.append(",\tAuthority:").append(uri.getAuthority()).append(System.lineSeparator());
			if ( uri.getQuery() != null ) {
				extractQueryParams(uri.getQuery()).forEach(
						(k, v) -> stringBuilder.append('\t').append(k).append(':').append(System.lineSeparator()).append("\t\t").append(v).append(System.lineSeparator())
				);
			}
			switch (exchange.getRequestHeaders().get(CONTENT_TYPE).get(0)) {
				case "application/x-www-form-urlencoded" -> {
					try (final var ins = new BufferedInputStream(exchange.getRequestBody())
					) {
						extractQueryParams(new String(ins.readAllBytes(), UTF_8)).forEach(
								(k, v) -> stringBuilder.append('\t').append(k).append(':').append(System.lineSeparator()).append("\t\t").append(v).append(System.lineSeparator())
						);
					}
				}
				case "application/json" -> {
					try (final var ins = new BufferedInputStream(exchange.getRequestBody())
					) {
						stringBuilder.append(new String(ins.readAllBytes(), UTF_8));
					}
				}
				default -> stringBuilder.append(exchange.getRequestHeaders().get(CONTENT_TYPE));
			}
			responseInfoOut(stringBuilder, exchange);
			try (final var outs = new BufferedOutputStream(exchange.getResponseBody())) {
				outs.write(((response + "\n").repeat(8)).getBytes(UTF_8));
			}
			println(stringBuilder.toString());
		}
		case "PUT" -> {
			var stringBuilder = new StringBuilder(256)
										.append("PUT:").append(System.lineSeparator())
										.append('\t').append(CONTEXT_ATTRIBUTES).append(System.lineSeparator());
			exchange.getHttpContext().getAttributes().forEach(
					(k, v) -> stringBuilder.append("\t\t").append(k).append(':').append(v).append(System.lineSeparator())
			);
			stringBuilder.append(System.lineSeparator());
			exchange.sendResponseHeaders(200, 0);
			try (final var outs = new BufferedOutputStream(exchange.getResponseBody())) {
				outs.write((response + response).getBytes(UTF_8));
			}
			println(stringBuilder.toString());
		}
		case "DELETE" -> {
			var stringBuilder = new StringBuilder(128)
										.append("DELETE:").append(System.lineSeparator())
										.append('\t').append(CONTEXT_ATTRIBUTES).append(System.lineSeparator());
			exchange.getHttpContext().getAttributes().forEach(
					(k, v) -> stringBuilder.append("\t\t").append(k).append(':').append(v).append(System.lineSeparator())
			);
			stringBuilder.append(System.lineSeparator());
			exchange.sendResponseHeaders(200, 0);
			try (final var outs = new BufferedOutputStream(exchange.getResponseBody())) {
				outs.write("DELETE".getBytes(UTF_8));
			}
			println(stringBuilder.toString());
		}
		default -> {
			exchange.sendResponseHeaders(200, 0);
			exchange.getResponseBody().close();
		}
	}
}, Filter.adaptRequest("Add Foo header.", (Request r) -> r.with("Foo", of("Bar"))), new Filter() {
	@Override
	public void doFilter(HttpExchange exchange, Chain chain) throws IOException {
		exchange.getResponseHeaders().add("Accept-Charset", "UTF-8");
		exchange.getResponseHeaders().add(CONTENT_TYPE, "text/txt;Charset=UTF-8");
		println("Filter 起作用啦!!!");
		chain.doFilter(exchange);
	}

	@Override
	public String description() {
		return "Add response header";
	}
});

关于 omputeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) 方法,这里官方的解释是:

If the specified key is not already associated with a value (or is mapped to null), attempts to compute its value using the given mapping function and enters it into this map unless null.
If the mapping function returns null, no mapping is recorded. If the mapping function itself throws an (unchecked) exception, the exception is rethrown, and no mapping is recorded. The most common usage is to construct a new object serving as an initial mapped value or memoized result, as in:
map.computeIfAbsent(key, k -> new Value(f(k)));
Or to implement a multi-value map, Map<K,Collection>, supporting multiple values per key:
map.computeIfAbsent(key, k -> new HashSet()).add(v);
The mapping function should not modify this map during computation.
This method will, on a best-effort basis, throw a ConcurrentModificationException if it is detected that the mapping function modifies this map during computation.
抛出:
ConcurrentModificationException – if it is detected that the mapping function modified this map

翻译:

如果指定的键尚未与值关联(或映射到null ),则尝试使用给定的映射函数计算其值并将其输入到此映射中,除非为null 。
如果映射函数返回null ,则不记录映射。如果映射函数本身抛出(未经检查的)异常,则重新抛出异常,并且不记录映射。最常见的用法是构造一个新对象作为初始映射值或记忆结果,如下所示:
map.computeIfAbsent(key, k -> new Value(f(k)));
或者实现一个多值映射, Map < K , Collection < V >> ,每个键支持多个值:
map.computeIfAbsent(key, k -> new HashSet()).add(v);
映射函数不应在计算期间修改此映射。
如果检测到映射函数在计算期间修改了此映射,则此方法将尽最大努力抛出ConcurrentModificationException 。
问:
接口Map中的computeIfAbsent
参形:
key – 与指定值关联的键 mappingFunction – 计算值的映射函数
返回值:
与指定键关联的当前(现有或计算的)值,如果计算的值为 null,则为 null
抛出:
ConcurrentModificationException – 如果检测到映射函数修改了此映射

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

被遗忘的DIA

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值