跨域请求访问事情

1、场景描述

1、前端使用Vue + Vue-router + Vant + axios
2、封装axios 请求,请求url: http://localhost:8080
3、后端使用Python-Flask 访问API: http://localhost:5000
4、数据库为:MongoDB
5、以上两上URL端口不相同,即为跨域请求

2、使用技术

2.1前端使用Vue

2.1.1 axios封装--请求API

/**
 * 用于封装请求的方法和url值;
 * 2019-11-21
 */
const CONTACT_API = {
    //获取联系人列表
    getContactList: {
        method: 'get',
        url: '/contactList'
    },
    //新建联系人 form-data
    newContactForm: {
        method: 'post',
        url: '/contact/new/form'
​
    },
    //新建联系人 application/json
    newContactJson: {
        method: 'post',
        url: '/contact/new/json'
    },
    editContact: {
        method: 'put',
        url: '/contact/edit'
    },
    deleteContact: {
        method: 'delete',
        url: '/contact'
    }
}
​
export default CONTACT_API

2.1.2 axios封装--请求Http

//引用axios
import axios from 'axios'
//引入封装请求的API
import service from './contactApi'
import {Toast} from 'vant'
​
// service 循环遍历输出不同的请求方法
//创建请求实例
let instance = axios.create({
    baseURL: 'http://localhost:5000/',
    //contentType: "text/html;charset=utf8",
    //dataType: "json",
    timeout: 1000
})
//声明请求的HTTP对象;
const Http = {}; //包裹请求方法的容器
​
//请求格式或参数的统一
for(let key in service)
{    
    let api = service[key];
    
    Http[key] = async function(
        params, //请求参数get,url; post,patch,put(data)
        isFormData = false,  //是否form-data请求
        config = {}
    ) {
       //let url = api.url
       let newParams = {}
       
       if(params && isFormData){
           newParams = new FormData()
           for(let i in params){
               newParams.append(i,params[i])
           }
       }else {
           newParams = params
       }
       console.log('newParams....',newParams)
       //不同请求判断
       let response = {}; //请求响影的值;
       if(api.method ==='post' ||api.method ==='put' ||api.method =='patch'){
           try{
               console.log('config',config)
               response = await instance[api.method](api.url,newParams,config)
​
           }catch(err){
               response = err
           }
​
       }else if (api.method ==='delete' || api.method ==='get') {
           config.params = newParams
           try{
               //instance[api.method] ---这一部分是获取Hanson名;
               //(api.url,config) 为函数体,其中第1上参数为url,第二个参数为传递的params参数;
               response = await instance[api.method](api.url,config)
           }catch(err){
               console.log(err)
           }
       }
       return response
    } // end function
}
//请求拦截器
//config 为请求的信息,封装有表头信息;
instance.interceptors.request.use(config=>{
    console.log('request...',config)
    Toast.loading({
        mask: false,
        duration: 0,
        forbidClick: true,  //禁止点击
        message: '加载中.....'
    })
    //发起请求前
    return config
}, ()=>{
    Toast.clear()
    Toast('请求错误,请稍后重试')
​
})
//请求响应的结果
//res为请求响应的结果;
instance.interceptors.response.use(res=>{
    console.log('response....',res)
    console.log('response-data',res.data)
    if(res.status === 200)
    {
        Toast.clear()
        return res.data
    }
    else
    {
        Toast('请求错误,请稍后重试')
    }
   
},()=>{
   Toast.clear()
   Toast('请求错误,请稍后重试')
})
​
export default Http

2.1.3 Vue--contactlist组件

<template>
  <div>
    <van-contact-list
      :list="list"
      @add="onAdd"
      @edit="onEdit"
      @select="onSelect"
    />
    <!-- 联系人编辑 -->
<van-popup v-model="showEdit" position="bottom">
  <van-contact-edit
    :contact-info="editingContact"
    :is-edit="isEdit"
    @save="onSave"
    @delete="onDelete"
  />
</van-popup>
  </div>
</template>
​
<script>
import axios from "axios";
import { ContactList, Toast, ContactEdit, Popup } from "vant";
export default {
  data() {
    return {
      list: [{
        name: '张三',
        tel: '13800000000',
        id: 0
      }],
      instance: null,
      showEdit: false,
      editingContact: {},
      isEdit: false
    }
  },
  components: {
      [ContactList.name]: ContactList,
      [ContactEdit.name]: ContactEdit,
      [Popup.name]: Popup
  },
  methods: {
    //获取列表信息
    async getList() {
      let res = await this.$Http.getContactList()
      console.log('getList....',res)
      this.list = res
    },
    //添加联系人;
    onAdd() {
      this.showEdit = true
      this.isEdit = false
​
    },
    //编辑联系人
    onEdit(info) {
     this.showEdit = true
     this.isEdit = true
     this.editingContact = info
    },
    onSelect() {
​
    },
    async onSave(info) {
      if(this.isEdit){
​
        let res = this.$Http.editContact(info)
        console.log('edit....',res);
        this.showEdit = false
        this.getList()
      }else{
​
        try{
            this.$Http.newContactJson(info)
            Toast('新建成功')
            this.showEdit = false
            this.getList()
        }catch(err){
           console.log(err)
        }
      }
    },
    //删除记录
    async onDelete(info) {
      try
      {
        let res = await this.$Http.deleteContact(info)
        Toast('删除成功')
        this.showEdit = false
        this.getList()
      }
      catch(err)
      {
        console.log(err)
      }
 
    }
​
  },
  //Vue生命周期函数
  created() {
    this.getList()
  }
}
</script>
​
<style scoped>
.van-contact-list__add{
  z-index: 0;
}
.van-popup {
  height: 100%;
}
​
</style>

