首页 > 解决方案 > 400. 这是一个错误。您的客户发出了格式错误或非法的请求。这就是我们所知道的 - Google App Engine Django App

问题描述

我一直在使用 Django 和 djangorestframework 编写一个 API,以便在 Google App Engine 中进行部署。API 基本上是一个包注册表,因此您可以使用 API 创建、更新、获取和删除包。

除了一个之外,所有端点似乎都可以工作。唯一不起作用的端点是显示在线注册表中所有包的分页列表的端点。

所有端点都在工作,但由于某种原因,当我点击特定端点'/packages/'时,GCP 给了我错误

400. That’s an error.

Your client has issued a malformed or illegal request. That’s all we know

当我在我的计算机上本地运行应用程序时,所有端点都可以正常工作。当我在 Google App Engine 上部署该应用程序时,该应用程序仅针对该特定路由停止工作。API 有效负载应为:

[ { “姓名”: ”*” } ]

我完全迷失了这一点,并希望有任何帮助。

视图.py

import django.db.utils
    from rest_framework.decorators import api_view
    from rest_framework.response import Response
    from django.core.paginator import Paginator, EmptyPage

import registry.models
from .serializers import *
from .models import *


# Create your views here.
@api_view(['GET'])
def apiOverview(request):
    api_urls = {
        'List': '/package-list/',
        'Create': '/package-create/',
        'Update': '/package-update/',
        'Delete': '/package-delete/',
        'Rate': '/package-rate/',
        'Download': '/package-download/'
    }
    return Response(api_urls)


@api_view(['GET'])
def packages_middleware(request):
    print(request)
    print(type(request))
    print(request.data)
    print(type(request.data))

    # determine offset query parameter
    offset = request.GET.get('Offset')
    if offset is None:
        offset = 0
    else:
        offset = int(offset)

    # capturing request body
    response = []
    queries = request.data

    if len(queries) < 1:
        return Response({'message: empty request body array'}, status=400)
    else:
        if len(queries) == 1 and queries[0]['Name'] == '*':
            response = list(PackageMetadata.objects.all().values())
        else:
            for query in queries:
                if 'Name' in query.keys() and 'Version' in query.keys():
                    for x in list(PackageMetadata.objects.filter(Name__icontains=query['Name']).filter(
                            Version__contains=query['Version']).values()):
                        response.append(x)
                else:
                    response.append({
                        'message': 'query {q} is missing at least one attribute - remember to check spelling and capitalization'.format(q=query)
                    })

        paginator = Paginator(response, 10)
        try:
            return Response(paginator.page(offset + 1).object_list, headers={'Offset': offset + 1})
        except EmptyPage:
            return Response(paginator.page(1).object_list, headers={'Offset': 2})


@api_view(['GET', 'PUT', 'DELETE'])
def package_middleware(request, pk):
    try:
        package = Package.objects.get(Metadata__ID__exact=str(pk))
        if request.method == 'GET':
            # CHECK THAT CONTENT FIELD IS SET
            serializer = PackageSerializer(package, many=False)
            return Response(serializer.data, status=200)
        elif request.method == 'PUT':
            payload = request.data
            if 'Metadata' in payload and 'Data' in payload:
                if payload['Metadata'] != PackageMetadataSerializer(package.Metadata, many=False).data:
                    return Response({"message": "name, version and ID must match"}, status=400)
                else:
                    # CHECK THAT ONLY ONE DATA FIELD IS SET
                    serializer_data = PackageDataSerializer(data=payload['Data'], many=False)
                    if serializer_data.is_valid(raise_exception=True):
                        try:
                            serializer_data.update(instance=package.Data, validated_data=serializer_data.validated_data)
                        except django.db.utils.IntegrityError:
                            return Response(
                                {"message": "both Content and URL must be included in query, but exactly one can be set"},
                                status=400)

                    return Response(status=200)
            else:
                return Response(
                    {"message": "incorrect request body schema - remember to check spelling and capitalization"},
                    status=400)
        else:
            package.Metadata.delete()
            package.Data.delete()
            package.delete()
            return Response({"message": "package deleted"}, status=200)

    except registry.models.Package.DoesNotExist:
        return Response({"message": "package not found"}, status=400)


