From 746c8874c42b22aa09e4b0f8f804d0fa9b02bb71 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Wed, 6 Apr 2022 08:47:11 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B0=83=E6=95=B4=E4=B8=BAhttp=E7=9B=B8?= =?UTF-8?q?=E5=BA=94=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/auth1/errors.py | 2 + apps/auth1/views.py | 10 +++-- apps/monitor/views.py | 9 +++-- apps/system/errors.py | 2 + apps/system/views.py | 33 +++++++--------- apps/third/views.py | 1 - apps/utils/exceptions.py | 46 ++++++++++++++++++++++ apps/utils/models.py | 7 ++-- apps/utils/permission.py | 4 +- apps/utils/response.py | 83 --------------------------------------- apps/utils/serializers.py | 2 +- apps/utils/snowflake.py | 10 ++--- apps/utils/views.py | 7 ++-- apps/utils/viewsets.py | 7 ++-- server/settings.py | 14 ++++--- server/urls.py | 4 +- 16 files changed, 104 insertions(+), 137 deletions(-) create mode 100644 apps/auth1/errors.py create mode 100644 apps/system/errors.py create mode 100644 apps/utils/exceptions.py delete mode 100644 apps/utils/response.py diff --git a/apps/auth1/errors.py b/apps/auth1/errors.py new file mode 100644 index 00000000..a982796f --- /dev/null +++ b/apps/auth1/errors.py @@ -0,0 +1,2 @@ + +NAME_OR_PASSWORD_WRONG = '账户名或密码错误' \ No newline at end of file diff --git a/apps/auth1/views.py b/apps/auth1/views.py index 2d2a874a..6a2c4bb7 100644 --- a/apps/auth1/views.py +++ b/apps/auth1/views.py @@ -1,4 +1,5 @@ +from rest_framework.exceptions import ParseError, ValidationError from django.shortcuts import render from rest_framework.views import APIView from rest_framework.response import Response @@ -7,8 +8,9 @@ from django.contrib.auth import authenticate, login, logout from rest_framework.generics import CreateAPIView from rest_framework.permissions import IsAuthenticated + from apps.auth1.serializers import LoginSerializer -from apps.utils.response import FailResponse, SuccessResponse + # Create your views here. class TokenBlackView(APIView): @@ -42,8 +44,8 @@ class LoginView(CreateAPIView): password = vdata.get('password')) if user is not None: login(request, user) - return SuccessResponse() - return FailResponse(msg='账户或密码错误') + return Response() + raise ParseError('账户名或密码错误', 'username_or_password_wrong') class LogoutView(APIView): authentication_classes = [] @@ -57,4 +59,4 @@ class LogoutView(APIView): 退出登录 """ logout(request) - return SuccessResponse() \ No newline at end of file + return Response() \ No newline at end of file diff --git a/apps/monitor/views.py b/apps/monitor/views.py index 2926001c..adaa2ddf 100644 --- a/apps/monitor/views.py +++ b/apps/monitor/views.py @@ -10,7 +10,7 @@ import os from rest_framework import serializers, status from drf_yasg import openapi from drf_yasg.utils import swagger_auto_schema -from apps.utils.response import SuccessResponse +from rest_framework.exceptions import NotFound # Create your views here. @@ -47,7 +47,7 @@ class ServerInfoView(APIView): ret['disk']['total'] = round(disk.total/1024/1024/1024, 2) ret['disk']['used'] = round(disk.used/1024/1024/1024, 2) ret['disk']['percent'] = disk.percent - return SuccessResponse(ret) + return Response(ret) def get_file_list(file_path): @@ -101,7 +101,7 @@ class LogView(APIView): "filepath": filepath, "size": round(fsize/1000, 1) }) - return SuccessResponse(data=logs) + return Response(logs) class LogDetailView(APIView): @@ -117,4 +117,5 @@ class LogDetailView(APIView): data = f.read() return Response(data) except: - return Response('未找到', status=status.HTTP_404_NOT_FOUND) + raise NotFound('不存在该日志') + diff --git a/apps/system/errors.py b/apps/system/errors.py new file mode 100644 index 00000000..cb18dd06 --- /dev/null +++ b/apps/system/errors.py @@ -0,0 +1,2 @@ +# 自定义的错误码 +from rest_framework.exceptions import ValidationError \ No newline at end of file diff --git a/apps/system/views.py b/apps/system/views.py index 1ef6d814..fc55e539 100644 --- a/apps/system/views.py +++ b/apps/system/views.py @@ -1,13 +1,10 @@ -import logging - -from django.conf import settings from django.contrib.auth.hashers import check_password, make_password from django.db import transaction from django_celery_beat.models import (CrontabSchedule, IntervalSchedule, PeriodicTask) from django_celery_results.models import TaskResult from rest_framework.decorators import action -from rest_framework.exceptions import ValidationError +from rest_framework.exceptions import ParseError, ValidationError from rest_framework.mixins import (CreateModelMixin, DestroyModelMixin, ListModelMixin, RetrieveModelMixin) from rest_framework.parsers import (JSONParser, @@ -17,9 +14,9 @@ from rest_framework.response import Response from rest_framework.views import APIView from apps.utils.mixins import (CustomCreateModelMixin) +from django.conf import settings from apps.utils.permission import get_user_perms_map from apps.utils.queryset import get_child_queryset2 -from apps.utils.response import FailResponse, SuccessResponse from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet from server.celery import app as celery_app from .filters import UserFilter @@ -32,8 +29,6 @@ from .serializers import (DeptCreateUpdateSerializer, DeptSerializer, DictCreate UserCreateSerializer, UserListSerializer, UserPostCreateSerializer, UserPostSerializer, UserUpdateSerializer) -logger = logging.getLogger('log') - # logger.info('请求成功! response_code:{};response_headers:{}; # response_body:{}'.format(response_code, response_headers, response_body[:251])) @@ -80,7 +75,7 @@ class PTaskViewSet(CustomModelViewSet): obj = self.get_object() obj.enabled = False if obj.enabled else True obj.save() - return SuccessResponse() + return Response() @transaction.atomic def create(self, request, *args, **kwargs): @@ -99,7 +94,7 @@ class PTaskViewSet(CustomModelViewSet): **interval_, defaults=interval_) data['interval'] = interval.id except: - raise ValidationError('时间策略有误') + raise ParseError('时间策略有误', 'schedule_wrong') if timetype == 'crontab' and crontab_: data['interval'] = None try: @@ -108,11 +103,11 @@ class PTaskViewSet(CustomModelViewSet): **crontab_, defaults=crontab_) data['crontab'] = crontab.id except: - raise ValidationError('时间策略有误') + raise ParseError('时间策略有误', 'schedule_wrong') serializer = self.get_serializer(data=data) serializer.is_valid(raise_exception=True) serializer.save() - return SuccessResponse() + return Response() @transaction.atomic def update(self, request, *args, **kwargs): @@ -133,7 +128,7 @@ class PTaskViewSet(CustomModelViewSet): **interval_, defaults=interval_) data['interval'] = interval.id except: - raise ValidationError('时间策略有误') + raise ParseError('时间策略有误', 'schedule_wrong') if timetype == 'crontab' and crontab_: data['interval'] = None try: @@ -144,12 +139,12 @@ class PTaskViewSet(CustomModelViewSet): **crontab_, defaults=crontab_) data['crontab'] = crontab.id except: - raise ValidationError('时间策略有误') + raise ParseError('时间策略有误', 'schedule_wrong') instance = self.get_object() serializer = self.get_serializer(instance, data=data) serializer.is_valid(raise_exception=True) serializer.save() - return SuccessResponse() + return Response() class PTaskResultViewSet(ListModelMixin, RetrieveModelMixin, CustomGenericViewSet): @@ -310,7 +305,7 @@ class UserViewSet(CustomModelViewSet): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) serializer.save(password=password) - return SuccessResponse(data=serializer.data) + return Response(data=serializer.data) @action(methods=['put'], detail=False, permission_classes=[IsAuthenticated]) def password(self, request, pk=None): @@ -326,11 +321,11 @@ class UserViewSet(CustomModelViewSet): if new_password1 == new_password2: user.set_password(new_password2) user.save() - return SuccessResponse() + return Response() else: - return FailResponse(msg='新密码两次输入不一致!') + raise ParseError('新密码两次输入不一致!', 'password_not_same') else: - return FailResponse(msg='旧密码错误!') + raise ValidationError('旧密码错误!', 'old_password_wrong') @action(methods=['get'], detail=False, permission_classes=[IsAuthenticated]) def info(self, request, pk=None): @@ -348,7 +343,7 @@ class UserViewSet(CustomModelViewSet): 'avatar': user.avatar, 'perms': perms, } - return SuccessResponse(data) + return Response(data) class FileViewSet(CustomCreateModelMixin, RetrieveModelMixin, ListModelMixin, CustomGenericViewSet): diff --git a/apps/third/views.py b/apps/third/views.py index 3dc9754d..901960f8 100644 --- a/apps/third/views.py +++ b/apps/third/views.py @@ -1,4 +1,3 @@ -from django.shortcuts import render from apps.utils.dahua import dhClient from apps.utils.xunxi import xxClient from rest_framework.response import Response diff --git a/apps/utils/exceptions.py b/apps/utils/exceptions.py new file mode 100644 index 00000000..57d57569 --- /dev/null +++ b/apps/utils/exceptions.py @@ -0,0 +1,46 @@ +from typing import Tuple +from django.core.exceptions import PermissionDenied +from django.http import Http404 +from server.settings import myLogger +from rest_framework.response import Response +from rest_framework import exceptions +from rest_framework.views import set_rollback +from django.utils.translation import gettext_lazy as _ +import traceback + +class MyError(exceptions.ParseError): + """自定义业务异常 + """ + + def __init__(self, error:Tuple[str, str]=(), detail=None, code=None): + if error: + code, detail = error + super().__init__(detail, code) + +def custom_exception_hander(exc, context): + """ + 自定义异常处理 + """ + if isinstance(exc, Http404): + exc = exceptions.NotFound() + elif isinstance(exc, PermissionDenied): + exc = exceptions.PermissionDenied() + + if isinstance(exc, exceptions.APIException): + headers = {} + if getattr(exc, 'auth_header', None): + headers['WWW-Authenticate'] = exc.auth_header + if getattr(exc, 'wait', None): + headers['Retry-After'] = '%d' % exc.wait + + if isinstance(exc.detail, (list, dict)): + data = {'err_detail':exc.detail} + data['err_codes'] = exc.get_codes() + else: + data = {'err_msg': exc.detail, 'err_code':exc.get_codes()} + + set_rollback() + return Response(data, status=exc.status_code, headers=headers) + # 未处理的异常记录日志 + myLogger.error(traceback.format_exc()) + return Response(data={'err_code':'server_error', 'err_msg':'服务器错误'}, status=500) \ No newline at end of file diff --git a/apps/utils/models.py b/apps/utils/models.py index 2c8d582c..6e9b9811 100644 --- a/apps/utils/models.py +++ b/apps/utils/models.py @@ -1,8 +1,9 @@ -from django.db import models import django.utils.timezone as timezone +from django.db import models from django.db.models.query import QuerySet -from apps.utils.snowflake import worker +from apps.utils.snowflake import idWorker + # 自定义软删除查询基类 @@ -55,7 +56,7 @@ class BaseModel(models.Model): """ 基本表 """ - id = models.CharField(max_length=20, primary_key=True, default=worker.get_id, editable=False, verbose_name='主键ID', help_text='主键ID') + id = models.CharField(max_length=20, primary_key=True, default=idWorker.get_id, editable=False, verbose_name='主键ID', help_text='主键ID') create_time = models.DateTimeField( default=timezone.now, verbose_name='创建时间', help_text='创建时间') update_time = models.DateTimeField( diff --git a/apps/utils/permission.py b/apps/utils/permission.py index 95da4c23..6b0c8f90 100644 --- a/apps/utils/permission.py +++ b/apps/utils/permission.py @@ -8,7 +8,7 @@ from django.db.models.query import QuerySet def get_user_perms_map(user): """ - 获取权限列表,可用redis存取 + 获取权限字典,可用redis存取 """ user_perms_map = {} if user.is_superuser: @@ -44,7 +44,7 @@ class RbacPermission(BasePermission): :return: """ if not hasattr(view, 'perms_map'): - return False + return True user_perms_map = cache.get('perms_' + request.user.id, None) if user_perms_map is None: user_perms_map = get_user_perms_map(request.user) diff --git a/apps/utils/response.py b/apps/utils/response.py deleted file mode 100644 index 812ab6d3..00000000 --- a/apps/utils/response.py +++ /dev/null @@ -1,83 +0,0 @@ -import traceback -from rest_framework.renderers import JSONRenderer -from rest_framework.views import exception_handler -from rest_framework.response import Response -from rest_framework import status as drf_status -import logging - -logger = logging.getLogger('log') - -def custome_exception_hander(exc, context): - """ - 自定义异常处理 - """ - res = exception_handler(exc, context) - if res: - return res - else: - """ - 日志记录 - """ - logger.error(traceback.format_exc()) - return None - -class FitJSONRenderer(JSONRenderer): - """ - 自行封装的渲染器 - """ - - def render(self, data, accepted_media_type=None, renderer_context=None): - """ - 如果使用这个render, - 普通的response将会被包装成: - {"code":200000,"data":"X","msg":"X"} - 这样的结果 - 使用方法: - - 全局 - REST_FRAMEWORK = { - 'DEFAULT_RENDERER_CLASSES': ('utils.response.FitJSONRenderer', ), - } - - 局部 - class UserCountView(APIView): - renderer_classes = [FitJSONRenderer] - - :param data: - :param accepted_media_type: - :param renderer_context: - :return: {"code":200,"data":"X","msg":"X"} - """ - response_body = dict(success=True, code=200000, msg='请求成功', data=None) - response = renderer_context.get("response") - status_code = response.status_code - if isinstance(data, dict) and data.keys() == response_body.keys(): - response_body = data - else: - # 处理drf格式的返回数据 - if status_code >= 400: # 如果http响应异常 - response_body = dict(success=False, code=400000, msg='请求失败', data=None) - response_body['data'] = data # data里是详细异常信息 - response_body['code'] = status_code*1000 - prefix = "" - if isinstance(data, dict): - prefix = list(data.keys())[0] - data = data[prefix] - if isinstance(data, list): - data = data[0] - - if prefix != 'detail': - response_body['msg'] = prefix + str(data) # 取一部分放入msg,方便前端alert - else: - response_body['data'] = data - renderer_context.get("response").status_code = 200 # 统一成200响应, 可用body里code区分业务异常 - return super(FitJSONRenderer, self).render(response_body, accepted_media_type, renderer_context) - - -class SuccessResponse(Response): - def __init__(self, data=None, msg='请求成功', code=200000, status=None, template_name=None, headers=None, exception=False, content_type=None): - std_data = dict(success=True, code=code, msg=msg, data=data) - super().__init__(std_data, status, template_name, headers, exception, content_type) - -class FailResponse(Response): - def __init__(self, msg='请求失败', data=None, code=400000, status=400, template_name=None, headers=None, exception=False, content_type=None): - std_data = dict(success=False, code=code, msg=msg, data=data) - super().__init__(std_data, status, template_name, headers, exception, content_type) \ No newline at end of file diff --git a/apps/utils/serializers.py b/apps/utils/serializers.py index 210d7695..5b397521 100644 --- a/apps/utils/serializers.py +++ b/apps/utils/serializers.py @@ -3,7 +3,7 @@ from rest_framework import serializers from django_restql.mixins import DynamicFieldsMixin class PkSerializer(serializers.Serializer): - pks = serializers.ListField(child=serializers.IntegerField(min_value=1), label="主键ID列表") + pks = serializers.ListField(child=serializers.CharField(max_length=20), label="主键ID列表") class GenSignatureSerializer(serializers.Serializer): path = serializers.CharField(label="图片地址") diff --git a/apps/utils/snowflake.py b/apps/utils/snowflake.py index 223b53ed..8b6f89f6 100644 --- a/apps/utils/snowflake.py +++ b/apps/utils/snowflake.py @@ -2,10 +2,7 @@ # https://github.com/twitter-archive/snowflake/blob/snowflake-2010/src/main/scala/com/twitter/service/snowflake/IdWorker.scala import time -import logging - -from django.conf import settings -# logger = logging.getLogger('log') +from server.settings import SNOW_DATACENTER_ID, SNOW_WORKER_ID class InvalidSystemClock(Exception): """ @@ -76,7 +73,6 @@ class IdWorker(object): # 时钟回拨 if timestamp < self.last_timestamp: - # logger.error('clock is moving backwards. Rejecting requests until {}'.format(self.last_timestamp)) raise InvalidSystemClock if timestamp == self.last_timestamp: @@ -101,7 +97,7 @@ class IdWorker(object): timestamp = self._gen_timestamp() return timestamp -worker = IdWorker(settings.SNOW_DATACENTER_ID, settings.SNOW_WORKER_ID) +idWorker = IdWorker(SNOW_DATACENTER_ID, SNOW_WORKER_ID) if __name__ == '__main__': - print(worker.get_id()) \ No newline at end of file + print(idWorker.get_id()) \ No newline at end of file diff --git a/apps/utils/views.py b/apps/utils/views.py index 6df54deb..945524a5 100644 --- a/apps/utils/views.py +++ b/apps/utils/views.py @@ -3,7 +3,8 @@ import os import cv2 from server.settings import BASE_DIR import numpy as np -from .response import FailResponse, SuccessResponse +from rest_framework.response import Response +from rest_framework.exceptions import ParseError from apps.utils.viewsets import CustomGenericViewSet from apps.utils.mixins import CustomCreateModelMixin from apps.utils.serializers import GenSignatureSerializer @@ -42,6 +43,6 @@ class SignatureViewSet(CustomCreateModelMixin, CustomGenericViewSet): ext = os.path.splitext(path) new_path = ext[0] + '.png' cv2.imwrite(new_path, image) - return SuccessResponse({'path': new_path.replace(BASE_DIR, '')}) + return Response({'path': new_path.replace(BASE_DIR, '')}) except: - return FailResponse(msg='签名照处理失败,请重新上传') + raise ParseError('签名照处理失败,请重新上传') diff --git a/apps/utils/viewsets.py b/apps/utils/viewsets.py index d29fee3b..1792bf47 100644 --- a/apps/utils/viewsets.py +++ b/apps/utils/viewsets.py @@ -4,9 +4,10 @@ from rest_framework.decorators import action from apps.utils.mixins import CustomCreateModelMixin, CustomDestoryModelMixin, CustomUpdateModelMixin, OptimizationMixin from apps.utils.permission import RbacDataMixin, RbacPermission from apps.utils.serializers import PkSerializer -from apps.utils.response import FailResponse, SuccessResponse +from rest_framework.response import Response from rest_framework.mixins import DestroyModelMixin, RetrieveModelMixin, ListModelMixin from rest_framework.permissions import IsAuthenticated +from rest_framework.exceptions import ValidationError class CustomGenericViewSet(GenericViewSet): @@ -59,9 +60,9 @@ class CustomModelViewSet(OptimizationMixin, CustomCreateModelMixin pks = request_data.get('pks',None) if pks: self.get_queryset().filter(id__in=pks).delete(update_by=request.user) - return SuccessResponse() + return Response() else: - return FailResponse(msg="未获取到pks字段") + raise ValidationError("未获取到pks字段") diff --git a/server/settings.py b/server/settings.py index 99ebc83b..4689345f 100644 --- a/server/settings.py +++ b/server/settings.py @@ -13,7 +13,7 @@ https://docs.djangoproject.com/en/3.0/ref/settings/ from datetime import datetime, timedelta import os from . import conf -from server.conf import DATABASES, XX_ENABLED, XX_LICENCE +import logging # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -164,10 +164,10 @@ REST_FRAMEWORK = { 'rest_framework.permissions.IsAuthenticated', 'apps.utils.permission.RbacPermission' ], - 'DEFAULT_RENDERER_CLASSES': [ - 'apps.utils.response.FitJSONRenderer', - 'rest_framework.renderers.BrowsableAPIRenderer' - ], + # 'DEFAULT_RENDERER_CLASSES': [ + # 'apps.utils.response.FitJSONRenderer', + # 'rest_framework.renderers.BrowsableAPIRenderer' + # ], 'DEFAULT_FILTER_BACKENDS': [ 'django_filters.rest_framework.DjangoFilterBackend', 'rest_framework.filters.SearchFilter', @@ -179,7 +179,7 @@ REST_FRAMEWORK = { 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema', 'UNAUTHENTICATED_USER': None, 'UNAUTHENTICATED_TOKEN': None, - 'EXCEPTION_HANDLER': 'apps.utils.response.custome_exception_hander', + 'EXCEPTION_HANDLER': 'apps.utils.exceptions.custom_exception_hander', } # simplejwt配置 SIMPLE_JWT = { @@ -302,6 +302,8 @@ LOGGING = { }, } } +# 实例化myLogger +myLogger = logging.getLogger('log') # 大华ICC平台 DAHUA_ENABLED = conf.DAHUA_ENABLED diff --git a/server/urls.py b/server/urls.py index 616e378f..67076a58 100644 --- a/server/urls.py +++ b/server/urls.py @@ -37,6 +37,7 @@ urlpatterns = [ # django后台 path('django/doc/', include('django.contrib.admindocs.urls')), path('django/', admin.site.urls), + path('api-auth/', include('rest_framework.urls')), # api path('', include('apps.auth1.urls')), @@ -46,6 +47,7 @@ urlpatterns = [ path('', include('apps.third.urls')), path('', include('apps.utils.urls')), + # api文档 path('api/docs/', include_docs_urls(title="接口文档", authentication_classes=[], permission_classes=[])), @@ -53,7 +55,7 @@ urlpatterns = [ path('api/redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'), # 前端页面入口 - path('',TemplateView.as_view(template_name="index.html")) + path('',TemplateView.as_view(template_name="index.html")), ] + \ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + \ static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)