十四、项目实战二(CORS、同源策略、cookie跨域)

news2024/12/21 14:37:31

项目实战二

需求

以前后端分离的方式实现学生的增删改查操作

学生列表接口

url:/students/

请求方法:get

参数:

  • 格式:查询参数
参数名类型是否必传说明
pageint页码,默认为1
sizeinit每页数据条数默认为10
namestr根据姓名过滤
ageint根据年龄过滤
sexint根据性别过滤
phonestr根据手机过滤
channelint根据渠道过滤

响应:

  • 状态码:200
  • 格式:json

  • 响应示例:

{
    "total": 7,
    "page": 1,
    "next_page": null,
    "pre_page": null,
    "results": [
        {
            "id": 8,
            "name": "yaya",
            "age": 18,
            "sex": 1,
            "phone": null,
            "channel": "",
            "c_time": "2022-09-01T06:30:48.417Z"
        },
        {
            "id": 7,
            "name": "yaya",
            "age": 18,
            "sex": 1,
            "phone": null,
            "channel": "",
            "c_time": "2022-09-01T06:10:41.869Z"
        },
        {
            "id": 6,
            "name": "心蓝",
            "age": 20,
            "sex": 1,
            "phone": "15873061798",
            "channel": "",
            "c_time": "2022-08-31T12:21:04.068Z"
        },
        {
            "id": 5,
            "name": "小简",
            "age": 16,
            "sex": 0,
            "phone": null,
            "channel": "抖音",
            "c_time": "2022-08-23T13:10:05.317Z"
        },
        {
            "id": 3,
            "name": "张柏芝",
            "age": null,
            "sex": 1,
            "phone": null,
            "channel": "",
            "c_time": "2022-08-23T08:00:15.165Z"
        },
        {
            "id": 2,
            "name": "刘德华",
            "age": null,
            "sex": 1,
            "phone": null,
            "channel": "",
            "c_time": "2022-08-23T08:00:08.035Z"
        },
        {
            "id": 1,
            "name": "心蓝",
            "age": null,
            "sex": 1,
            "phone": null,
            "channel": "",
            "c_time": "2022-08-23T07:59:29.417Z"
        }
    ]
}

学生详情接口

url:/students/pk/

请求方法:get

参数:

  • 格式:路径参数

响应数据:

  • 状态码:200

  • 格式:json

  • 响应实例:
{
    "id": 1,
    "name": "心蓝",
    "age": null,
    "sex": 1,
    "phone": null,
    "channel": "",
    "c_time": "2022-08-23T07:59:29.417Z"
}

学生添加接口

url:/students/

请求方法:post

参数:

  • 格式:json
参数名类型是否必传说明
namestr姓名
ageint年龄
sexint性别
phonestr手机
channelint渠道id
  • 请求实例:
{
    "name": "刘德华",
    "age": 60,
    "sex": 1,
    "phone": '13888888888',
    "channel": 1
},

响应:

  • 状态码:201

  • 格式:json

  • 响应示例:
{
    "id": 8,
    "name": "yaya",
    "age": 18,
    "sex": 1,
    "phone": null,
    "channel": "",
    "c_time": "2022-09-01T06:10:41.869Z"
}

学生修改接口

url:/students/pk/

请求方法:put/patch

参数:

  • 格式:json,路径
参数名类型是否必传说明
namestr姓名
ageint年龄
sexint性别
phonestr手机
channelint渠道id
  • 请求实例:
{
    "name": "刘德华",
    "age": 60,
    "sex": 1,
    "phone": '13888888888',
    "channel": 1
},

响应:

  • 状态码:200

  • 格式:json

  • 响应示例:
{
    "id": 8,
    "name": "yaya",
    "age": 18,
    "sex": 1,
    "phone": null,
    "channel": "",
    "c_time": "2022-09-01T06:10:41.869Z"
}

学生删除接口

url:/students/pk/

请求方法:delete

参数:

  • 格式:路径

响应:

  • 状态码:204
  • 格式:无响应内容

后端代码

视图