2.1.4 请求Http的挂载

==== main.js =====
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import Http from './service/http.js'
​
Vue.config.productionTip = false
//挂载http请求实例;
Vue.prototype.$Http = Http
​
new Vue({
  router,
  render: h => h(App)
}).$mount('#app')
​

2.2 后端使用Flask-MongoDB

2.2.1 查询

​
from flask import render_template,Response,json,make_response
from app.model import Contactlist
from . import vueapi
​
#查询记录
@vueapi.route('/contactList')
def get():
    contacts = Contactlist.objects.filter()
    list = []
    cons = {
        'id': 0,
        'name': '',
        'tel': ''
    }
    print(contacts[0].id)
    for contact in contacts:
        cons = {
            'id': str(contact.id),
            'name': contact.name,
            'tel': contact.tel
        }
        list.append(cons)
        print(str(contact.id) + ' ' + contact.name + ' ' + contact.tel)
    res = make_response(json.dumps(list))
    res.headers['Access-Control-Allow-Origin'] = '*'
​
    return res
​

2.2.2 新增

#保存记录;
@vueapi.route('/contact/new/json', methods=["POST"])
def save_data():
    #获取参数信息;
    data = eval(request.data.decode('utf-8'))
    name = data.get('name')
    tel =  data.get('tel')
    print(name+'...'+ tel)
    #获取ID
    count = Contactlist.objects.filter().count()+1
    #实例化对象;
    contact = Contactlist(id=count,name= name,tel=tel)
    contact.save()
    contacts = [{
        'name': name,
        'tel': tel
    },]
    #以下为解决跨域请求
    res = make_response(json.dumps(contacts))
    res.headers['Access-Control-Allow-Origin'] = '*'
    res.headers['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS, DELETE'
    res.headers['Access-Control-Allow-Credentials'] = 'true'
    return res

2.2.3 修改

#更新记录
@vueapi.route('/contact/edit',methods=['PUT'])
def update_data():
    #获取参数值
    data = eval(request.data.decode('utf-8'))
    id = data.get('id')
    name = data.get('name')
    tel = data.get('tel')
    #根据参数获取记录
    contact = Contactlist.objects.get_or_404(id=id)
    #修改查询出记录的内容;
    contact.id = id
    contact.name = name
    contact.tel = tel
    #保存记录
    contact.save()
    res = make_response('OK')
    res.headers['Access-Control-Allow-Origin'] = '*'
    res.headers['Access-Control-Allow-Methods'] = 'POST, GET, OPTIONS, DELETE'
    res.headers['Access-Control-Allow-Credentials'] = 'true'
    return res

2.2.4 删除

#删除记录
@vueapi.route('/contact', methods=['delete'])
def delete_data():
    id = request.args['id']
    contact = Contactlist.objects.get_or_404(id = id)
    if contact:
        contact.delete()
    res = make_response("OK")
    res.headers['Access-Control-Allow-Origin'] = '*'
    res.headers['Access-Control-Allow-Credentials'] = 'true'
    return res

 

2.3 数据库MongoDB

2.3.1 配置文件

config.py
​
MONGODB_SETTINGS ={
    'db': 'api_list',
    'host': '127.0.0.1',
    'port': 27017
}

2.3.2 数据模型

from app import db
​
class Contactlist(db.Document):
    id = db.IntField(primary_key=True)
    name = db.StringField()
    tel  = db.StringField()
​
    meta = {
        'collection': 'contacts',
        'strict': False
    }
​
​
    def __repr__(self):
        return '<Contactlist %r>' % self.name

3、跨域请求问题点

3.1 安装flask_cors

pip install flask_cors

3.2 flask_cors引用和注册

from flask import Flask
​
from flask_cors import *
​
from flask_mongoengine import MongoEngine
​
db = MongoEngine()
​
def create_app():
    app = Flask(__name__)
​
    app.config.from_object('config')
​
    db.init_app(app)
​
    #蓝图注册
    from app.vueapi import vueapi as api_blueprint
    app.register_blueprint(api_blueprint, url_prfix="/vueapi")
​
    CORS(app, supports_credentials=True)
    return app

 

3.2最终解决方案为:

需要在flask数据访问函数中增加response的表头信息:
from flask import render_template,Response,json,make_response
​
res = make_response(json.dumps(list))
res.headers['Access-Control-Allow-Origin'] = '*'
下面这行很重要,否则会报上面的错误信息

4、最终结果:

Logo

前往低代码交流专区

更多推荐