requests icon indicating copy to clipboard operation
requests copied to clipboard

What about `before_request` hook?

Open RStatkevych opened this issue 7 years ago • 7 comments

Didn't find a correct place where to post my question.

Hook system is a cool idea but I'm wondered why there is no some kind of 'before_request' hook?

Let's assume I want to create requests.Session, and do some preparation steps for upcoming requests (like body formatting or simply log what URL and method). One of possible ways to do that is to override requests.Session.request method, but adding one more hook sounds more reasonable. It is possible to put it here https://github.com/requests/requests/blob/master/requests/sessions.py#L623

Is there any reason of not having this hook? Or is there some appropriate way to execute some piece of code before each request within Session?

RStatkevych avatar Sep 06 '18 14:09 RStatkevych

We had many more hooks pre-1.0. They were removed. There hasn't been enough demand to add them back since. Further, as you might have read before creating this issue, Requests is under a feature-freeze so we will likely not accept this feature request

sigmavirus24 avatar Sep 06 '18 15:09 sigmavirus24

yeah, we used to have this.

We can consider it for 3.0 — can you explain your use case?

kennethreitz avatar Sep 10 '18 19:09 kennethreitz

Here is an example, how I would implement some common logic for all requests within session without hooks by overriding requests.Session.request method

class CustomSession(requests.Session):

    session = None
    _base_url = None

    def __init__(self, base_url):
        requests.Session.__init__(self)
        self._base_url = base_url

    def request(self, method, url, *args, **kwargs):
        """
        Overriden method of requests.Session.request.

        You may add any request preprocessing or response postprocessing logic here.
        """
        full_url = self._base_url + url  # I want to make requests without specifying base url
        LOGGER.debug("Sending request %s %s", method, full_url) # 

        return requests.Session.request(self, method, full_url, *args, **kwargs)

Here is how it can be used

session = CustomSession('http://test.com')

session.get('/v1/something') 
session.delete('/v1/something/23')

session.post('/v1/something/23') 

I think we can have some kind of hooks to do the same logic. That can be functions or callable objects like this :

class BaseURLHook(object):
     def __init__(self, base_url):
           self.base_url = base_url

     def __call__(self, request, *args, **kwargs):
           request.url = self.base_url + request.url
           return request


def log_request_hook(request, *args, **kwargs):
      LOGGER.debug("Sending request %s %s", request.method, request.url)      

....
hooks = {
    'before_request': [
         BaseURLHook('http://test.com'),
         log_request_hook
    ]
}
session = request.Session()
session.hooks = hooks

Having such kind of hooks can allow to perform preprocessing of request and postprocessing of response without overriding requests.Session methods.

I hope I clarified my idea :)

RStatkevych avatar Sep 14 '18 11:09 RStatkevych

But probably hooks is a wrong abstraction for this purposes. What I'm thinking about is some king of Request Factory, that provides Prototypes of requests (it can be an extension of PreparedRequest).

RStatkevych avatar Sep 14 '18 11:09 RStatkevych

This could also be useful for adding distributed tracing support to requests. I had thought about using a TransportAdapter for that though, so really before_request isn't entirely necessary

sigmavirus24 avatar Sep 15 '18 18:09 sigmavirus24

I have a vary similar idea about base url. I'm not sure if there is a need to have before_request hook, but it would be really useful to have base url.

Here is example:

s = requests.Session()
s.auth = ('user', 'pass')
s.base = 'https://httpbin.org'
s.get('/headers')

Under the hood, if base is set, then for each request, urljoin would be called.

I think, that would be really useful.

Now in most cases, I must configure base url somewhere else and pass it everywhere and construct urls manually.

sirex avatar Oct 02 '18 07:10 sirex

Howdy.

If you support the concept of hooks in the requets API, they should be comprehensive and allow a lot of control over how the API works. So far only a "response" hook is partially useful.

My use case: I want to run the API in either capturing mode (write all responses to disk, for that the response hook works) or read back mode (for this case I'd need a "request" hook before the request is sent to produce a response. And/or, a global hook when a session is created so I can override the HTTPAdapters to an adapter that reads back from the cache.

Thank you.

joaoe avatar Aug 15 '22 23:08 joaoe

In an effort to clean up the issue tracker to only have issues that are still relevant to the project we've done a quick pass and decided this issue may no longer be relevant for a variety of potential reasons, including:

  • Applies to a much older version, unclear whether the issue still applies.
  • Change requires a backwards incompatible release and it's unclear if the benefits are worth the migration effort from the community.
  • There isn't a clear demand from the community on the change landing in Requests.

If you think the issue should remain open, please comment so below or open a new issue and link back to the original issue. Again, thank you for opening the issue and for the discussion, it's much appreciated.

sethmlarson avatar May 20 '24 14:05 sethmlarson