第七章-前后端结合项目整合后端接口

111 阅读3分钟

7-1前后端分离开发是什么?

什么是RESTful API?

patch(update)在服务i去更新资源

delete从服务器删除资源

7-4使用swagger在线文档查看接口详情

endpoints具体路径

使用什么样的数据

swagger在线文档查看接口详情

axios的基本用法和独家后端api使用

import App from './App.vue'
import 'easymde/dist/easymde.min.css'
axios.defaults.baseURL = process.env.NODE_ENV === 'development' ? '/' : 'http://api.vikingship.xyz/api/'
axios.interceptors.request.use(config => {
  store.commit('setLoading', true)
  store.commit('setError', { status: false, message: '' })
  return config
})


axios.interceptors.response.use(config => {
  setTimeout(() => {
    store.commit('setLoading', false)
  }, 1000)
  return config
}, e => {
  const { error } = e.response.data
  store.commit('setError', { status: true, message: error })
  store.commit('setLoading', false)
  return Promise.reject(e.response.data)
})
const app = createApp(App)
app.use(router)
app.use(store)
app.mount('#app')

mutation当中的数据必须是同步的

actions:{

fetchColumns(context){

axios.get('/cloums').then(res=>{

context.commit(fetchColumns',resp.data)})

fetchColumns(state,rawData){

state.columsn=rawData.

setup(){

const store=useStore<Global

interface ImageProps{

_id?:string;

url?:string;

createdAt?:string;}

else{

coljjms.awater.url=columns.

7-7-使用vue-action发送异步请求

export interface GlobalDataProps{

state:{

onMounted(()==>{

store.dispatch('fetchColum',currentId)})

PostList.vue

<template>
  <div class="post-list">
    <article v-for="post in posts" :key="post._id" class="card mb-3 shadow-sm">
      <div class="card-body">
        <h4><router-link :to="`/posts/${post._id}/`">{{post.title}}</router-link></h4>
        <div class="row my-3 align-items-center">
          <div v-if="post.image && typeof post.image !== 'string'" class="col-4">
            <img :src="post.image.fitUrl" :alt="post.title" class="rounded-lg w-100">
          </div>
          <p :class="{'col-8': post.image}" class="text-muted">{{post.excerpt}}</p>
        </div>
        <span class="text-muted">{{post.createdAt}}</span>
      </div>
    </article>
  </div>
</template>


<script lang="ts">
import { defineComponent, PropType, computed } from 'vue'
import { PostProps, ImageProps } from '../store'
import { generateFitUrl } from '../helper'
export default defineComponent({
  props: {
    list: {
      required: true,
      type: Array as PropType<PostProps[]>
    }
  },
  setup(props) {
    const posts = computed(() => {
      return props.list.map(post => {
        generateFitUrl(post.image as ImageProps, 200, 110, ['m_fill'])
        return post
      })
    })
    return {
      posts
    }
  }
})
</script>


<style scoped>
.post-list h4 a {
  text-decoration: none;
  color:#1a1a1a;
}
.post-list h4 a:hover {
  color:#0d6efd;
}
</style>

const getAndCommit=async(url:string,mutationNmae:string,commit.commit){

const{data}=

7-10使用axios拦截器添加loading效果

setLoading

const getAndCommit=async(url:string,mutationName:string){{

const isLaoding=computed(()=>store

axios.interceptors.request.use(config=>{

store.commit('setLoading',true)

7-11loader组件编码

<template>
<teleport to="#back">
  <div
    class="d-flex justify-content-center align-items-center h-100 w-100 loading-container"
    :style="{backgroundColor: background || ''}"
  >
    <div class="loading-content">
      <div class="spinner-border text-primary" role="status">
        <span class="visually-hidden">{{ text || 'loading'}}</span>
      </div>
      <p v-if="text" class="text-primary small">{{text}}</p>
    </div>
  </div>
</teleport>
</template>


<script lang="ts">
import { defineComponent, onUnmounted } from 'vue'


export default defineComponent({
  props: {
    text: {
      type: String
    },
    background: {
      type: String
    }
  },
  setup() {
    const node = document.createElement('div')
    node.id = 'back'
    document.body.appendChild(node)
    onUnmounted(() => {
      document.body.removeChild(node)
    })
  }
})
</script>


<style>
.loading-container {
  background: rgba(255, 255, 255, .5);
  z-index: 100;
  position: fixed;
  top: 0;
  left: 0;
}
.loading-container {
  text-align: center;
}
</style>

7-12-loader组件编码teleport

后端Icode终极使用方案

慕课网提供的 Icode 经过几次升级,现在把最终的解决方案整理如下,供同学们参考。

可以在 main.ts 中的拦截器里面一劳永逸的添加。

// 替换 baseURL
axios.defaults.baseURL = 'http://apis.imooc.com/api/'
// 下面的 icode 值是从慕课网获取的 token 值,可以在课程右侧的项目接口校验码找到
axios.interceptors.request.use(config => {
... 其他代码
// get 请求,添加到 url 中
config.params = { ...config.params, icode: '******' }
// 其他请求,添加到 body 中
// 如果是上传文件,添加到 FormData 中
if (config.data instanceof FormData) {
config.data.append('icode', '******')
} else {
// 普通的 body 对象,添加到 data 中
config.data = { ...config.data, icode: '******' }
}
return config
})

如果同学们这时候去回顾 CreatePost 页面的话,由于 ColumnProps 对应的更新,这里的 CreatePost页面 暂时没有同时更新(由于现在不会用到该功能,但是以后升级 CreatePost 的时候我们会修改这些代

码),会发现一些类型错误。如图所示:

这里如果同学们要追求类型的完美,可以在这里提前暂时修改它,根本原因就是类型不匹配,由于在 ColumnProps 中我们把 id 修改成了 _id,类型成了 string,columnId 修改为了 column,类型变成了 string。

所以这里可以简单对应一下

const { columnId } = store.state.user
if (columnId) {
const newPost: PostProps = {
// 转成 string 类型
_id: new Date().getTime().toString(),
title: titleVal.value,
content: contentVal.value,
// 转成 string 类型
column: columnId.toString(),
createdAt: new Date().toLocaleString()
}
store.commit('createPost', newPost)
router.push({ name: 'column', params: { id: columnId } })
}

当然这个创建文章的页面这样修改以后,暂时会没有办法工作,创建成功以后跳转到对应的column也没有数据,这是因为我们接入了真实的后端数据,假设数据暂时会有问题,等后面学到了对应的用户和创建真实的数据的时候就没有问题了

第7章 前后端结合 - 项目整合后端接口

#7-2 RESTful API 设计理念

[en.wikipedia.org/wiki/Repres… API)是目前比较成熟的一套互联网应用程序的API设计理论。 Endpoint

在RESTful架构中,每个网址代表一种资源(resource),所以网址中不能有动词,只能有名词,而且所用的名词往往与数据库的表格名对应。一般来说,数据库中的表都是同种记录的"集合"(collection),所以API中的名词也应该使用复数。

https://api.examples.com/teams
https://api.examples.com/players

Verb 动词

  • GET(SELECT):从服务器取出资源(一项或多项)
  • POST(CREATE):在服务器新建一个资源
  • PUT(UPDATE):在服务器更新资源
  • PATCH(UPDATE):在服务器更新资源
  • DELETE(DELETE):从服务器删除资源

举例

// endpoints
https://api.example.com/teams
https://api.example.com/players


// verbs
GET /teams:列出所有球队
POST /teams:新建一个球队
GET /teams/ID:获取某个球队的信息
PUT /teams/ID:更新某个球队的信息(提供球队的全部信息)
PATCH /teams/ID:更新某个球队的信息(提供球队的部分信息)
{
  name: 'new team name'
}
DELETE /teams/ID:删除某个球队


// 复杂结构 一对多
GET /teams/ID/players:列出某个指定球队的所有球员


// 常见状态码
200 OK - [GET]:服务器成功返回用户请求的数据
201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
204 NO CONTENT - [DELETE]:用户删除数据成功。
401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作。

// 项目使用的 endpoints
GET /columns:列出所有专栏
GET /columns/ID:列出某个专栏的信息
GET /columns/ID/posts:列出某个专栏的所有文章
POST /columns/ID/posts 在某个专栏创建文章(需要权限)
GET /posts/ID: 列出某个文章的信息
POST /users/login 用户登录
GET /users/current 获取当前用户登录信息(需要权限)

#7-3 使用 swagger在线文档查看接口详情

接口文档需要包括的点

  • 第一 endponits,是具体的路径,或者说是网址。
  • 第二 使用什么样的 method,get,post,put,patch 或者 delete
  • 第三 发送请求要有什么样的参数,参数是在 url 上的 query还是 body 里面的复杂信息。
  • 第四 请求返回的格式是什么样的。

如果使用文档,有可能是这样的

### endpoints 
GET /teams/${ID}/players

### parameters
{
  name: 'ID',
  desc: '当前球队的 ID',
  type: 'string'
}

### responses
**200响应**

  {
    "code": 0,
    "data": [
      {
        "createdAt": "2020-06-05 16:45:22",
        "description": "有一段非常有意思的简介,可以更新一下欧",
        "name": "洛杉矶湖人",
        "_id": "5eda0622acb0d2280c10385e"
      },
      {
        "createdAt": "2020-06-05 16:45:22",
        "description": "有一段非常有意思的简介,可以更新一下欧",
        "name": "金州勇士",
        "_id": "5eda0544ce65c327d718e57b"
      }
    ],
    "msg": "请求成功"
  }

**401响应**

文档地址:(api.vikingship.xyz/)[http://ap…] 基于 (swagger)[swagger.io/]

#7-4 axios 的基本用法和独家后端API 使用(必看) 7-5 后端Icode终极使用方案

地址:coding.imooc.com/lesson/449.…

慕课网提供的 Icode 经过几次升级,现在把最终的解决方案整理如下,供同学们参考。 可以在 main.ts 中的拦截器里面一劳永逸的添加。

// 替换 baseURL
axios.defaults.baseURL = 'http://apis.imooc.com/api/'
// 下面的 icode 值是从慕课网获取的 token 值,可以在课程右侧的项目接口校验码找到
axios.interceptors.request.use(config => {
  ... 其他代码
  // get 请求,添加到 url 中
  config.params = { ...config.params, icode: '******' }
  // 其他请求,添加到 body 中
  // 如果是上传文件,添加到 FormData 中
  if (config.data instanceof FormData) {
    config.data.append('icode', '******')
  } else {
  // 普通的 body 对象,添加到 data 中
    config.data = { ...config.data, icode: '******' }
  }
  return config
})

#7-6 使用vuex action 发送异步请求

官方文档地址:vuex.vuejs.org/zh/guide/ac…

Action 类似于 mutation,不同在于:

  • Action 提交的是 mutation,而不是直接变更状态。
  • Action 可以包含任意异步操作。

代码提交地址:git.imooc.com/coding-449/…

#7-7 使用vuex action 发送异步请求第二部分

代码提交地址:git.imooc.com/coding-449/…

#7-8 使用 async 和 await 改造异步请求

async 和 await 是基基于promises的语法糖,使异步代码更易于编写和阅读。通过使用它们,异步代码看起来更像是老式同步代码,因此它们非常值得学习。

文档地址:developer.mozilla.org/en-US/docs/…

代码示例:

// 例一
function hello() { return "Hello" };
hello();

async function hello() { return "Hello" };
hello();

hello().then((value) => console.log(value))

// 例二
async function hello() {
  const greeting = await Promise.resolve("Hello");
  return greeting
};

hello().then((value) => console.log(value));

代码提交地址:git.imooc.com/coding-449/…

#7-9 使用axios拦截器添加loading效果

Axios 拦截器文档地址:axios-http.com/docs/interc…

axios.interceptors.request.use(config => {
  store.commit('setLoading', true)
  return config
})


axios.interceptors.response.use(config => {
  store.commit('setLoading', false)
  return config
})

#7-10 7-11 Loader 组件编码

Bootstrap 提供的 Spinner:getbootstrap.com/docs/5.1/co…

Loader 第一部分编码地址:git.imooc.com/coding-449/…

Vue3 关于 Teleport 的官方文档:vuejs.org/guide/built…

Loader 第二部分编码地址:git.imooc.com/coding-449/…