Do You Really Know How to Define baseAddress with HttpClient?

When using <span>HttpClient</span> to define <span>baseAddress</span>, the core rule is: <span>baseAddress</span> must be a valid absolute URI (including protocol, host/IP, port), and may include a base path (which must end with <span>/</span>), but must not contain query parameters (<span>?</span>), anchors (<span>#</span>), or redundant path segments. It is inaccurate to say “only IP and port are allowed”—the correct requirement is “no additional content that interferes with URI concatenation,” rather than completely disallowing paths.

Do You Really Know How to Define baseAddress with HttpClient?

1. First, clarify: <span>baseAddress</span> must have a valid format

<span>baseAddress</span> must comply with the absolute URI specification, with the core components being: <span>protocol://IP/domain[:port]/[optional base path/]</span> (Key: if a path exists, it must end with <span>/</span>, otherwise concatenating relative paths will result in errors).

Valid examples (recommended):

// Only IP+port (with protocol, no path)
var baseAddress1 = new Uri("http://192.168.1.100:8080/"); 

// Domain name + default port (https defaults to 443, http defaults to 80)
var baseAddress2 = new Uri("https://api.example.com/"); 

// Includes base path (must end with /)
var baseAddress3 = new Uri("http://192.168.1.100:8080/api/v1/"); 

Invalid examples (will throw errors or concatenate incorrectly):

// 1. Missing protocol (http/https) → throws UriFormatException
var badBase1 = new Uri("192.168.1.100:8080"); 

// 2. Contains query parameters → will overwrite subsequent parameters during concatenation, causing logical errors
var badBase2 = new Uri("http://192.168.1.100:8080?key=123"); 

// 3. Contains anchors → invalid, anchors only affect the client in URI specifications, the server does not recognize them
var badBase3 = new Uri("http://192.168.1.100:8080#part1"); 

// 4. Base path not ending with / → will "replace" the last segment of the path when concatenating relative paths
var badBase4 = new Uri("http://192.168.1.100:8080/api/v1"); 

2. Why is it not recommended to add “extra content”? — <span>HttpClient</span> URI concatenation rules

<span>HttpClient</span> concatenates request addresses as follows: <span>baseAddress + relative path</span>, but the rules are strict, and extra content can lead to unexpected results:

Key rules:

  1. If <span>baseAddress</span> path ends with <span>/</span> → the relative path is directly concatenated (correct);
  2. If <span>baseAddress</span> path does not end with <span>/</span> → the relative path will replace the last segment of the <span>baseAddress</span> path (incorrect);
  3. <span>baseAddress</span> query parameters/anchors will be retained, but subsequent request parameters will overlap or overwrite, causing logical confusion.

Counterexample demonstration (concatenation error):

// Incorrect baseAddress (path does not end with /)
var baseAddress = new Uri("http://192.168.1.100:8080/api/v1"); 

using var client = new HttpClient { BaseAddress = baseAddress };

// Expected request: http://192.168.1.100:8080/api/v1/user/1
// Actual request: http://192.168.1.100:8080/api/user/1 (v1 was replaced!)
var response = await client.GetAsync("user/1"); 

Correct example demonstration (correct concatenation):

// Correct baseAddress (path ends with /)
var baseAddress = new Uri("http://192.168.1.100:8080/api/v1/"); 

using var client = new HttpClient { BaseAddress = baseAddress };

// Actual request: http://192.168.1.100:8080/api/v1/user/1 (as expected)
var response = await client.GetAsync("user/1"); 

// Request with parameters (parameters placed in the relative path, not in baseAddress)
// Actual request: http://192.168.1.100:8080/api/v1/user?name=test
var response2 = await client.GetAsync("user?name=test"); 

3. Practical suggestions (avoid pitfalls)

  1. Must include protocol: <span>http://</span> or <span>https://</span> cannot be omitted, otherwise it will throw a <span>UriFormatException</span>;
  2. Port is optional: when using default ports (http:80, https:443), it can be omitted; non-default ports must be explicitly specified;
  3. Path must end with <span>/</span>: even if there is no base path, it is recommended to add <span>/</span> (e.g., <span>http://192.168.1.100:8080/</span>), to avoid concatenation risks;
  4. Parameters/dynamic paths should be included in requests: query parameters (<span>?key=value</span>), dynamic path segments (e.g., <span>user/1</span>) should not be included in <span>baseAddress</span>, but passed through <span>GetAsync</span>/<span>PostAsync</span> relative paths;
  5. Use <span>Uri</span> for complex paths: if the relative path contains special characters (e.g., Chinese, spaces), it is recommended to explicitly concatenate using <span>new Uri(baseAddress, relativePath)</span>, which automatically encodes:
    var baseAddress = new Uri("http://192.168.1.100:8080/api/v1/");
    var relativeUri = new Uri(baseAddress, "user/张三?age=20"); 
    // Result: http://192.168.1.100:8080/api/v1/user/%E5%BC%A0%E4%B8%89?age=20 (automatically encoded)
    

4. Common issue troubleshooting

  1. Error <span>UriFormatException</span>: Check if the protocol (http/https) is missing, or if the IP/domain format is incorrect (e.g., too many colons);
  2. Request address concatenation error: Check if <span>baseAddress</span> ends with <span>/</span>, and if it contains extra path segments;
  3. Parameters not effective: Check if <span>baseAddress</span> contains query parameters (<span>?</span>), if so, remove them and move the parameters to the relative path of the request.

Conclusion

<span>baseAddress</span> has the core requirement of “absolute URI + no interfering concatenation content“, rather than “only IP and port”:

  • Allowed to include: protocol, IP/domain, port, base path (must end with <span>/</span>);
  • Prohibited from including: query parameters (<span>?</span>), anchors (<span>#</span>), redundant path segments;
  • Best practice: keep <span>baseAddress</span> concise, containing only “protocol + IP/domain + port + base path (if any)”, with dynamic content (parameters, specific paths) passed in during requests.

Leave a Comment