class StudentView(View):

    def serialize(self, item):

        return {
                'id': item.id,
                'name': item.name,
                'age': item.age,
                'sex': item.sex,
                'phone': item.phone,
                'channel': item.channel.title if item.channel else '',
                'c_time': item.c_time
            }

    def get(self, request, pk=None):
        if pk is not None:
            #  查看学生详情
            obj = self.get_obj(pk)
            data = self.serialize(obj)
            return JsonResponse(data)

        # 1. 获取查询参数
        query_params = {key: value for key, value in request.GET.items()}
        # 2. 获取分页参数
        page = int(query_params.pop('page', 1))
        size = int(query_params.pop('size', 10))
        # 3. 获取查询集
        queryset = Student.objects.all()
        for key, value in query_params.items():
            try:
                queryset = queryset.filter(**{key: value})
            except:
                pass
        # 4. 分页处理
        # 数据总条数
        total_num = queryset.count()
        # 总页数
        total_page = math.ceil(total_num / size)
        # 下一页
        absolute_url = self.request.build_absolute_uri()
        next_url = None
        pre_url = None
        if page < total_page:

            if 'page' in absolute_url:
                next_url = re.sub(r'page=\d*', 'page={}'.format(page+1), absolute_url)
            else:
                next_url = absolute_url + '&page={}'.format(page+1)
        # 上一页
        if page > 1:

            if 'page' in absolute_url:
                pre_url = re.sub(r'page=\d*', 'page={}'.format(page-1), absolute_url)
            else:
                pre_url = absolute_url + '&page={}'.format(page-1)
        # 分页过滤
        queryset = queryset[(page - 1) * size:page * size]
        # 5.序列化
        students = [
            self.serialize(item) for item in queryset
        ]
        data = {
            'total': total_num,
            'page': page,
            'next_page': next_url,
            'pre_page': pre_url,
            'results': students
        }
        # 6. 返回响应
        return JsonResponse(data)

    def post(self, request):
        # 1.接受参数
        create_data = json.loads(request.body)
        # 2.实例化表达
        form = StudentForm(create_data)
        # 3.校验
        if form.is_valid():
            instance = form.save()
            # 4.序列化
            data = self.serialize(instance)
            return JsonResponse(data, status=201)
        else:
            # 5.错误信息
            data = {'errors': form.errors}
            return JsonResponse(data, status=400)

    def get_obj(self, pk):
        obj = get_object_or_404(Student, pk=pk)
        return obj

    def put(self, request, pk):
        # 1. 获取对象
        obj = self.get_obj(pk)
        # 2. 接收参数
        update_data = json.loads(request.body)
        # 2. 实例化表单
        form = StudentForm(update_data, instance=obj)
        # 3. 校验
        if form.is_valid():
            instance = form.save()
            # 4. 序列化
            data = self.serialize(instance)
            return JsonResponse(data, status=200)
        else:
            # 5.错误信息
            data = {'errors': form.errors}
            return JsonResponse(data, status=400)

    def delete(self, request, pk):
        # 1. 获取对象
        obj = self.get_obj(pk)
        # 2. 删除对象
        try:
            obj.delete()
            return HttpResponse(status=204)
        except Exception as e:
            return JsonResponse(data={'errors': str(e)}, status=400)

路由

path('students/', views.StudentView.as_view(), name='student-list-create'),
path('students/<int:pk>/', views.StudentView.as_view(), name='student-retrieve-update-delete')

前端代码

列表页面

<!-- student_list_single.html -->
<!doctype html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <title>Bootstrap 101 Template</title>

    <!-- Bootstrap -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css"
          integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">

    <!-- HTML5 shim 和 Respond.js 是为了让 IE8 支持 HTML5 元素和媒体查询(media queries)功能 -->
    <!-- 警告:通过 file:// 协议(就是直接将 html 页面拖拽到浏览器中)访问页面时 Respond.js 不起作用 -->
    <!--[if lt IE 9]>
    <script src="https://fastly.jsdelivr.net/npm/html5shiv@3.7.3/dist/html5shiv.min.js"></script>
    <script src="https://fastly.jsdelivr.net/npm/respond.js@1.4.2/dest/respond.min.js"></script>
    <![endif]-->
    <style>
        [v-cloak] {
            display: none;
        }
    </style>
