normalize static method

String normalize(
  1. String url
)

Produces a normalized version of a full URL string by applying various standardizations:

  • Converts scheme and host to lowercase
  • Removes default ports (80 for HTTP, 443 for HTTPS)
  • Resolves path segments (. and ..)
  • Reduces multiple slashes
  • Removes trailing slashes for non-root paths
  • Sorts and re-encodes query parameters
  • Re-encodes fragment
  • Preserves user info

Example:

final url = 'HTTP://Example.com:80/users?id=2&name=John';
final normalized = UriTemplate.normalize(url);
print(normalized); // http://example.com/users?id=2&name=John

Implementation

static String normalize(String url) {
  Uri uri = Uri.parse(url);

  // Scheme normalization: lowercase
  String normalizedScheme = uri.scheme.toLowerCase();

  // Host normalization: lowercase and remove trailing dot
  String normalizedHost = uri.host.toLowerCase();
  if (normalizedHost.isNotEmpty && normalizedHost.endsWith('.')) {
    normalizedHost = normalizedHost.substring(0, normalizedHost.length - 1);
  }

  // Port normalization: remove default ports
  int? normalizedPort = uri.port;
  if ((normalizedScheme == 'http' && normalizedPort == 80) ||
      (normalizedScheme == 'https' && normalizedPort == 443)) {
    normalizedPort = null; // Indicate default port, so it's not included in the URI string
  }

  // Path normalization: resolve . and .., reduce multiple slashes, remove trailing slash for non-root
  String normalizedPath = uri.path;
  if (normalizedPath.isEmpty) {
    normalizedPath = '/'; // Empty path becomes root path
  } else {
    // Use Uri.parse().normalizePath() to resolve . and .. and handle multiple slashes.
    // This also handles percent-encoding for path segments.
    normalizedPath = normalizePath(normalizedPath);

    // Remove trailing slash if it's not the root path
    if (normalizedPath.length > 1 && normalizedPath.endsWith('/')) {
      normalizedPath = normalizedPath.substring(0, normalizedPath.length - 1);
    }
  }

  // Query normalization: sort parameters by key, then by value, and re-encode
  Map<String, List<String>> queryParams = uri.queryParametersAll;
  List<String> sortedKeys = queryParams.keys.toList()..sort();
  List<String> normalizedQuerySegments = [];

  for (String key in sortedKeys) {
    List<String> values = List.from(queryParams[key]!)..sort(); // Sort values for each key
    String encodedKey = Uri.encodeQueryComponent(key);
    for (String value in values) {
      String encodedValue = Uri.encodeQueryComponent(value);
      normalizedQuerySegments.add('$encodedKey=$encodedValue');
    }
  }
  String? normalizedQuery = normalizedQuerySegments.isEmpty ? null : normalizedQuerySegments.join('&');

  // Fragment normalization: re-encode
  String? normalizedFragment = uri.fragment.isEmpty ? null : Uri.encodeComponent(uri.fragment);

  // Reconstruct the URI
  Uri resultUri = Uri(
    scheme: normalizedScheme,
    userInfo: uri.userInfo.isEmpty ? null : uri.userInfo, // Preserve user info if present
    host: normalizedHost,
    port: normalizedPort,
    path: normalizedPath,
    query: normalizedQuery,
    fragment: normalizedFragment,
  );

  return resultUri.toString();
}