@api_view(['POST'])
def create_package_middleware(request):
    payload = request.data
    print(payload)
    if 'Metadata' in payload and 'Data' in payload:

        serializer_metadata = PackageMetadataSerializer(data=payload['Metadata'], many=False)
        serializer_data = PackageDataSerializer(data=payload['Data'], many=False)
        if serializer_data.is_valid(raise_exception=True) and serializer_metadata.is_valid(raise_exception=True):
            try:
                metadata = PackageMetadata.objects.create(ID=serializer_metadata.data.get('ID'),
                                                          Name=serializer_metadata.data.get('Name'),
                                                          Version=serializer_metadata.data.get('Version'))
            except django.db.utils.IntegrityError:
                return Response({"message": "duplicate key-value (Name, Version) violates uniqueness constraint"},
                                status=403)
            try:
                data = PackageData.objects.create(Content=serializer_data.data.get('Content'),
                                                  URL=serializer_data.data.get('URL'))
            except django.db.utils.IntegrityError:
                metadata.delete()
                return Response(
                    {"message": "both Content and URL must be included in query, but exactly one can be set"},
                    status=400)

            Package.objects.create(Data=data, Metadata=metadata)
            serializer_metadata = PackageMetadataSerializer(metadata, many=False)
            return Response(serializer_metadata.data, status=200)

    else:
        return Response({"message": "incorrect request body schema - remember to check spelling and capitalization"},
                        status=400)


@api_view(['DELETE'])
def byName_middleware(request, name):
    if name == '*':
        return Response({"message": "query name reserved"}, status=400)

    querySet = Package.objects.filter(Metadata__Name__iexact=name)

    if len(querySet) == 0:
        return Response({"message": "package not found"})
    else:
        for package in querySet:
            package.Metadata.delete()
            package.Data.delete()
            package.delete()
        return Response(status=200)

URLS.py

from django.urls import path, include
from . import views

urlpatterns = [
    path('', views.apiOverview),
    path('packages/', views.packages_middleware, name='packages_middleware'),
    path('package/<str:pk>', views.package_middleware, name='package'),
    path('package/', views.create_package_middleware, name='create'),
    path('package/byName/<str:name>', views.byName_middleware, name='byName')

]

模型.py

from django.db import models


# Create your models here.
class PackageData(models.Model):
    Content = models.TextField(blank=True, null=True)  # actual zip file
    URL = models.CharField(max_length=500, blank=True, null=True)  # url of package

    # class Meta:
    #     constraints = [
    #         models.CheckConstraint(
    #             name="%(app_label)s_%(class)s_content_or_url",
    #             check=models.Q(Content__isnull=True, URL__isnull=False) | models.Q(Content__isnull=False, URL__isnull=True)
    #         )
    #     ]


class PackageMetadata(models.Model):
    class Meta:
        constraints = [
            models.UniqueConstraint(fields=['Name', 'Version'], name='unique_package')
        ]

    ID = models.CharField(primary_key=True, max_length=50)
    Name = models.CharField(max_length=50)
    Version = models.CharField(max_length=50)


class Package(models.Model):
    Data = models.ForeignKey(PackageData, on_delete=models.CASCADE)
    Metadata = models.ForeignKey(PackageMetadata, on_delete=models.CASCADE)


class PackageRating(models.Model):
    BusFactor = models.DecimalField(max_digits=10, decimal_places=9)
    Correctness = models.DecimalField(max_digits=10, decimal_places=9)
    GoodPinningPractice = models.DecimalField(max_digits=10, decimal_places=9)
    LicenseScore = models.DecimalField(max_digits=10, decimal_places=9)
    RampUp = models.DecimalField(max_digits=10, decimal_places=9)
    ResponsiveMaintainer = models.DecimalField(max_digits=10, decimal_places=9)


class PackageQuery(models.Model):
    Name = models.CharField(max_length=50)
    Version = models.CharField(max_length=50)

标签: djangoapigoogle-app-enginedjango-rest-framework

解决方案


您正在GET调用/packages/由路由定义处理的调用packages_middleware(request)

在您拥有的那个功能中,您queries = request.data的其余逻辑依赖于queries. 该文件request.data适用于 POST、PATCH、PUT。如果这是正确的,那么这似乎就是你的错误所在。

您可以在该行之前和之后放置一个打印语句,然后查看您的应用程序是否在之后打印该语句。这将帮助您确认这是否是问题所在。

您还应该打印queries. 由于您实际上并未使用 GET 请求发送正文,因此您的其余逻辑可能会失败,因为 的值queries不是您所期望的。


推荐阅读