接口概念

  IOP:面向接口编程,不再关注具体的实现;只关注输入、输出。

  http://www.ruanyifeng.com/blog/2018/10/restful-api-best-practices.html

服务器返回数据类型:

  网页数据html,为浏览器使用

  Json数据,ajax javascript发请求的一种方式;也可以使用request的python请求方式

为移动端编写接口关注:

  接口地址是什么:/xxx/yyy/zzz

  接口需要什么参数:参数根据业务场景

  返回什么格式的数据:大多数是json数据

RestfulAPI:

   一种软件架构风格、设计风格、而不是标准,只是提供了一组设计原则和约束条件。它主要用户客户端和服务器交互类的软件。

  基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存机制等。REST全程是Representational State Transfer,表征性状态转移。

  首次在2000年Roy Thomas Fielding的博士论文中出现,Fielding是一个非常重要的人,他是HTTP协议(1.0版和1.1版)的主要设计者,

  Apache服务器软件的作者之一,Apache基金会的第一任主席。所以,他的这篇论文一经发表,就引起了广泛的关注。

理解Rest-Formwork

介绍:https://github.com/RockTeach/PythonCourse/blob/master/web/flask/restful.md

  要理解RESTful架构,最好的就是去理解它的单词 Representational State Transfer 到底是什么意思,它的每一个词到底要表达什么。

  REST的释义,"(资源的)表现层状态转化",其实这省略了主语。“表现层”其实指的是“资源(Resource)”的“表现层”。

状态码

  服务器向用户返回的状态码和提示信息,常见的有以下一些地方

  • 200:OK - [GET]:服务器成功返回用户请求的数据
  • 201:CREATED -[POST/PUT/PATCH]:用户新建或修改数据成功
  • 202:Accepted - [*] :表示一个请求已经进入后台排队(异步任务)
  • 204:NO CONTENT - [DELETE]:表示数据删除成功
  • 400:INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误
  • 401:Unauthorized - [*] :表示用户没有权限(令牌,用户名,密码错误)
  • 403:Forbidden - [*]:表示用户得到授权,但是访问是被禁止的
  • 404:NOT FOUND - [*]:用户发出的请求针对的是不存在的记录
  • 406:Not Acceptable - [*]:用户请求格式不可得
  • 410:Gone - [GET] :用户请求的资源被永久移除,且不会再得到的
  • 422:Unprocesable entity -[POST/PUT/PATCH]:当创建一个对象时,发生一个验证错误
  • 500:INTERNAL SERVER EROR - [*] :服务器内部发生错误

资源(Resource)

  所谓“资源”,就是网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本,一张图片,一首歌曲,一种服务,总之就是一个具体的实例。

  你可以使用一个URI(统一资源定位符)指向它,每种资源对应一个特定的URI。要获取这个资源,访问它的URI就可以了,因此URI就成了每一个资源的地址或独一无二的识别符。

  所谓“上网”就是与互联网上一系列的“资源”互动,调用它们的URI。

表现层(Representation)

  “资源”是一种信息实体,它可以有多种外在表现形式。我们把“资源”具体呈现出来的形式,叫做它的”表现层“(Representation)。

  URI只代表资源的实体,不代表它的形式。严格地说,有些网站最后的”.html“后缀名是不必要的,因为这个后缀表示格式,属于”表现层“范畴,而URI应该只代表”资源“的位置。

  它的具体表现形式,应该在HTTP请求头的信息中使用Accept和Content-Type字段指定。

状态转换(State Transfer)

  访问一个网站,就代表客户端和服务端的一个互动过程。在这个过程中,势必涉及到数据和状态的变化。

  互联网通信协议HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在服务端。

  因此,如果客户端想要操作服务器,就必须通过某种手段,让服务器端发生”状态转换(State Transfer)“。

  而这种转换是建立在表现层之上的,所以就是”表现层状态转化“。

  客户端用到的手段,只能是HTTP协议。具体来说,就是HTTP协议中,四个表示操作方式的动词:GET,POST,PUT,DELETE。

  它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源(也可用于更新资源),PUT用来更新资源,DELETE用来删除资源

