all Python 3urllib.requestterm
urllib.request.HTTPRedirectHandler
errors and redirects
The redirect handler: it follows Location for 301/302/303/307/308 responses by itself and detects loops.
Syntax
urllib.request.HTTPRedirectHandler()
The trap: on 301/302/303 a POST becomes a GET and the body is lost — the new address receives an empty request, while a POST answered with 307/308 is not repeated at all and raises HTTPError. At most 10 hops are followed (max_redirections). To stop following redirects, subclass it and return None from redirect_request(): the 3xx then arrives as HTTPError with a Location header.
Examples
import email.message, io, urllib.error, urllib.request, urllib.response
def reply(req, code, body=b'', **headers):
msg = email.message.Message()
for name, value in headers.items():
msg[name.replace('_', '-')] = value
resp = urllib.response.addinfourl(io.BytesIO(body), msg, req.full_url, code)
resp.msg = 'demo'
return resp
class Server(urllib.request.BaseHandler):
handler_order = 100
def http_open(self, req):
print(req.get_method(), req.selector, req.data)
if req.selector == '/old':
return reply(req, 302, Location='/new')
return reply(req, 200, b'ok')
opener = urllib.request.build_opener(Server)
opener.open(urllib.request.Request('http://example.com/old', data=b'x=1'))
# → POST /old b'x=1' / GET /new None
import email.message, io, urllib.error, urllib.request, urllib.response
def reply(req, code, body=b'', **headers):
msg = email.message.Message()
for name, value in headers.items():
msg[name.replace('_', '-')] = value
resp = urllib.response.addinfourl(io.BytesIO(body), msg, req.full_url, code)
resp.msg = 'demo'
return resp
class Server(urllib.request.BaseHandler):
handler_order = 100
def http_open(self, req):
return reply(req, 302, Location='/new')
class NoRedirect(urllib.request.HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
try:
urllib.request.build_opener(Server, NoRedirect).open('http://example.com/old')
except urllib.error.HTTPError as e:
print(e.code, e.headers['Location']) # → 302 /new
See also
Open in the full glossarydocs.python.orgReport an inaccuracy