</head>
<body>
<div class="container" style="width: 1000px" id="app">
    <h1>学生列表</h1>
    <a class="btn btn-success" style="float: right" href="./student_detail_single.html">添加</a>
    <table class="table table-hover table-bordered table-condensed" v-cloak>
        <thead>
        <tr>
            <th>序号</th>
            <th>姓名</th>
            <th>性别</th>
            <th>年龄</th>
            <th>phone</th>
            <th>渠道</th>
            <th>创建时间</th>
            <th>操作</th>
        </tr>
        </thead>
        <tbody>
        <tr v-for="(stu, index) in students">
            <td>{{ index }}</td>
            <td>{{ stu.name }}</td>
            <td>{{ stu.sex }}</td>
            <td>{{ stu.age }}</td>
            <td>{{ stu.phone }}</td>
            <td>{{ stu.channel }}</td>
            <td>{{ stu.c_time }}</td>
            <td style="text-align: center;padding: 0.75em 0"><a :href="'./student_detail_single.html?id='+stu.id"
                                                                class="btn btn-primary btn-sm">编辑</a>&nbsp;
                <button class="btn btn-danger btn-sm" @click="delStudent(stu.id, index)">删除</button>
            </td>
        </tr>
        </tbody>
    </table>
    <nav aria-label="...">
        <ul class="pager">
            <li class="previous" :class="{ disabled: !pre_url }"><a
                    href="{{ students.pre_url }}"><span
                    aria-hidden="true">&larr;</span> 上一页</a></li>
            <li class="next" :class="{ disabled: !next_url }"><a
                    href="{{ students.next_url }}">下一页 <span
                    aria-hidden="true">&rarr;</span></a>
            </li>
        </ul>
    </nav>
</div>


<!-- jQuery (Bootstrap 的所有 JavaScript 插件都依赖 jQuery,所以必须放在前边) -->
<script src="https://fastly.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js"
        integrity="sha384-nvAa0+6Qg9clwYCGGPpDQLVpLNn0fRaROjHqs13t4Ggj3Ez50XnGQqc/r8MhnRDZ"
        crossorigin="anonymous"></script>
<!-- 加载 Bootstrap 的所有 JavaScript 插件。你也可以根据需要只加载单个插件。 -->
<script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"
        integrity="sha384-aJ21OjlMXNL5UyIl/XNwTMqvzeRMZH2w8c5cRVpzpU8Y5bApTppSuUkhZXN0VxHd"
        crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
    let baseUrl = 'http://127.0.0.1:8000'
    const app = new Vue({
        el: '#app',
        data: {
            students: [],
            next_url: null,
            pre_url: null,
        },
        computed: {

        },
        methods: {
            delStudent(sid, index) {
                axios.delete(baseUrl + '/crm/students/' + sid + '/').then(res => {
                    this.students.splice(index, 1)
                })
            }
        },
        created() {
            axios.get(baseUrl + '/crm/students/').then(res => {
                this.students = res.data.results
                this.next_url = res.data.next_url
                this.pre_url = res.data.pre_url
            })
        }
    })
</script>
</body>
</html>

详情页

<!-- student_detail_single.html -->
<!doctype html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <title>Bootstrap 101 Template</title>

    <!-- Bootstrap -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css"
          integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">

    <!-- HTML5 shim 和 Respond.js 是为了让 IE8 支持 HTML5 元素和媒体查询(media queries)功能 -->
    <!-- 警告:通过 file:// 协议(就是直接将 html 页面拖拽到浏览器中)访问页面时 Respond.js 不起作用 -->
    <!--[if lt IE 9]>
    <script src="https://fastly.jsdelivr.net/npm/html5shiv@3.7.3/dist/html5shiv.min.js"></script>
    <script src="https://fastly.jsdelivr.net/npm/respond.js@1.4.2/dest/respond.min.js"></script>
    <![endif]-->
    <style>
        [v-cloak]{
              display: none;
          }
    </style>
