Revamp docs on Client instances (#836)
* Revamp docs on Client instances * Apply suggestions from code review Co-Authored-By: Hugo van Kemenade <hugovk@users.noreply.github.com> * Reword 'Why use a Client?' section * Apply suggestions from code review Co-Authored-By: Hugo van Kemenade <hugovk@users.noreply.github.com> Co-authored-by: Hugo van Kemenade <hugovk@users.noreply.github.com>
This commit is contained in:
parent
112fb9c07d
commit
7256366ba9
160
docs/advanced.md
160
docs/advanced.md
@ -2,22 +2,60 @@
|
||||
|
||||
## Client Instances
|
||||
|
||||
Using a Client instance to make requests will give you HTTP connection pooling,
|
||||
will provide cookie persistence, and allows you to apply configuration across
|
||||
all outgoing requests.
|
||||
|
||||
!!! hint
|
||||
A Client instance is equivalent to a Session instance in `requests`.
|
||||
If you are coming from Requests, `httpx.Client()` is what you can use instead of `requests.Session()`.
|
||||
|
||||
!!! note
|
||||
Starting from `httpx==0.10.0`, the default and recommended Client class is `AsyncClient`. This should help prevent breaking changes once sync support is reintroduced.
|
||||
### Why use a Client?
|
||||
|
||||
A `Client` synonym remains for compatibility with `httpx==0.9.*` releases, but you are encouraged to migrate to `AsyncClient` as soon as possible.
|
||||
!!! note "TL;DR"
|
||||
If you do anything more than experimentation, one-off scripts, or prototypes, then you should use a `Client` instance.
|
||||
|
||||
#### More efficient usage of network resources
|
||||
|
||||
When you make requests using the top-level API as documented in the [Quickstart](/quickstart) guide, HTTPX has to establish a new connection _for every single request_ (connections are not reused). As the number of requests to a host increases, this quickly becomes inefficient.
|
||||
|
||||
On the other hand, a `Client` instance uses [HTTP connection pooling](https://en.wikipedia.org/wiki/HTTP_persistent_connection). This means that when you make several requests to the same host, the `Client` will reuse the underlying TCP connection, instead of recreating one for every single request.
|
||||
|
||||
This can bring **significant performance improvements** compared to using the top-level API, including:
|
||||
|
||||
- Reduced latency across requests (no handshaking).
|
||||
- Reduced CPU usage and round-trips.
|
||||
- Reduced network congestion.
|
||||
|
||||
#### Extra features
|
||||
|
||||
`Client` instances also support features that aren't available at the top-level API, such as:
|
||||
|
||||
- Cookie persistance across requests.
|
||||
- Applying configuration across all outgoing requests.
|
||||
- Sending requests through HTTP proxies.
|
||||
- Using [HTTP/2](/http2).
|
||||
|
||||
The other sections on this page go into further detail about what you can do with a `Client` instance.
|
||||
|
||||
### Usage
|
||||
|
||||
The recommended way to use a `Client` is as a context manager. This will ensure that connections are properly cleaned up when leaving the `with` block:
|
||||
|
||||
```python
|
||||
with httpx.Client() as client:
|
||||
...
|
||||
```
|
||||
|
||||
Alternatively, you can explicitly close the connection pool without block-usage using `.close()`:
|
||||
|
||||
```python
|
||||
client = httpx.Client()
|
||||
try:
|
||||
...
|
||||
finally:
|
||||
client.close()
|
||||
```
|
||||
|
||||
### Making requests
|
||||
|
||||
Once you have a `Client`, you can send requests using `.get()`, `.post()`, etc. For example:
|
||||
|
||||
```python
|
||||
>>> with httpx.Client() as client:
|
||||
... r = client.get('https://example.com')
|
||||
@ -26,26 +64,24 @@ The recommended way to use a `Client` is as a context manager. This will ensure
|
||||
<Response [200 OK]>
|
||||
```
|
||||
|
||||
Alternatively, you can explicitly close the connection pool without block-usage using `.close()`:
|
||||
These methods accept the same arguments as `httpx.get()`, `httpx.post()`, etc. This means that all features documented in the [Quickstart](/quickstart) guide are also available at the client level.
|
||||
|
||||
For example, to send a request with custom headers:
|
||||
|
||||
```python
|
||||
>>> client = httpx.Client()
|
||||
>>> try:
|
||||
... r = client.get('https://example.com')
|
||||
... finally:
|
||||
... client.close()
|
||||
>>> with httpx.Client() as client:
|
||||
... headers = {'X-Custom': 'value'}
|
||||
... r = client.get('https://example.com', headers=headers)
|
||||
...
|
||||
>>> r
|
||||
<Response [200 OK]>
|
||||
>>> r.request.headers['X-Custom']
|
||||
'value'
|
||||
```
|
||||
|
||||
Once you have a `Client`, you can use all the features documented in the [Quickstart](/quickstart) guide.
|
||||
|
||||
### Configuration
|
||||
### Sharing configuration across requests
|
||||
|
||||
Clients allow you to apply configuration to all outgoing requests by passing parameters to the `Client` constructor.
|
||||
|
||||
For example, to apply a set of custom headers on every request:
|
||||
For example, to apply a set of custom headers _on every request_:
|
||||
|
||||
```python
|
||||
>>> url = 'http://httpbin.org/headers'
|
||||
@ -57,15 +93,47 @@ For example, to apply a set of custom headers on every request:
|
||||
'my-app/0.0.1'
|
||||
```
|
||||
|
||||
!!! note
|
||||
When you provide a parameter at both the client and request levels, one of two things can happen:
|
||||
### Merging of configuration
|
||||
|
||||
- For headers, query parameters and cookies, the values are merged into one.
|
||||
- For all other parameters, the request-level value is used.
|
||||
When a configuration option is provided at both the client-level and request-level, one of two things can happen:
|
||||
|
||||
Additionally, `Client` accepts some parameters that aren't available at the request level.
|
||||
- For headers, query parameters and cookies, the values are combined together. For example:
|
||||
|
||||
One particularly useful parameter is `base_url`, which allows you to define a base URL to prepend to all outgoing requests:
|
||||
```python
|
||||
>>> headers = {'X-Auth': 'from-client'}
|
||||
>>> params = {'client_id': 'client1'}
|
||||
>>> with httpx.Client(headers=headers, params=params) as client:
|
||||
... headers = {'X-Custom': 'from-request'}
|
||||
... params = {'request_id': 'request1'}
|
||||
... r = client.get('https://example.com', headers=headers, params=params)
|
||||
...
|
||||
>>> r.request.url
|
||||
URL('https://example.com?client_id=client1&request_id=request1')
|
||||
>>> r.request.headers['X-Auth']
|
||||
'from-client'
|
||||
>>> r.request.headers['X-Custom']
|
||||
'from-request'
|
||||
```
|
||||
|
||||
- For all other parameters, the request-level value takes priority. For example:
|
||||
|
||||
```python
|
||||
>>> with httpx.Client(auth=('tom', 'mot123')) as client:
|
||||
... r = client.get('https://example.com', auth=('alice', 'ecila123'))
|
||||
...
|
||||
>>> _, _, auth = r.request.headers['Authorization'].partition(' ')
|
||||
>>> import base64
|
||||
>>> base64.b64decode(auth)
|
||||
b'alice:ecila123'
|
||||
```
|
||||
|
||||
If you need finer-grained control on the merging of client-level and request-level parameters, see [Request instances](#request-instances).
|
||||
|
||||
### Other Client-only configuration options
|
||||
|
||||
Additionally, `Client` accepts some configuration options that aren't available at the request level.
|
||||
|
||||
For example, `base_url` allows you to prepend an URL to all outgoing requests:
|
||||
|
||||
```python
|
||||
>>> with httpx.Client(base_url='http://httpbin.org') as client:
|
||||
@ -75,7 +143,7 @@ One particularly useful parameter is `base_url`, which allows you to define a ba
|
||||
URL('http://httpbin.org/headers')
|
||||
```
|
||||
|
||||
For a list of all available client-level parameters, see the [`Client` API reference](/api/#client).
|
||||
For a list of all available client parameters, see the [`Client`](/api/#client) API reference.
|
||||
|
||||
## Calling into Python Web Apps
|
||||
|
||||
@ -120,19 +188,37 @@ with httpx.Client(dispatch=dispatch, base_url="http://testserver") as client:
|
||||
...
|
||||
```
|
||||
|
||||
## Build Request
|
||||
## Request instances
|
||||
|
||||
You can use `Client.build_request()` to build a request and
|
||||
make modifications before sending the request.
|
||||
For maximum control on what gets sent over the wire, HTTPX supports building explicit [`Request`](/api#request) instances:
|
||||
|
||||
```python
|
||||
>>> with httpx.Client() as client:
|
||||
... req = client.build_request("OPTIONS", "https://example.com")
|
||||
... req.url.full_path = "*" # Build an 'OPTIONS *' request for CORS
|
||||
... r = client.send(req)
|
||||
...
|
||||
>>> r
|
||||
<Response [200 OK]>
|
||||
request = httpx.Request("GET", "https://example.com")
|
||||
```
|
||||
|
||||
To dispatch a `Request` instance across to the network, create a [`Client` instance](#client-instances) and use `.send()`:
|
||||
|
||||
```python
|
||||
with httpx.Client() as client:
|
||||
response = client.send(request)
|
||||
...
|
||||
```
|
||||
|
||||
If you need to mix client-level and request-level options in a way that is not supported by the default [Merging of parameters](#merging-of-parameters), you can use `.build_request()` and then make arbitrary modifications to the `Request` instance. For example:
|
||||
|
||||
```python
|
||||
headers = {"X-Api-Key": "...", "X-Client-ID": "ABC123"}
|
||||
|
||||
with httpx.Client(headers=headers) as client:
|
||||
request = client.build_request("GET", "https://api.example.com")
|
||||
|
||||
print(request.headers["X-Client-ID"]) # "ABC123"
|
||||
|
||||
# Don't send the API key for this particular request.
|
||||
del request.headers["X-Api-Key"]
|
||||
|
||||
response = client.send(request)
|
||||
...
|
||||
```
|
||||
|
||||
## .netrc Support
|
||||
|
||||
Loading…
Reference in New Issue
Block a user