ReactJS是一个很棒的前端框架,而Django是一个很棒的后端框架。 但是,像往常一样,在处理琐碎复杂的事情时,很难将两者很好地放在一起。 这不像是在墙上贴香蕉。
这是一个中级教程,超出了大多数使Django和React协同工作的教程范围。 我不仅要在这里给您留下一张不完整的图片。 这就是整个shebang。 克服它并构建有用的东西。 当我们第一次为我的初创公司Lollipop.ai做这件事时,这是一个很大的痛苦,所以请明智地使用此知识并宽恕插件。
为什么还要使用JSON Web令牌? 由于应用程序通常不支持会话,因此我们希望能够构建一个同时支持网络和应用程序的后端API,因此我们将使用JSON Web令牌或JWT来处理会话之间的身份验证切换。前端和后端。 最重要的是,JWT非常紧凑且易于使用。 有关更多信息,请在此处检查Auth0的描述 。
仅需6个步骤即可完成所有操作!
第1部分-Django:
1. Django自定义用户
2. DRF序列化器和身份验证
第2部分-React:
3.作为独立应用程序将React安装在我们的Django项目中
4. 准备React for Authentication ,带有路由,以及注册和登录表单
5. Axios用于请求和令牌
6. 注销和列入黑名单令牌
完整的代码位于GitHub上 ,您可以在各个步骤中浏览分支以查看代码(1-1、1-2),等等。
1-1)在Django中设置自定义用户
首先,新建目录以容纳我们的整个项目。
$ mkdir django-jwt-react
$ cd django-jwt-react
然后创建我们的虚拟环境并使用pipenv
安装。
PS:诗歌更好,但要使第一个虚拟动作开始运行就更加困难
$ pipenv --python 3.7
$ pipenv install django djangorestframework djangorestframework-simplejwt
注意:您可能会在网上看到对名为djangorestframework-jwt
的软件包的引用,但不再对其进行维护。 请改用djangorestframework-simplejwt 。
激活虚拟环境并创建Django项目。
$ pipenv shell
$ django-admin startproject djsr
现在,您应该在django-jwt-react/
目录中包含以下内容。
-django-jwt-react/
--djsr/
---djsr/
----__init__.py
----settings.py
----urls.py
----wsgi.py
---manage.py
--Pipfile
--Pipfile.lock
在大多数情况下,您将不会在项目中使用Django的常规用户,而是会添加自定义属性,例如喜欢的颜色。 但是当我们在数据库中创建了User模型后,我们修改User模型时,Django并不非常喜欢它。 为了避免这些错误,我们首先创建我们的自定义用户,然后才进行并运行数据库迁移。 Django自己建议这样做。
这是在Django中创建我们的身份验证应用程序的好时机。
$ python djsr/manage.py startapp authentication
并将其添加到settings.py
的INSTALLED_APPS
中。
# djsr/djsr/settings.py
INSTALLED_APPS = [
'django.contrib.admin' ,
'django.contrib.auth' ,
'django.contrib.contenttypes' ,
'django.contrib.sessions' ,
'django.contrib.messages' ,
'django.contrib.staticfiles' ,
'authentication'
]
让我们在authentication/models.py
创建我们的自定义用户模型,并添加fav_color属性,因为我们确实关心多彩的用户。
# djsr/authentication/models.py
from django.contrib.auth.models import AbstractUser
from django.db import models
class CustomUser ( AbstractUser ):
fav_color = models.CharField(blank=True, max_length= 120 )
CustomUser
是AbstractUser
扩展,它使我们可以访问标准Django User模型的属性和功能,例如用户名,密码等。 因此,我们不需要将这些添加到我们的。 真好
并使用最基本的ModelAdmin
将其添加到authentication / ModelAdmin
。
# djsr/authentication/admin.py
from django.contrib import admin
from .models import CustomUser
class CustomUserAdmin ( admin . ModelAdmin ):
model = CustomUser
admin.site.register(CustomUser, CustomUserAdmin)
最后,在settings.py
,将CustomUser
配置为AUTH_USER_MODEL
。
# djsr/djsr/settings.py
# ...
# Custom user model
AUTH_USER_MODEL = "authentication.CustomUser"
现在,通过我们的自定义用户设置,我们可以进行并运行迁移。 在执行此操作时,还要创建一个超级用户。
$ python djsr/manage.py makemigrations
$ python djsr/manage.py migrate
$ python djsr/manage.py createsuperuser
凉。 现在运行服务器。
$ python djsr/manage.py runserver
您应该看到默认的Django成功页面。
注意:如果在创建CustomUser之前进行了迁移,则可能必须删除并重新创建数据库。
第1–1节的GitHub代码位于此处 。
1–2)DRF串行器和身份验证
太好了,既然您的项目已经设置了“自定义用户”,我们就可以使用该自定义用户和Django Rest Framework + DRF Simple JWT创建基于Java Web令牌的身份验证。 我们已经安装了那些。
本节将涵盖:
一个。 配置DRF + DRF简单JWT
b。 验证并获取刷新和访问令牌
C。 刷新令牌
d。 自定义获取令牌序列化器和视图以添加额外的上下文
e。 注册新用户
F。 创建和测试受保护的视图
1–2a。 配置DRF + DRF简单JWT
为了使球滚动,请在settings.py
配置DRF和Simple JWT。 将“rest_framework”
添加到已安装的应用程序和REST_FRAMEWORK
配置字典。 不需要将Django Rest Framework简单JWT包添加到INSTALLED_APPS
。
# djsr/djsr/settings.py
# Needed for SIMPLE_JWT
from datetime import timedelta
# ...
INSTALLED_APPS = [
...
'rest_framework' # add rest_framework
]
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES' : (
'rest_framework.permissions.IsAuthenticated' ,
),
'DEFAULT_AUTHENTICATION_CLASSES' : (
'rest_framework_simplejwt.authentication.JWTAuthentication' ,
), #
}
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME' : timedelta(minutes= 5 ),
'REFRESH_TOKEN_LIFETIME' : timedelta(days= 14 ),
'ROTATE_REFRESH_TOKENS' : True,
'BLACKLIST_AFTER_ROTATION' : False,
'ALGORITHM' : 'HS256' ,
'SIGNING_KEY' : SECRET_KEY,
'VERIFYING_KEY' : None,
'AUTH_HEADER_TYPES' : ( 'JWT' ,),
'USER_ID_FIELD' : 'id' ,
'USER_ID_CLAIM' : 'user_id' ,
'AUTH_TOKEN_CLASSES' : ( 'rest_framework_simplejwt.tokens.AccessToken' ,),
'TOKEN_TYPE_CLAIM' : 'token_type' ,
}
默认情况下,我们只允许经过身份验证的查看者访问我们的视图,并且他们可以使用来自simplejwt包的JWTAuthentication进行身份验证。
配置简单的JWT可能会有些复杂。 这里要注意的关键是刷新令牌(持续14天)用于获取访问令牌(持续5分钟)。 用户只有使用有效的访问令牌才能访问视图,否则DRF将返回401未经授权的错误。 我们会轮流刷新令牌,以便用户在14天之内访问时无需再次登录,以便于使用。 您可以在旋转令牌后将其列入黑名单,但在此不做介绍。
如果您没有使用库存user_id
,而是使用诸如电子邮件地址之类的东西,那么您还需要更改USER_ID_FIELD
和USER_ID_CLAIM
以与新的用户ID字段相对应。
请特别注意“ AUTH_HEADER_TYPES
”,因为您稍后在此处放置的任何值都必须反映在React的标头中。 我们将其设置为"JWT"
,但我也看到了“Bearer”
用法。
无需再次进行迁移。
1-2b。 验证并获取刷新和访问令牌
我们需要先将DRF简单JWT URL添加到我们的项目中,以便能够测试登录。
# djsr/djsr/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path( 'admin/' , admin.site.urls),
path( 'api/' , include( 'authentication.urls' ))
]
并在身份验证目录中创建一个新的urls.py
,以便我们可以使用DRF Simple JWT提供的孪生视图来获取令牌对并刷新令牌。
# djsr/authentication/urls.py
from django.urls import path
from rest_framework_simplejwt import views as jwt_views
urlpatterns = [
path( 'token/obtain/' , jwt_views.TokenObtainPairView.as_view(), name= 'token_create' ), # override sjwt stock token
path( 'token/refresh/' , jwt_views.TokenRefreshView.as_view(), name= 'token_refresh' ),
]
现在,将CURL与您先前设置的超级用户凭据一起使用。
$ curl --header "Content-Type: application/json" -X POST http: //127.0.0.1:8000/api/token/obtain/ --data '{"username":"djsr","password":"djsr"}'
{ "refresh" : "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTU2MTYyMTg0OSwianRpIjoiYmE3OWUxZTEwOWJkNGU3NmI1YWZhNWQ5OTg5MTE0NjgiLCJ1c2VyX2lkIjoxfQ.S7tDJaaymUUNs74Gnt6dX2prIU_E8uqCPzMtd8Le0VI" , "access" : "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTYwNDEyNTQ5LCJqdGkiOiJiMmM0MjM4MzYyZjI0MTJhYTgyODJjMTMwNWU3ZTQwYiIsInVzZXJfaWQiOjF9.0ry66-v6SUxiewAPNmcpRt99D8B8bu-fgfqOCpVnN1k" }
繁荣。 代币。 我们已通过认证! 可是等等。 有令牌复数。 Refresh令牌持续14天(我们可以认为已登录),但是Access令牌仅持续5分钟。 这意味着,只要您的用户尝试在没有有效访问令牌的情况下访问某些内容,它将被拒绝,然后您需要从前端向后端发送刷新请求以获取新的请求。 让我们用CURL做到这一点。
1–2c。 刷新令牌
从上方获取刷新令牌,然后再次使用CURL:
$ curl --header "Content-Type: application/json" -X POST http: //127.0.0.1:8000/api/token/refresh/ --data '{"refresh":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTU2MTYyMTg0OSwianRpIjoiYmE3OWUxZTEwOWJkNGU3NmI1YWZhNWQ5OTg5MTE0NjgiLCJ1c2VyX2lkIjoxfQ.S7tDJaaymUUNs74Gnt6dX2prIU_E8uqCPzMtd8Le0VI"}'
{ "access" : "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTYwNDEyOTQ0LCJqdGkiOiI1N2ZiZmI3ZGFhN2Y0MzkwYTZkYTc5NDhhMjdhMzMwMyIsInVzZXJfaWQiOjF9.9p-cXSn2uwwW2E0fX1FcOuIkYPcM85rUJvKBhypy1_c" , "refresh" : "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTU2MTYyMjI0NCwianRpIjoiYWUyZTNiNmRiNTI0NGUyNDliZjAyZTBiMWI3NTFmZjMiLCJ1c2VyX2lkIjoxfQ.peB-nzZRjzgMjcNASp1TZZ510p3lJt7N9SeCWUt0ngI" }
看看那,新的代币! 如果您在settings.py
没有ROTATE_REFRESH_TOKENS:True
,那么Refresh令牌将是相同的,但是由于我们正在轮换使用,因此它也是一个新的Refresh令牌。 只要用户在此过期之前继续访问,就不再需要再次登录。
到底什么构成了JWT? 转至jwt.io并插入您的令牌。对于上面的“刷新令牌”,解码后您将看到。
标头:
{
"typ" : "JWT" ,
"alg" : "HS256"
}
有效负载:
{
"token_type" : "refresh" ,
"exp" : 1561622244 ,
"jti" : "ae2e3b6db5244e249bf02e0b1b751ff3" ,
"user_id" : 1
}
请注意, 令牌!= jti 。 JTI包含在令牌中,以及类型,到期时间和您放入令牌中的任何其他信息。
和访问令牌保存类似的信息。
看看有效载荷如何包含user_id? 您可以使用令牌添加任何想要的信息,您只需要先稍微修改一下声明即可。
1–2d。 自定义获取令牌序列化器和视图
之前,我们在CustomUser
模型上添加了fav_color属性。 首先,进入管理面板127.0.0.1:8000/admin/
并选择一种颜色。
DRF Simple JWT包使开发自定义声明变得非常容易,因此我们可以通过导入和使用原始序列化器子类化,在每个令牌中发送用户喜欢的颜色。
# djsr/authentication/serializers.py
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
class MyTokenObtainPairSerializer ( TokenObtainPairSerializer ):
@ classmethod
def get_token ( cls , user ):
token = super (MyTokenObtainPairSerializer, cls).get_token(user)
# Add custom claims
token[ 'fav_color' ] = user.fav_color
return token
它需要与之相伴的观点。
# djsr/authentication/views.py
from rest_framework_simplejwt.views import TokenObtainPairView
from rest_framework import permissions
from .serializers import MyTokenObtainPairSerializer
class ObtainTokenPairWithColorView ( TokenObtainPairView ):
permission_classes = (permissions.AllowAny,)
serializer_class = MyTokenObtainPairSerializer
在urls.py
需要一个新条目来替换打包的条目。
# djsr/authentication/urls.py
from django.urls import path
from rest_framework_simplejwt import views as jwt_views
from .views import ObtainTokenPairWithColorView
urlpatterns = [
path( 'token/obtain/' , ObtainTokenPairWithColorView.as_view(), name= 'token_create' ),
path( 'token/refresh/' , jwt_views.TokenRefreshView.as_view(), name= 'token_refresh' ),
]
旧的Refresh令牌仍然可以获取新的Access令牌,因此,此时您应该将所有未使用的令牌列入黑名单,以有效地注销所有人。
要查看运行中的新令牌,请再次使用CURL。
$ curl --header "Content-Type: application/json" -X POST http: //127.0.0.1:8000/api/token/obtain/ --data '{"username":"djsr","password":"djsr"}'
{ "refresh" : "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTU2MTYyNjQ5NywianRpIjoiODVhMmRlNWUyNjQ0NGE1ZWFmOGQ1NDAzMmM1ODUxMzIiLCJ1c2VyX2lkIjoxLCJmYXZfY29sb3IiOiIifQ.1eJr6XVZXDm0nmm19tyu9WP9AfdY8Ny_D_tK4Qtvo9E" , "access" : "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTYwNDE3MTk3LCJqdGkiOiI5ZjE4NmM4OTQ0ZWI0NGYyYmNmYjZiMTQ5MzkyY2Y4YSIsInVzZXJfaWQiOjEsImZhdl9jb2xvciI6IiJ9.Ad2szXkTB4eOqnRk3GIcm1NDuNixZH3rNyf9RIePXCU" }
在那里,您可以在已解码令牌中看到自己的收藏夹颜色。
凉。 为什么这有用? 取决于您的实现,但是最好有一些额外的上下文以及令牌,或者使用自定义序列化器/视图执行自定义操作。 这并不意味着要充当任何类型的get_user_info()
函数。 不要那样用。
现在,我们可以登录现有用户并给他们一个令牌。 我们还有两件事要做。 注册用户并创建受保护的视图。
1–2e。 注册用户
令人惊讶的(或没有)创建新用户与JWT毫无关系。 这只是原始的Django Rest Framework。
我们不需要对CustomUser
模型做任何事情,但是我们需要为其创建一个序列化器,并将其放入带有URL的视图中。
首先, CustomUserSerializer
模型序列化器。 如果您不熟悉Django Rest Framework,则序列化程序主要负责将JSON转换为可用的Python数据结构,然后采取相应措施。 有关串行器的更多信息,请参阅非常好的DRF文档 。 我们使用的这个序列化器是超级典型的。
# djsr/authentication/serializers.py
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from rest_framework import serializers
from .models import CustomUser
# ...
class CustomUserSerializer(serializers.ModelSerializer):
"" "
Currently unused in preference of the below.
" ""
email = serializers.EmailField(
required=True
)
username = serializers.CharField()
password = serializers.CharField(min_length= 8 , write_only=True)
class Meta :
model = CustomUser
fields = ( 'email' , 'username' , 'password' )
extra_kwargs = { 'password' : { 'write_only' : True}}
def create(self, validated_data):
password = validated_data.pop( 'password' , None)
instance = self.Meta.model(**validated_data) # as long as the fields are the same, we can just use this
if password is not None:
instance.set_password(password)
instance.save()
return instance
对于我们的视图集,而不是使用ModelViewSet
,我们创造我们自己只是一个POST端点观点。 对于CustomUser
对象的任何GET请求,我们都有一个不同的终结CustomUser
。
在settings.py
由于REST_FRAMEWORK的权限默认值仅是经身份验证的用户只能访问的视图,因此我们必须将权限显式设置为AllowAny ,否则尝试注册并向您付款的新用户将获得未授权的错误。 坏枣
当像我们在此处将数据馈送到模型序列化程序时,只要序列化程序具有create()或update()方法,就可以使用serializer.save()神奇地创建(或更新)相应的对象(在我们的案例CustomUser
)并返回实例。 文件。
# djsr/authentication/views.py
from rest_framework_simplejwt.views import TokenObtainPairView
from rest_framework import status, permissions
from rest_framework.response import Response
from rest_framework.views import APIView
from .serializers import MyTokenObtainPairSerializer, CustomUserSerializer
class ObtainTokenPairWithColorView ( TokenObtainPairView ):
serializer_class = MyTokenObtainPairSerializer
class CustomUserCreate ( APIView ):
permission_classes = (permissions.AllowAny,)
def post(self, request, format= 'json' ):
serializer = CustomUserSerializer(data=request.data)
if serializer.is_valid():
user = serializer.save()
if user:
json = serializer.data
return Response(json, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
最后,在urls.py
我们添加了新视图。
# djsr/authentication/urls.py
from django.urls import path
from rest_framework_simplejwt import views as jwt_views
from .views import ObtainTokenPairWithColorView, CustomUserCreate
urlpatterns = [
path( 'user/create/' , CustomUserCreate.as_view(), name= "create_user" ),
path( 'token/obtain/' , ObtainTokenPairWithColorView.as_view(), name= 'token_create' ),
path( 'token/refresh/' , jwt_views.TokenRefreshView.as_view(), name= 'token_refresh' ),
]
查看+序列化器+ URL =很好。
此时,您将必须确保对其进行测试,因为如果此视图仅限于经过身份验证的用户,则将获得0个新用户。 他们会很不高兴,并且当您有用户时,吸引投资者的几率会上升。 这意味着更多的卷曲。
$ curl --header "Content-Type: application/json" -X POST http: //127.0.0.1:8000/api/user/create/ --data '{"email":"[email protected]","username":"ichiro1","password":"konnichiwa"}'
{ "email" : "[email protected]" , "username" : "ichiro1" }
答对了! 效果很好。
在Django Rest Framework方面,我们的最后一步是创建一个受保护的视图,供我们尝试访问。
1–2f。 创建和测试受保护的视图
我们判断所有这些操作是否奏效的唯一方法是创建一个虚拟视图……我们将其称为HelloWorld……并在尝试使用和不使用Javascript Web令牌身份验证令牌访问它时对其进行保护。
让我们做一个最简单的视图。
# djsr/authentication/views.py
...
class HelloWorldView(APIView):
def get(self, request):
return Response(data={ "hello" : "world" }, status=status.HTTP_200_OK)
并将其添加到urls.py
。
# djsr/authentication/urls.py
from django.urls import path
from rest_framework_simplejwt import views as jwt_views
from .views import ObtainTokenPairWithColorView, CustomUserCreate, HelloWorldView
urlpatterns = [
path( 'user/create/' , CustomUserCreate.as_view(), name= "create_user" ),
path( 'token/obtain/' , ObtainTokenPairWithColorView.as_view(), name= 'token_create' ),
path( 'token/refresh/' , jwt_views.TokenRefreshView.as_view(), name= 'token_refresh' ),
path( 'hello/' , HelloWorldView.as_view(), name= 'hello_world' )
]
返回CURL。 如果我们正确执行了此操作,则没有令牌的API请求将失败。
$ curl --header "Content-Type: application/json" -X GET http: //127.0.0.1:8000/api/hello/
{ "detail" : "Authentication credentials were not provided." }
如预期的那样。 现在有了凭据。 确保先刷新它们,否则访问令牌已过期。 或尝试使用新用户。
$ curl --header "Content-Type: application/json" -X POST http: //127.0.0.1:8000/api/token/obtain/ --data '{"username":"ichiro1","password":"konnichiwa"}'
{ "refresh" : "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTU2MTYzODQxNiwianRpIjoiMGM5MjY5NWE0ZGQwNDUyNzk2YTM5NTY3ZDMyNTRkYzgiLCJ1c2VyX2lkIjoyLCJmYXZfY29sb3IiOiIifQ.sV6oNQjQkWw2F3NLMQh5VWWleIxB9OpmIFvI5TNsUjk" , "access" : "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTYwNDI5MTE2LCJqdGkiOiI1NWVlZDA4MGQ2YTg0MzI4YTZkZTE0Mjg4ZjE3OWE0YyIsInVzZXJfaWQiOjIsImZhdl9jb2xvciI6IiJ9.LXqfhFifGDA6Qg8s4Knl1grPusTLX1lh4YKWuQUuv-k" }
$ curl --header "Content-Type: application/json" -X GET http: //127.0.0.1:8000/api/hello/
{ "detail" : "Authentication credentials were not provided." }
$ curl --header "Content-Type: application/json" --header "Authorization: JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTYwNDI5MTE2LCJqdGkiOiI1NWVlZDA4MGQ2YTg0MzI4YTZkZTE0Mjg4ZjE3OWE0YyIsInVzZXJfaWQiOjIsImZhdl9jb2xvciI6IiJ9.LXqfhFifGDA6Qg8s4Knl1grPusTLX1lh4YKWuQUuv-k" -X GET http: //127.0.0.1:8000/api/hello/
{ "hello" : "world" }
首先,我们登录了新用户Ichiro,然后在受保护的端点上尝试了GET请求。 它仍然被拒绝,因为尽管我们可以用肉眼看到它,但实际上并未传递令牌。 它必须在标题中传递。 这就是为什么要记住在settings.py中我们在AUTH_HEADER_TYPES
设置“JWT”
的AUTH_HEADER_TYPES
。
在标头中,令牌必须以“Authorization: JWT “ + access token
。 或将AUTH_HEADER_TYPES
设置为的任何内容。 否则,没有骰子。 当我们连接前端时,这将变得很重要。
如您所见,当我们这样做时,我们能够获得视图的响应{"hello":"world"}
!
惊人。 现在,我们可以使用JWT Refresh和Access令牌对自定义用户进行身份验证,并且仅当在标头中传输Access令牌时才允许他们访问受保护的视图。
如果您等待了5分钟并尝试使用过期的访问令牌怎么办?
$ curl -Type: application/json " --header " Authorization: JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTYwNDI5MTE2LCJqdGkiOiI1NWVlZDA4MGQ2YTg0MzI4YTZkZTE0Mjg4ZjE3OWE0YyIsInVzZXJfaWQiOjIsImZhdl9jb2xvciI6IiJ9.LXqfhFifGDA6Qg8s4Knl1grPusTLX1lh4YKWuQUuv-k " -X GET http://127.0.0.1:8000/api/hello/
{" detail ":" Given token not valid for any token type "," code ":" token_not_valid "," messages ":[{" token_class ":" AccessToken "," token_type ":" access "," message ":" Token is invalid or expired "}]}
在Django服务器控制台中,您将能够看到魔术:
Unauthorized: /api/ hello/
[ 13 /Jun/ 2019 12 : 53 : 29 ] "GET /api/hello/ HTTP/1.1" 401 183
该视图将无法访问,您必须刷新。 手动使用CURL进行操作非常繁琐,但是在前端框架内,它很容易实现自动化。
接下来,我们将继续使用ReactJS创建前端以使用我们的API。
第1–2节的GitHub代码位于此处 。
设置前端框架以使用Django可以通过多种方法来完成。 它们可以完全分开,仅通过API进行联系。 在这种情况下,本地开发将运行Django开发服务器和单独的React开发服务器,而部署将涉及独立于后端部署的前端。 例如,当Django托管在EC2服务器上时,对AWS S3做出反应。 这将需要一些有趣的CORS配置。
另一方面,React可以在Django自己的模板系统中更深入地交织在一起,以便Django处理为模板提供服务,但您仍然可以使用React magic。 首先,它有点不利于拥有前端框架的目的。
本教程使用中间方法-将React安装在独立的Django应用程序中。 CORS头文件不需要太多工作,您将获得React框架的全部好处。 有一定的警告。 同一服务器将负责提供所有数据,这可能会减慢数据速度。 如果您想提供一些Django URL,例如运行状况检查或/ admin,URL路由也有些棘手。 初始设置也很麻烦。 我们将逐步解决。
首先要做的是制作一个新的Django应用来保存React。
$ cd djsr
$ python manage.py startapp frontend
并将其添加到settings.py
的INSTALLED_APPS
中。
在frontend中创建一个templates/frontend/index.html
文件,它将作为React以及常规Django渲染索引视图的基础模板。 在此特定模板中,可以使用Django模板上下文处理器,如果要从settings.py
控制React的行为,这将非常有用。 现在,让我们像标准Django基本模板一样准备它。
{% load static %}
DRF + React = Winning the game
This will be the base template.
并在使用时制作最小的style.css。
// djsr/fontend/static/frontend/style.css
#root{
background-color:rebeccapurple;
}
我们以视图和更新的URL完成此操作。
# djsr/djsr/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path( 'admin/' , admin.site.urls),
path( 'api/' , include( 'authentication.urls' )),
path( '' , include( 'frontend.urls' ))
]
确保将这个include放在urlpatterns
。 这样,任何不匹配Django URL的内容都将由前端处理,这使我们可以使用React的路由器来管理前端视图,同时仍将其托管在同一服务器上。 因此很好地避免了CORS的疯狂。
# djsr/frontend/views.py
from django.shortcuts import render
# Create your views here.
def index(request):
return render(request, 'frontend/index.html' , context=None)
此视图呈现index.html
,它将作为所有React的基础模板。 它需要做的就是渲染模板。 如果需要,可以添加上下文。
最后,我们将此视图添加到前端URL。 我们必须添加两次,首先捕获空URL,例如https://lollipop.ai,然后捕获其他所有URL,例如https://lollipop.ai/lollisignup/ 。
# djsr/frontend/urls.py
urlpatterns = [
path( '' , index_view), # for the empty url
url(r '^.*/$' , index_view) # for all other urls
]
最后,再次运行服务器,查看索引是否正确显示。
$ cd ../.. (so you 're in project root: django-jwt-react)
$ python djsr/manage.py runserver
导航到http://127.0.0.1/并成功看到此漂亮的几乎为空的页面。 而且您会在http://127.0.0.1:8000/asdjfklasdjfklasdfjklasdf/上看到相同的内容,从而向我们展示了我们并没有弄乱。 至少还没有。 凉。 现在我们可以开始使用React了。
请注意,正则表达式以/ $结尾。 默认情况下,Django在每个URL的末尾添加一个/,因此在React中进行路由时,每个路径的末尾都需要一个/。 这有助于强制执行该纪律。
现在我们可以开始在前端应用程序中设置React了。 这不是一项简单的任务,我们不能仅仅依靠Create React App。
相反,我们改编了Jedai Saboteur的精彩教程 ,创建了自己的工具链。
确保您位于应用程序的根文件夹(带有Pipfile)。 首先创建package.json
文件。 此处问题的答案无关紧要,因此只需将其保留为默认值即可。
$ npm init
现在,在frontend
Django应用程序中,创建一个src
目录。 这将保留我们的React组件。 在static/frontend
目录中,创建另一个名为public的目录,以保存已编译的React文件。
现在,Django应用程序目录应如下所示:
djsr
+-- authentication/
+-- djsr/
+-- frontend/
| +-- migrations/
| +-- src/
| +-- static /
| | +-- frontend/
| | | +-- public/
| | | +-- style.css
| +-- templates/
| | +-- frontend/
| | | +-- index.html
+--db.sqlite3
+--manage.py
打开index.html
并将这有点尴尬的行添加到正文的底部。 我们仍然可以在此html文件中使用Django的模板魔术。