</head>
<body>
<div class="container" style="width: 800px" id="app" v-cloak>
    <h1>{{ title }}</h1>
    <form class="form-horizontal">
        <div class="form-group">
            <label for="name" class="col-sm-2 control-label">姓名</label>
            <div class="col-sm-10">
                <input type="text" class="form-control" id="name" name="name" v-model="stu.name" placeholder="姓名">
            </div>
        </div>
        <div class="form-group">
            <label for="sex" class="col-sm-2 control-label">性别</label>
            <div class="col-sm-10">
                <input type="text" class="form-control" id="sex" name="sex" v-model="stu.sex" placeholder="性别">
            </div>
        </div>
        <div class="form-group">
            <label for="age" class="col-sm-2 control-label">年龄</label>
            <div class="col-sm-10">
                <input type="text" class="form-control" id="age" name="age" v-model="stu.age" placeholder="年龄">
            </div>
        </div>
        <div class="form-group">
            <label for="qq" class="col-sm-2 control-label">qq</label>
            <div class="col-sm-10">
                <input type="text" class="form-control" id="qq" name="qq" v-model="stu.qq" placeholder="qq">
            </div>
        </div>
        <div class="form-group">
            <label for="phone" class="col-sm-2 control-label">手机号码</label>
            <div class="col-sm-10">
                <input type="text" class="form-control" id="phone" name="phone" v-model="stu.phone"
                       placeholder="手机号码">
            </div>
        </div>
        <div class="form-group">
            <label for="channel" class="col-sm-2 control-label">渠道</label>
            <div class="col-sm-10">
                <select name="channel" id="channel" class="form-control" v-model="stu.channel">
                    <option value="">--------</option>
                    <option v-for="channel in channels" :value="channel.id">{{ channel.name }}</option>
                </select>
            </div>
        </div>
        <div class="form-group">
            <div class="col-sm-offset-2 col-sm-10">
                <button class="btn btn-info float-right"
                        @click.prevent="btnSubmit(stu)">{{ btnName }}</button>
            </div>
        </div>


    </form>
</div>

<!-- jQuery (Bootstrap 的所有 JavaScript 插件都依赖 jQuery,所以必须放在前边) -->
<script src="https://fastly.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js"
        integrity="sha384-nvAa0+6Qg9clwYCGGPpDQLVpLNn0fRaROjHqs13t4Ggj3Ez50XnGQqc/r8MhnRDZ"
        crossorigin="anonymous"></script>
<!-- 加载 Bootstrap 的所有 JavaScript 插件。你也可以根据需要只加载单个插件。 -->
<script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"
        integrity="sha384-aJ21OjlMXNL5UyIl/XNwTMqvzeRMZH2w8c5cRVpzpU8Y5bApTppSuUkhZXN0VxHd"
        crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
    let baseUrl = 'http://127.0.0.1:8000'
    const app = new Vue({
        el: '#app',
        data: {
            title: '',
            stu: {},
            channels: [{name: '百度', id: 1}, {name: '抖音', id: 2}],
            btnName: ''
        },
        methods: {
            delStudent(sid, index) {
                axios.delete(baseUrl + '/crm/students/' + sid + '/' ).then(res => {
                    this.students.splice(index, 1)
                })
            },
            getParams() {
                let query = window.location.search.substring(1)
                let vars = query.split("&");
                let data = {}
                for (let v of vars) {
                    v = v.split('=');
                    data[v[0]] = v[1]
                }
                return data

            },
            saveStu(sid, stu){
                axios.put(baseUrl + '/crm/students/' + sid + '/' , stu).then(res=>{
                    location.href = 'student_list_single.html'
                })
            },
            createStu(stu){
                axios.post( baseUrl + '/crm/students/', stu).then(res=>{
                    location.href = 'student_list_single.html'
                })
            },
            btnSubmit(stu){
                let sid = this.getParams().id;
                if (sid){
                    this.saveStu(sid, stu)
                }else {
                    this.createStu(stu)
                }
            }

        },
        created() {
            let sid = this.getParams().id;
            if (sid) {
                this.title = '学生详情';
                this.btnName = '保存';
                axios.get(baseUrl + '/crm/students/' + sid + '/' ).then(res => {
                    this.stu = res.data
                })
            } else {
                this.title = '添加学生';
                this.btnName = '添加'
            }

        }
    })
</script>
</body>
</html>

CORS

后端服务写好后,通过postman可以正常访问,但通过浏览器时,发送ajax请求回报CORS错误。

要理解CORS需要先了解几个概念

同源策略

同源策略是一个重要的安全策略,它是浏览器最核心最基本的安全功能。

它限制web应用程序只能从加载应用程序的同一个域请求HTTP资源。

当向不同的域请求HTTP资源时就发生了跨域,默认请情况下浏览器会阻止跨域的请求。

那如何判断是否同源呢?

如果两个URL的协议,端口和主机都相同的话,则这两个URL是同源。

例如以下所有资源都具有相同的来源:

http://example.com/
http://example.com:80/
http://example.com/path/file

每个url都有相同的协议,主机和端口号。

而以下每个资源都与其他不同源:

 http://example.com/
 http://example.com:8080/
 http://www.example.com/
 https://example.com:80/
 https://example.com/
 http://example.org/
 http://ietf.org/