总结 :

  1. 每一个URI代表一种资源
  2. 客户端和服务器之间,传递这种资源的某种表现层
  3. 客户端通过四个HTTP动词,对服务端资源进行操作,实现”表现层状态转换“
  4. 同一个url针对用户的不同的请求操作,表现出来的状态是不同的。表现出来的多种形式,就是表现层状态转换。
  • RESTful是软件架构设计思想。使用在CS,客户端和服务端这种架构模型中。

  • 表现层状态转换

    • 主语 (资源)

    • URI 每个URI代表一种资源

    • 资源展现给我们的形式就叫做表现层

    • 通过HTTP的请求谓词来实现表现层转换

  • 重要概念

    • URI、HTTP请求谓词、JSON

注意:

  postman/前端 在向服务器提交json数据时,需要声明提交的类型。

  在postman的请求的headers增加content-type:application/json。

  flask 在确认请求数据是通过json提交后,会将json字符产转换成 字典。保存在request.json中

  函数视图:FBV(function base views);  类视图:CBV(class base views)

图示:

  • 前后端统一开发

    

  • 前后端分离开发

    


FBV简单体验:由源码中的dispatch进行各请求分发

  • api/urls.py
 from django.conf.urls import url
from API import views urlpatterns = [
url(r'^books/$', views.books, name='books'),
url(r'^books/(?P<bookid>\d+)/', views.book, name='book'),
  • api/models.py
 from django.db import models

 class Book(models.Model):
b_name = models.CharField(max_length=32)
b_price = models.FloatField(default=1) def to_dict(self):
return {'id': self.id, 'b_name': self.b_name, 'b_price': self.b_price}
  • api/views.py  [选中此文件,右键--> Refactor-->Convert To Python Package 将其变成一个包文件] [from .BookModel import Book 导入到在此包文件中的init.py文件]
 from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from API.models import Book @csrf_exempt # 进行csrf豁免
def books(request):
# get 获取书
if request.method == "GET":
book_list = Book.objects.all()
book_list_json = []
for book in book_list:
book_list_json.append(book.to_dict())
data = {
'status': 200,
'msg': 'ok',
'data': book_list_json
}
return JsonResponse(data=data)
# post 创建书
elif request.method == "POST":
b_name = request.POST.get('b_name')
b_price = request.POST.get('b_price')
book = Book()
book.b_name = b_name
book.b_price = b_price
book.save()
data = {
'status': 201,
'msg': 'add success',
'data': book.to_dict()
}
return JsonResponse(data=data, status=201) # 注意: 在pycahrm工具栏:Tools ---> HTTP Client ---> Test RESTful web server 进行模拟请求。亦可HTTPie和Postman @csrf_exempt
def book(request, bookid):
# get 带参数获取某一本书
if request.method == "GET":
book_obj = Book.objects.get(pk=bookid)
data = {
'msg': 'ok',
'status': 200,
'data': book_obj.to_dict()
}
return JsonResponse(data=data)
# delete 带参数删除某一个书
elif request.method == "DELETE":
book_obj = Book.objects.get(pk=bookid)
book_obj.delete()
data = {
'msg': 'delete success',
'status': 204, # 前端看到的状态码
# 'data': {} 如果某个对象删除后没有数据了,有默认值就返回对应的数据类型格式,否则前端不知如何解析
}
return JsonResponse(data=data, # status=204) # status=204 设置真正的网络传输状态码204,舍弃响应体中的内容
  • books.html
 <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>BookList</title>
<script type="text/javascript" src="/static/js/jquery.js"></script>
<script type="text/javascript">
$(function () {
// $("button").click(function () {
$.getJSON("/api/books/", function (data) {
console.log(data);
if (data['status'] === 200){
var $ul = $("ul");
var books = data['data'];
for(var i=0; i < books.length; i++){
var $li = $("<li></li>");
$li.html(books[i]['b_name']);
$li.appendTo($ul);
}
}
})
// })
}) </script>
</head>

CBV简单体验:由源码中的dispatch进行各请求分发

  •  urls.py
from django.contrib import admin
from django.urls import path
from user.views import UserView, RegisterView urlpatterns = [
path('admin/', admin.site.urls),
path('register/', RegisterView.as_view(), name='register'),
path('user/<int:uid>', UserView.as_view(), name='user'),
]
  • views.py
 # Create your views here.
from django.http import HttpResponse
from django.views import View 6 # 用FBV
7 def indexs(request):
8 return HttpResponse('基础流程') # 用CRF: 实现登陆注册
class RegisterView(View):
# 用户登陆
def get(self, request):
pass
# 用户注册
def post(self, request):
pass # 用CRF: get post put patch delete
class UserView(View):
# 请求get,获取信息
def get(self, request, uid):
pass
# 提交post,添加信息
def post(self, request, uid):
pass
# 修改put,全部修改
def put(self, request, uid):
pass
# 修改patch,差量修改
def patch(self, request, uid):
pass
# 删除delete,删除操作
def delete(self, request, uid):
pass

使用:

  • urls.py  只能接收已经存在的属性作为参数
 from django.conf.urls import url
from CBV import views urlpatterns = [
url(r'^hello/', views.HelloCBV.as_view(msg='Sleeping'), name='hello'),
    # as_view(msg)中的参数msg(变量名),只能接收此路由对应的类视图中已经存在的属性作为参数
]
  • views.py
 from django.http import HttpResponse
from django.views import View class HelloCBV(View): msg = None # 类视图中只有设置了此msg属性,路由中as_view(msg)中的变量名msg才能接收到并生效(名字要对应好)
def get(self, request):
return HttpResponse("hahaha %s" % self.msg)
def post(self, request):
return HttpResponse("POST 666")
def put(self, request):
return HttpResponse("PUT 666")

Api接口 CBV方式 利用在模型中定义[对象转字典]的类方法 来简单实现

  • urls.py
from django.contrib import admin
from django.urls import path
from user.views import UserView urlpatterns = [
path('user/<int:uid>', UserView.as_view(), name='user'),
]
  • models.py
 from django.db import models

 class User(models.Model):
username = models.CharField(max_length=20, unique=True, verbose_name='用户名')
password = models.CharField(max_length=128, verbose_name='密码') class Meta:
db_table = 'user'
verbose_name = '用户表'
verbose_name_plural = verbose_name def __str__(self):
return self.username
def to_dict(self): # 在模型中定义类方法,实现模型对象的序列化,转为字典形式
return {'id': self.id, 'username': self.username, 'password': self.password}
  • views.py
 from django.http import JsonResponse
from django.views import View
from user.models import User class UserView(View):
def get(self, request, uid):
user = User.objects.get(pk=uid)
return JsonResponse({'status': 200, 'user': user.to_dict()})
       # 在模型中定义类方法,实现模型对象的序列化,转为字典形式 def post(self, request, uid):
username = request.POST.get('username')
password = request.POST.get('password')
repassword = request.POST.get('repassword')
if password == repassword:
user = User.objects.create(username=username, password=password)
if user:
return JsonResponse({'status': 200, 'msg': '注册成功'})
return JsonResponse({'status': 200, 'msg': '注册失败'})


Django-Rest-Formwork

安装配置

  • pip install djangorestframework 若使用序列化和类视图继承view或APIView,必须安装此行
  • 添加:INSTALLED_APPS = ['user.apps.UserConfig',  'rest_framework', ]在settings.py文件中

        

初步体验:

  • urls.py  请求:某个http://127.0.0.1:8000/groups/2/ 或 某组http://127.0.0.1:8000/groups
 from django.conf.urls import url, include
from rest_framework.routers import DefaultRouter
from drf.views import UserViewSet, GroupViewSet # router做了路由注册
router = DefaultRouter()
router.register(r'users',UserViewSet)
router.register(r'groups',GroupViewSet) urlpatterns = [
url(r'^',include(router.urls)),
]
  • drf/serializers.py
 from django.contrib.auth.models import User, Group
from rest_framework import serializers # HyperlinkedModelSerializer 实现超链接
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User # 系统自带的user
fields = ('url', 'username', 'email', 'groups')
# HyperlinkedModelSerializer 实现超链接
class GroupSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Group # 系统自带的group
fields = ('url', 'name')
  • drf/views.py
 from django.contrib.auth.models import User, Group
from rest_framework import viewsets
from drf.serializers import UserSerializer, GroupSerializer # 继承自:viewsets.ModelViewSet。对数据集合实现了:get、post
# 本身是一个CBV,也是一个视图集合。 对单个集合实现了:get、post、delete、put、patch封装
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer # 继承自:viewsets.ModelViewSet。对数据集合实现了:get、post
# 本身是一个CBV,也是一个视图集合。 对单个集合实现了:get、post、delete、put、patch封装
class GroupViewSet(viewsets.ModelViewSet):
queryset = Group.objects.all()
serializer_class = GroupSerializer

 注意:一般不会用这种继承方式,不灵活


序列化器

  • 序列化:   表示将对象转换成可以在IO[网络]中传输的格式如json
  • 反序列化:将在IO[网络]中传输的格式[如json]转化为对象的形式。

基本使用1:

  • urls.py
 from django.conf.urls import url, include

 urlpatterns = [
url(r'^ser/', include('RestSerializers.urls')),
]
  • RestSerializers/urls.py
 from django.conf.urls import url
from RestSerializers import views urlpatterns = [
url(r'^persons/', views.PersonView.as_view()),
url(r'^students/', views.StudentView.as_view()),
url(r'^books/', views.books), # FBV
]
  • RestSerializers/models.py
 from django.db import models

 class Person(models.Model):
p_name = models.CharField(max_length=32)
p_age = models.IntegerField(default=1)
p_sex = models.BooleanField(default=False) class Student(models.Model):
s_name = models.CharField(max_length=32)
s_age = models.IntegerField(default=1)
  • RestSerializers/serializers.py
 from rest_framework import serializers
from RestSerializers.models import Person, Student, Book # serialization的子类
"""
模块serializers中
1。Serializer: 手动原生序列化。有创建和更新两个功能
2。ModelSerializer:模型序列化。
3。HyperLinkedModelSerializer:带超链接的模型序列化工具
"""
class PersonSerializer(serializers.Serializer):
    # 手动设置需要序列还的字段
id = serializers.IntegerField(read_only=True)
p_name = serializers.CharField(max_length=32)
p_age = serializers.IntegerField(default=1)
p_sex = serializers.BooleanField(default=False) # 实现抽象方法,手动创建对象。validated_data验证过的数据
def create(self, validated_data):
return Person.objects.create(**validated_data) # 实现抽象方法,手动更新对象。validated_data验证过的数据,instance实例
def update(self, instance, validated_data):
instance.p_name = validated_data.get('p_name', instance.p_name) # 没有获取到,给其原有值
instance.p_age = validated_data.get('p_age', instance.p_age)
instance.p_sex = validated_data.get('p_sex', instance.p_sex)
instance.save()
return instance class StudentSerializer(serializers.ModelSerializer):
class Meta:
model = Student
fields = ('s_name', 's_age')
       # fields:包含什么字段;exclude:不包含什么字段
  • RestSerializers/views.py
 from django.http import JsonResponse
from django.views import View
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.views import APIView
from RestSerializers.models import Person, Student
from RestSerializers.serializers import PersonSerializer, StudentSerializer, class PersonView(View): def get(self, request):
persons = Person.objects.all()
# many=True 表示有多个需要序列化转化
person_serializer = PersonSerializer(persons, many=True)
return JsonResponse(person_serializer.data, safe=False) def post(self, request):
p_name = request.POST.get("p_name")
p_age = request.POST.get("p_age")
person = Person()
person.p_name = p_name
person.p_age = p_age
person.save()
person_serializer = PersonSerializer(person)
return JsonResponse(person_serializer.data) class StudentView(APIView): def post(self, request):
s_name = request.POST.get('s_name')
s_age = request.POST.get('s_age')
student = Student()
student.s_name = s_name
student.s_age = s_age
student.save()
student_serializer = StudentSerializer(student)
# 只要类视图继承自APIView,request类型就变成了<class 'rest_framework.request.Request'>
print(type(request))
return JsonResponse(student_serializer.data) 

基本使用2:

  • urls.py
 from django.contrib import admin
from django.urls import path
from user import views urlpatterns = [
path('admin/', admin.site.urls),
path('oneuser/', views.UserViews.as_view(), name='user'),
path('twouser/', views.UserViewsSimple.as_view(), name='user'),
]
  • user/models.py
 from django.db import models

 class User(models.Model):
username = models.CharField(max_length=20, unique=True)
password = models.CharField(max_length=128)
phone = models.CharField(max_length=11)
add_time = models.DateTimeField(auto_now=True) class Meta:
db_table = 'user' # 数据库中此模型的表名称 def __str__(self):
return self.username
  • user/serializers.py  在此文件内构件序列化类。继承自serializers.Serializer 和 继承自serializers.ModelSerializer
 from django.contrib.auth.hashers import make_password
from rest_framework import serializers
from user.models import User # UserSerializer class中的第一部分定义了有哪些字段需要被序列化/反序列化。
# 方法create()和方法update()部分定义了当调用serializer.save()方法时,serializer应该怎样构造实例 # 基础写法。建序列化类,继承自serializers.Serializer
class UserSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True) # read_only=True 只读,不可修改
username = serializers.CharField(max_length=20, min_length=6, required=True) # required=True 必填项
password = serializers.CharField(max_length=128, required=True) # max_lenth = 128 最长是128
phone = serializers.CharField(max_length=11, min_length=11) # min_lenth = 11 最短是11

# -------------------------------------------------------------------------------------------------------#
# UserSerializerSimple继承自ModelSerializer;
# ModelSerializer继承自Serializer;ModelSerializer中重写了BaseSerializer父类中的create()方法
# Serializer继承自BaseSerializer。
# BaseSerializer有save()方法和create()方法。所以UserSerializerSimple就可以调用save()方法 # 简化写法。建序列化类,继承自serializers.ModelSerializer
class UserSerializerSimple(serializers.ModelSerializer):
# 新增需要验证的字段,且数据库中没有此字段。需要write_only=True表示只是在前端验证,不会进到模型中去
repassword = serializers.CharField(max_length=128, write_only=True) class Meta:
model = User
# fields = '__all__' 对所有字段进行校验
# exclude = ('id') 排除不想校验的字段
fields = ['id', 'username', 'password', 'phone','repassword'] # 需要序列化的字段有哪些 # 重写父类方法 验证密码一致。重写父类中的validate进行全局验证。
    # attrs是传递来的所有参数的字典形式。如果只验证一个参数,如验证密码长度可以直接把password当作参数
def validate(self, attrs):
if attrs['password'] != attrs['repassword']:
raise serializers.ValidationError('两次密码不相等')
return attrs # 重写父类方法 实现密码加密。重写父类中的create方法。validated_data是
def create(self, validated_data):
username = validated_data['username'] # validated_data父类中的
password = validated_data['password']
phone = validated_data['phone']
password = make_password(password) # 密码加密
user = User.objects.create(username=username, password=password, phone=phone) return user
  • user/views.py
 from django.contrib.auth.hashers import make_password
from django.http import JsonResponse
from rest_framework.views import APIView
from user.models import User
from user.serializers import UserSerializer, UserSerializerSimple # 基本实现 序列化
class UserViews(APIView): # APIView是View的子类
# get请求
def get(self, request):
pass # post请求 用户注册。http://127.0.0.1:8000/oneuser/
def post(self, request):
username = request.POST.get('username')
password = request.POST.get('password')
phone = request.POST.get('phone') password = make_password(password) # 密码加密
# 1。创建并得到一个用户对象
user = User.objects.create(username=username, password=password, phone=phone)
# 2。放到UserSerializer类中得到一个用户序列化对象。
user_serializer = UserSerializer(user)
# 3。将序列化的数据(json)返回出去。获取序列化中的数据的方法是:序列化类对象名.data(得到序列化之后的数据,打印是字典类型)
return JsonResponse({'status': 200, 'user': user_serializer.data}) #----------------------------------------------------------------------------------------------------------------#
# 简化实现 序列化
class UserViewsSimple(APIView): # APIView是View的子类
# get请求
def get(self, request):
pass
# post请求 用户注册。http://127.0.0.1:8000/twouser/
def post(self, request):
# 1。将前端传递的数据送到Serializer中验证,获取序列化对象。只有在继承自APIView中的子类才可以使用request.data
user_serializer = UserSerializerSimple(data=request.data)
# 2。# 判断前端传来的数据时候是否符合models.py中定义的数据格式要求
if user_serializer.is_valid():
user_serializer.save()
return JsonResponse({'status': 200, 'user': user_serializer.data})
return JsonResponse({'status':400})

总结:

方法:

  • user_serializer.is_valid( )
    • 序列化类对象.is_valid() :此方法功能是判断传入的数据是否符合models中定义的数据格式要求
  • user_serializer.data
    • 序列化类对象.data :此方法功能是 将序列化后的数据获取出来
  • user_serializer.save()
    • 序列化类对象.save() : 此方法功能是 将验证通过的数据存到数据库中
    • user_serializer.save() 底层save()两个动作create,update

  • request.data
    • request.data :此方法功能是 获取前端传递来的所有数据。只有类视图继承自APIView父类,才会有此方法
    • data = JSONParser().parse(request)

注意:

  • ModelSerializer需要解决的两个问题:

    • 某个字段不属于指定model,它是write_only,需要用户传进来,但我们不能对它进行save( ),因为ModelSerializer是基于Model,这个字段在Model中没有对应属性字段,这个时候我们需要重载validate!write_only与read_only对应。就是用户post过来的数据,后台服务器处理后不会再经过序列化后返回给客户端;最常见的就是我们在使用手机注册的验证码和填写的密码。required: 顾名思义,就是这个字段是否必填,例如要求:用户名,密码等是必须填写的;不填写就直接报错
allow_null/allow_blank:是否允许为NULL/空 。 
error_messages:出错时,信息提示。
    • 某个字段属于指定model,它是read_only,read_only:True表示不允许用户自己上传,只能用于api的输出。如果某个字段设置了read_only=True,那么就不需要进行数据验证,只会在返回时,将这个字段序列化后返回,举个简单的例子:在用户进行购物的时候,用户post订单时,肯定会产生一个订单号,而这个订单号应该由后台逻辑完成,而不应该由用户post过来,如果不设置read_only=True,那么验证的时候就会报错。再例如,我们在网上购物时,支付时通常会产生支付状态,交易号,订单号,支付时间等字段,这些字段都应该设置为read_only=True,即这些字段都应该由后台产生然后返回给客户端;
    • write_only:前端必须传递过来,但是序列化时不被序列化
    • read_only: 前端必须传递过来,但是序列化的时候会自动给出

补充:

  • 新建的app包文件,需要调用models.py,生成迁移文件时,必须要将app包文件注册到settings.py文件中。
  • 如果只是调用路由和视图函数文件,不需要models文件时,不注册新建的app包文件在settings.py也没关系

 

最新文章

  1. JS函数相关及递归函数的使用
  2. 2014百度之星资格赛 1004:Labyrinth(DP)
  3. linux下生成rsa密钥的方法
  4. 仿酷狗音乐播放器开发日志二十三 修复Option控件显示状态不全的bug(附源码)
  5. 《cracking the coding intreview》——链表
  6. AWS EC2 CentOS release 6.5 部署zookeeper、kafka、dubbo
  7. Ubuntu ROS Arduino Gazebo学习镜像iso说明(indigo版)
  8. Ocelot统一权限验证
  9. ASLP Kaldi
  10. Docker 入门指南——常用命令
  11. API服务网关(Zuul)
  12. 算法训练 Tricky and Clever Password
  13. 各种java面经资源
  14. mysql 解除正在死锁的状态
  15. jquery ajax调用WCF,采用System.ServiceModel.WSHttpBinding协议
  16. Android stado 运行项目,apk does not exist on disk.
  17. o&#39;Reill的SVG精髓(第二版)学习笔记——第四章
  18. app后端开发系列文章文件夹
  19. karaf中利用Bundle引入外部log4j配置文件
  20. SpringSecurity学习笔记(一):搭建最简单的SpringSecurity应用

热门文章

  1. Spring Cloud Gateway整合Eureka
  2. Java冒泡排序,二分查找法
  3. IDEA中配置Jetty Server
  4. Git 命令行解决冲突
  5. ZOJ4019——贪心&amp;&amp;DP
  6. Verilog从文件读数据
  7. dsu on tree(树上启发式合并)
  8. vue 复制内容到粘贴板
  9. 1分钟快速制作漂亮的Html5本地记事本
  10. python中selenium操作下拉滚动条方法