django 使用中间件 访问request里面的内容
2017-11-21 16:47
453 查看
博主要做一个记录请求api日志的事情。
声明:本文指定Django版本为1.8,其它版本实现方式自己网上搜!
方法一 Nginx记录请求和响应 我最初用Nginx实现过能记录请求的信息(已经实现,记录的日志写在文件上),但是要Nginx需要其它Linux软件才能实现记录响应信息!如果你的机器不能联网,没有yum等,或者我特么就是讨厌手动安装软件在Linux机器上! please skip!
方法二 项目中有人提出利用装饰器在每个view上面利用装饰器模式来记录日志 。第一:耦合,耦合,耦合!我觉得是高度耦合!虽然只是import 这个方法,然后加上@funciton!第二:这个装饰器记录不了response的信息!所以也请你skip !
方法三 使用的是Django中的中间件(类似Java中serverlet中的拦截器)!
发现在中间件中的request与Django view中的request都是出自django.core.handlers.wsgi.WSGIRequest这个对象,瞬间就觉得棒!
能记录IP、用户等信息但是却不能访问请求中的body。原因是:you cannot access body after reading from request’s data stream
然后查看官网 发现:
Accessing request.POST or request.REQUEST inside middleware from process_request or process_view will prevent any view running after the middleware from being able to modify the upload handlers for the request, and should normally be avoided.
我们看看报错来自何处:
Django源码
看这个意思说,别读过了,然后报错!why?
难道不行了吗?错!
你把这个值复制给另外一个变量就可以了:
你不能访问request.body,但是你可以访问复制过后的变量!还有,这个方法不适用在process_response方法中,我在process_request中是可以的!
Stack Overflow上有一个网友提到django restframework,我猜想,restframework它是继承Django的django.core.handlers.wsgi.WSGIRequest对象,他可以访问body,我想也是可以找到其它方法直接访问reqeust.body这个变量的,但是看了restframework源码,我一脸懵逼,能力有限,看不懂!最后,我附上国外友人提到的记录:
https://stackoverflow.com/questions/22740310/how-to-update-django-httprequest-body-in-middleware
最最后,我附上我能实现的代码:
最后看到一篇介绍中间件不错的好文章!
http://blog.csdn.net/wolaiye320/article/details/52035451
ps:相比较而言,浏览官网的信息才是最好的。但是能去GitHub,Stack Overflow上最好,尤其是Stack Overflow(最近公司的网日了
——————-分割线——————–
一个很有意思的问题出现了!我之前提到在中间件 process_response(request,response)中的request是拿不到request.body这个变量的,因为会报”you cannot access body after reading from request’s data stream”这种错误!我猜想,process_response(request,response)和process_request(request)中的request不一样吧!
结果,我测试了一下:
把process_request中的request传给初始化变量,然后再去比较process_response(request,response)中的request变量,
id(request) == id(self.request) request is self.re_quest
发现是一样的。这就奇怪了!为什么我能在前者拿到body,在后面却拿不到值!
—————分割线——————
那么,在response的时候,是否可以修改response中的传给前端的值呢?官网如下解释:
it could alter the given response。Stack Overflow上有人问过:https://stackoverflow.com/questions/44112528/editing-response-contect-in-django-middleware
里面如下:
然后我就是改掉content这个值,就可以实现更改了!
声明:本文指定Django版本为1.8,其它版本实现方式自己网上搜!
方法一 Nginx记录请求和响应 我最初用Nginx实现过能记录请求的信息(已经实现,记录的日志写在文件上),但是要Nginx需要其它Linux软件才能实现记录响应信息!如果你的机器不能联网,没有yum等,或者我特么就是讨厌手动安装软件在Linux机器上! please skip!
方法二 项目中有人提出利用装饰器在每个view上面利用装饰器模式来记录日志 。第一:耦合,耦合,耦合!我觉得是高度耦合!虽然只是import 这个方法,然后加上@funciton!第二:这个装饰器记录不了response的信息!所以也请你skip !
方法三 使用的是Django中的中间件(类似Java中serverlet中的拦截器)!
发现在中间件中的request与Django view中的request都是出自django.core.handlers.wsgi.WSGIRequest这个对象,瞬间就觉得棒!
能记录IP、用户等信息但是却不能访问请求中的body。原因是:you cannot access body after reading from request’s data stream
然后查看官网 发现:
Accessing request.POST or request.REQUEST inside middleware from process_request or process_view will prevent any view running after the middleware from being able to modify the upload handlers for the request, and should normally be avoided.
我们看看报错来自何处:
Django源码
@property def body(self): if not hasattr(self, '_body'): if self._read_started: raise RawPostDataException("You cannot access body after reading from request's data stream") # Limit the maximum request data size that will be handled in-memory. if (settings.DATA_UPLOAD_MAX_MEMORY_SIZE is not None and int(self.META.get('CONTENT_LENGTH') or 0) > settings.DATA_UPLOAD_MAX_MEMORY_SIZE): raise RequestDataTooBig('Request body exceeded settings.DATA_UPLOAD_MAX_MEMORY_SIZE.') try: self._body = self.read() except IOError as e: six.reraise(UnreadablePostError, UnreadablePostError(*e.args), sys.exc_info()[2]) self._stream = BytesIO(self._body) return self._body
看这个意思说,别读过了,然后报错!why?
难道不行了吗?错!
你把这个值复制给另外一个变量就可以了:
re_request_body = getattr(request,'_body',request.body) print re_request_body
你不能访问request.body,但是你可以访问复制过后的变量!还有,这个方法不适用在process_response方法中,我在process_request中是可以的!
Stack Overflow上有一个网友提到django restframework,我猜想,restframework它是继承Django的django.core.handlers.wsgi.WSGIRequest对象,他可以访问body,我想也是可以找到其它方法直接访问reqeust.body这个变量的,但是看了restframework源码,我一脸懵逼,能力有限,看不懂!最后,我附上国外友人提到的记录:
https://stackoverflow.com/questions/22740310/how-to-update-django-httprequest-body-in-middleware
最最后,我附上我能实现的代码:
import datetime import random class middleware_record_req_rsp(object): def __init__(self): nowTime = datetime.datetime.now().strftime("%Y%m%d%H%M%S") randomNum = random.randint(0,1000) uniqueNum = str(nowTime) + str(randomNum) self.flag = uniqueNum def process_request(self,request): if request.method == 'POST': x_forward_for = request.META.get('HTTP_X_FORWARDED_FOR') request_ip = x_forward_for.split(',')[0] if x_forward_for else request.META.get('REMOTE_ADDR') request_user = request.META.get('USER') re_request_body = getattr(request,'_body',request.body) flag = self.flag return None def process_response(self,request,response): if request.method == 'POST': flag = self.flag response_status_code = response.status_code return response
最后看到一篇介绍中间件不错的好文章!
http://blog.csdn.net/wolaiye320/article/details/52035451
ps:相比较而言,浏览官网的信息才是最好的。但是能去GitHub,Stack Overflow上最好,尤其是Stack Overflow(最近公司的网日了
——————-分割线——————–
一个很有意思的问题出现了!我之前提到在中间件 process_response(request,response)中的request是拿不到request.body这个变量的,因为会报”you cannot access body after reading from request’s data stream”这种错误!我猜想,process_response(request,response)和process_request(request)中的request不一样吧!
结果,我测试了一下:
把process_request中的request传给初始化变量,然后再去比较process_response(request,response)中的request变量,
id(request) == id(self.request) request is self.re_quest
发现是一样的。这就奇怪了!为什么我能在前者拿到body,在后面却拿不到值!
—————分割线——————
那么,在response的时候,是否可以修改response中的传给前端的值呢?官网如下解释:
it could alter the given response。Stack Overflow上有人问过:https://stackoverflow.com/questions/44112528/editing-response-contect-in-django-middleware
里面如下:
Response is already rendered in the middleware stage so you can't just change response.data, you need to rerender it or change rendered content directly. class RequestLogMiddleWare(object): def __init__(self, get_response): self.get_response = get_response def __call__(self, request): response = self.get_response(request) if isinstance(response, Response): response.data['detail'] = 'I have been edited' # you need to change private attribute `_is_render` # to call render second time response._is_rendered = False response.render() return response The second approach is just change content directly, but in that case built in rest framework browser API will not work because template will not render properly. import json class RequestLogMiddleWare(object): def __init__(self, get_response): self.get_response = get_response def __call__(self, request): response = self.get_response(request) if isinstance(response, Response): response.data['detail'] = 'I have been edited' response.content = json.dumps(response.data) return response
然后我就是改掉content这个值,就可以实现更改了!
相关文章推荐
- 通过nginx配置目录可以使用url访问里面的内容
- 如何将频繁使用的内容缓存起来以避免频繁访问磁盘?
- 使用 /proc 文件系统来访问 Linux 内核的内容
- 在ASP.NET MVC中客户端使用文本编辑器检测到有潜在危险的 Request.Form 值。无法访问控制器。
- C#、VB.NET使用HttpWebRequest访问https地址(SSL)的实现
- Django补充的内容(request,redirect,context)
- 11 Spring MVC 访问入参获取和拦截器使用(@RequestParam 和 Interceptors)登录模块demo
- HTMLParser使用详解(3)- 通过Filter访问内容
- JQuery 访问 iframe 里面的内容
- C#、VB.NET使用HttpWebRequest访问https地址(SSL)的实现方法
- 使用 /proc 文件系统来访问 Linux 内核的内容
- HTMLParser使用详解(3)- 通过Filter访问内容
- 使用TextWatcher监听EditText的文本变化之后动态改变EditText里面的内容
- 使用struts2的标签,将setAttribute里面的内容直接在页面提取
- 使用 /proc 文件系统来访问 Linux 内核的内容
- SpringMVC——使用RequestDispatcher.include()和HttpServletResponseWrapper动态获取jsp输出内容
- ASP.NET使用代码访问网址,并读取响应的内容
- HTMLParser使用详解(4):通过VISITOR访问内容
- 使用 /proc 文件系统来访问 Linux 内核的内容
- Django里面的RequestContext