所以所谓的同源策略简单的理解就是,打开某个页面后,这个页面上的ajax请求默认只能向和页面同源的url发送http请求。

同源策略固然保证了安全,但同时也限制了应用的灵活性,所以出现了CORS.

什么是CORS

CORS是一个W3C标准,全称是"跨域资源共享"(cross-origin resource sharing)。

它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。

CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能。

整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于前端开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。

因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

CORS原理

跨域请求

浏览器将跨域请求分为两类:简单请求和非简单请求。

只要同时满足一下两个条件,就属于简单请求:

  1. 请求方法是一下三种方法之一:
  2. head
  3. get
  4. post
  5. http请求的头信息不超出以下几种字段:
  6. accept
  7. accept-language
  8. content-language
  9. Last-Event-ID
  10. Content-Type的值仅限于下列三者之一:
    • text/plain
    • multipart/form-data
    • application/x-www-form-urlencoded

凡是不同时满足上面两个条件,就属于非简单请求。

浏览器对这两种请求的处理是不一样的。

简单请求CORS步骤

对于简单请求CORS的基本流程如下:

第一步:客户端(浏览器)请求

当浏览器发出跨域请求时,该浏览器会添加一个包含当前源(协议,主机和端口)的Origin头。

第二步:服务器响应

在服务器,当服务器看到Origin头并想要允许访问时,就需要在响应中加入一个Access-Control-Allow-Origin响应头来指定请求源(例如加入*表示允许任何源)

第三步:浏览器接受响应

当浏览器看到带有相应Access-Control-Allow-Origin响应头的响应时,即允许与客户端网站共享响应数据。否则抛出CORS异常。

注意同源策略只是浏览器遵守的规则,使用别的工具进行请求不会遵循同源策略的影响。

复杂请求CORS步骤

第一步:发送预检请求

浏览器会根据需要创建预检请求。该请求是一个options请求,会在实际请求消息之前被发送。

预检请求中关键请求头是origin表示请求来自哪个源。除了origin字段,预检请求头的信息还包含两个特殊字段。

  1. access-control-request-method

该字段是必须的,用来列出接下来的CORS请求会用到哪些HTTP方法,上面图片中的是PATCH

  1. access-control-request-headers

这个字段是一个逗号分隔的字符串,指定接下来的CORS请求还会携带哪些额外的字段,上面图片中的是content-type

第二步:响应预检请求

服务器收到预检请求后,检查origin,access-control-request-method,access-control-request-headers字段后,就可以返回响应。

响应中的access-control-allow-origin字段表示允许跨域的源,*表示允许任意跨域请求。其他CORS相关响应头如下:

  1. Access-Control-Allow-Methods

逗号分隔的一个字符串,表明服务器允许的跨域请求方法

  1. Access-Control-Allow-Headers

逗号分隔的一个字符串,表明服务器支持的头字段

  1. Access-Control-Max-Age

该字段可选,用来指定本次预检请求的有效期,单位为秒。上图中的有效期是一天(86400秒),在此期间不用发出另一条预检请求。

注意:如果服务器否定了预检请求,也会返回一个正常的HTTP响应,但是不包含任何CORS相关的响应头。

第三步:发送跨域请求

一旦服务器通过了预检请求,以后每次浏览器正常的CORS请求,就跟简单请求一样,会有一个origin头字段。服务器的回应,也会有一个access-control-allow-origin头信息字段。

cookie跨域

出于隐私原因,CORS请求默认不带cookie。如果想要在使用CORS时发送cookie,就需要发送请求时携带cookie并且服务器也同意。

请求

ajax请求需要打开withCredentials属性才可以携带cookie:

const request = axios.create({
    baseURL: 'http://127.0.0.1:8000',
    timeout: 5000,
    withCredentials: true // 设置为true 跨域时会携带cookie
})

响应

如果要接受cookie跨域,access-control-allow-origin就不能设置为星号,必须指定明确,并且响应头中必须包含字段Access-Control-Allow-Credentials,值为true

同时cookie依然遵循同源策略,只有服务器指明的域名的cookie才会上传。

django-cors-headers

在django项目中要实现CORS可以手写(重复造轮子),也可以使用成熟插件(推荐)。

django-cors-headers是一个处理跨域资源共享(CORS)所需服务器器头信息的django应用。

它的使用非常简单:

安装

pip install django-cors-headers

添加到apps

INSTALLED_APPS = [
 ...,
 "corsheaders",
 ...,
]

设置中间件

Django-cors-headers是通过中间件实现cors头设置的,所以需要设置对应的中间件

MIDDLEWARE = [
 ...,
 "corsheaders.middleware.CorsMiddleware",
 "django.middleware.common.CommonMiddleware",
 ...,
]

CorsMiddleware应该放在尽可能高的位置,特别是在任何可以生成响应的中间件之前,比如django的CommonMiddleware。否则无法将CORS头添加到这些响应中。

配置

要使用CORS,还需要在settings.py模块中添加如下配置:

# CORS设置
# 允许跨域的域名列表
CORS_ALLOWED_ORIGINS = [
 'http://localhost:8080'
]
CORS_ALLOW_ALL_ORIGINS = True # 表示y
# 允许cookies跨域
CORS_ALLOW_CREDENTIALS = True

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/371236.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot+HttpClient+JsonPath提取A接口返回值作为参数调用B接口

前言 在做java接口自动化中&#xff0c;我们常常需要依赖多个接口&#xff0c;A接口依赖B&#xff0c;C&#xff0c;D接口的响应作为请求参数&#xff1b;或者URL中的参数是从其他接口中提取返回值作获取参数这是必不可少的。那么怎么实现呢&#xff1f;下面就来介绍多业务依赖…

大数据技术之Hive(三)函数

hive有大量内置函数&#xff0c;大致可分为&#xff1a;单行函数、聚合函数、炸裂函数、窗口函数。查看内置函数show functions;查看内置函数用法desc function upper;查看内置函数详细信息desc function extended upper;一、单行函数单行函数的特点是一进一出&#xff0c;输入…

Kubernetes之服务的基本管理

svc是kubernetes最核心的概念&#xff0c;通过创建Service&#xff0c;可以为一组具有相同功能的容器应用提供一个统一的入口地址&#xff0c;并将请求进行负载分发到后端的各个容器应用上。pod生命周期短不稳定&#xff0c;pod异常后新生成的pod的IP会发生变化&#xff0c;通过…

JSD2212班第二次串讲-面向对象阶段

1. 概述 系统介绍一下接下来阶段要学习的内容&#xff1a; 1.JAVA&#xff1a; 1.1 java语言基础 1.2 java面向对象阶段&#xff08;很重要&#xff01;起的承上启下&#xff01;&#xff01;&#xff09; 1.3 API阶段&#xff08;Application Interface &#xff09;学会看字…

Spring Cloud @RefreshScope 原理分析:代理类的创建

背景 前面我们知道&#xff0c;被 RefreshScope 注解的实例&#xff0c;在扫描生成 BeanDefiniton 时&#xff0c;被偷龙转凤了&#xff1a;注册了两个 Bean 定义&#xff0c;一个 beanName 同名、类型是 LockedScopedProxyFactoryBean.class 代理工厂 Bean&#xff0c;一个 s…

6-Java中新建一个文件、目录、路径

文章目录前言1-文件、目录、路径2-在当前路径下创建一个文件3-在当前路径下创建一个文件夹&#xff08;目录&#xff09;3.1 测试1-路径已经存在3.2 测试2-路径不存在3.2 创建不存在的路径并新建文件3.3 删除已存在的文件并新建4-总结前言 学习Java中如何新建文件、目录、路径…

ASE40N50SH-ASEMI高压MOS管ASE40N50SH

编辑-Z ASE40N50SH在TO-247封装里的静态漏极源导通电阻&#xff08;RDS(ON)&#xff09;为100mΩ&#xff0c;是一款N沟道高压MOS管。ASE40N50SH的最大脉冲正向电流ISM为160A&#xff0c;零栅极电压漏极电流(IDSS)为1uA&#xff0c;其工作时耐温度范围为-55~150摄氏度。ASE40N…

【架构师】零基础到精通——架构发展

博客昵称&#xff1a;架构师Cool 最喜欢的座右铭&#xff1a;一以贯之的努力&#xff0c;不得懈怠的人生。 作者简介&#xff1a;一名Coder&#xff0c;软件设计师/鸿蒙高级工程师认证&#xff0c;在备战高级架构师/系统分析师&#xff0c;欢迎关注小弟&#xff01; 博主小留言…

PHP - ChatGpt API 接入 ,代码,亲测!(最简单!)

由于最近ChatGpt 大火&#xff0c;但是门槛来说是对于大家最头疼的环节&#xff0c; 我自己也先开发了一个个人小程序&#xff01;大家可以访问使用下&#xff0c; 由此ChatGpt 有一个API 可以仅供大伙对接 让我来说下资质&#xff1a; 1&#xff1a;首先要搞得到一个 ChatGp…

HttpRunner接口自动化测试框架--常见问题

本篇文章主要总结在使用httprunner框架做接口自动化测试过程中遇到的问题 官方的问题总结&#xff1a;Issues httprunner/httprunner GitHub 1.在参数化过程中读取CSV文件&#xff0c;不能读取出整型来。 读取下方文件数据&#xff0c;全部是字符串格式 原因&#xff1a;c…

操作系统(复试准备)

操作系统&#xff08;复试准备&#xff09; 第一章知识点 操作系统概述 操作系统的概念 负责协调软硬件等计算机资源的工作 为上层用户&#xff0c;应用程序提供简单易用的接口 是一种系统软件 操作系统的功能与目标 资源的管理者 处理机管理&#xff0c;存储器管理&#x…

Linux 基础知识:指令与shell

目录一、操作系统二、指令三、shell一、操作系统 什么是操作系统&#xff1f; 单纯的操作系统应该是指操作系统内核。内核的作用就是管理计算机的软硬件资源&#xff0c;让计算机在合适的时候干合适的事情。 但是有一个问题&#xff0c;并不是人人都会直接通过内核来操作计算机…

异常信息记录入库

方案介绍 将异常信息放在日志里面&#xff0c;如果磁盘定期清理&#xff0c;会导致很久之前的日志丢失&#xff0c;因此考虑将日志中的异常信息存在表里&#xff0c;方便后期查看定位问题。 由于项目是基于SpringBoot构架的&#xff0c;所以采用AdviceControllerExceptionHand…

【跟着ChatGPT学深度学习】ChatGPT带我入门深度学习

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️&#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

树与二叉树与森林的相关性质

文章目录树的度树的性质二叉树的性质二叉树与森林树的度 树的度指的是树内所有节点的度数的最大值。 节点的度&#xff1a;节点所拥有的子树的数量。简单来说&#xff0c;我们直接数分支即可&#xff0c;例如下图&#xff1a; 在这颗二叉树中&#xff0c;节点2的度为2&#…

【Java】Synchronized锁原理和优化

一、synchronized介绍 synchronized中文意思是同步&#xff0c;也称之为”同步锁“。 synchronized的作用是保证在同一时刻&#xff0c; 被修饰的代码块或方法只会有一个线程执行&#xff0c;以达到保证并发安全的效果。 synchronized是Java中解决并发问题的一种最常用的方法…

K3S系列文章-使用AutoK3s在腾讯云上安装高可用K3S集群

开篇 《K3s 系列文章》《Rancher 系列文章》 方案 在腾讯云上安装 K3S 后续会在这套 K3S 集群上安装 Rancher 方案目标 高可用3 台master 的 k3s 集群 数据备份k3s 数据备份到 腾讯云对象存储 cos 尽量复用公有云的能力Tencent Cloud Controller Manager (❌ 因为腾讯云已…

【LINUX】环境变量以及main函数的参数

文章目录前言环境变量常见环境变量&#xff1a;设置环境变量&#xff1a;和环境变量相关的命令&#xff1a;环境变量的组织方式&#xff1a;获取环境变量环境变量可以被子进程继承环境变量总结main函数的参数前言 大家好久不见&#xff0c;今天分享的内容是环境变量和main函数…

JUC并发编程与源码分析笔记09-原子类操作之十八罗汉增强

基本类型原子类 AtomicInteger、AtomicBoolean、AtomicLong。 常用API&#xff1a; public final int get();// 获取当前的值 public final int getAndSet(int newValue);// 获取当前值&#xff0c;并设置新值 public final int getAndIncrement();// 获取当前的值&#xff0…

2.25Maven的安装与配置

一.Mavenmaven是一个Java世界中,非常知名的"工程管理工具"/构建工具"核心功能:1.管理依赖在进行一个A 操作之前,要先进行一个B操作.依赖有的时候是很复杂的,而且是嵌套的2.构建/编译(也是在调用jdk)3. 打包把java代码给构建成jar或者warjar就是一个特殊的压缩包…