Forgive me for being a clicker, but read this article carefully and it will be rewarding!
The main purpose of this article is to make the front end of the request way to do an extremely streamlined and automated, to our heavy brick life, bring a little happiness ^_^.
I’m sure front-end dogs like me are tired of writing various requests and writing various request paths. Everyone requests in different ways in a project, but the general approach is the same. Let’s take a look at some of the common request types and see if they have similar pain points. Take the Vue + Axios project as an example.
#Common file structure
request
|-- config.js
|-- http.js
Copy the code
// config.js, mainly for the project request exception capture, add configuration, etc
import Vue from "vue";
import axios from "axios";
import { Notification } from 'element-ui';
import store from "@/store";
/ / configure the content-type
axios.defaults.headers.post["Content-Type"] = "aplication/json";
/** * Configure axios */
// HTTP request interceptor
axios.interceptors.request.use(
config= > {
return config;
},
err => {
return Promise.reject(err); });// HTTP Response interceptor
axios.interceptors.response.use(
response= > {
// Determine some error code
if(response.data.code == 2) {// identity failure
store.dispatch("LogOut");
}
if(response.data.code ! = =0&& response.data.msg ! = =- 1) {
Notification({
title: 'System error'.message: response.data.msg,
type: "error".offset: 35.duration: 2000
});
}
return response;
},
error => {
console.log(error); });export default axios;
Copy the code
This file, which should exist in all your projects, is used for common configuration and exception catching in requests.
// http.js to wrap axios completed by config
import axios from config
export function get(url, payload){
return axios.get(url, {
params: payload
})
}
export function post(url, payload){
return axios.post(url, {
params: payload
})
}
// export function delete...
Copy the code
This file encapsulates some common axios methods. For consistency of argument passing, you don’t need to write axios. And so on, so that there is a unified entry, call more convenient. This has its advantages, but is there a better solution? What if there were DELETE PUT requests? What if there were 10 ways to request? This is labeled pain point one, and we’re going to do that next.
// test.vue
import * as http from '@/path/to/http'
export default {
methods: {
getData(){
https.get('/v1/systemInfo').then(res= > {
// do something})}}}Copy the code
This is what it looks like when we call it, and it looks pretty regular. But imagine if the backend made a batch of changes to the API. If every interface call is scattered in every component file, it would be tedious to modify them in each file. And every time to check the API document (pain point two) to call the interface path (pain point three) and request (pain point four) is what, and then copy to the business page, every time we do such a thing in the heart is not in silent scold, how so many interfaces to write??
The solution
There’s a lot of crap, a lot of questions. If we don’t come up with a good plan, labor and management… Take it easy, everybody. Listen. Listen to me…
The directory structure
http
|--apiModules
| |--user.js
| |--system.js
|--parse
| |--parse.js
| |--api.json
|--fetch.js
|--config.js
Copy the code
That’s our directory structure, and I’m going to walk you through it
How to solve the pain point 1?
To avoid the tedious process of encapsulating existing request methods, we can write a method that calls the axios object’s method as it is passed in. The way arguments are passed is written in the judgment, which avoids the need to encapsulate a request method for any method we use.
import axios from './config' // The config file is the same as above, which is not explained here
// fetch.js
export function fetch(method, url, payload){
// Check the axios documentation, we know that all the parameters except get are passed directly, so we only need to check get
if(method === 'get') {return axios['get'](url, {params: payload})
} else {
return axios[method](url, payload)
}
}
Copy the code
So our business page code looks like this:
// test.vue
import fetch from '@/path/to/fetch'
export default {
methods: {
getData(){
fetch('get'.'/v1/systemInfo', {... }).then(res= > {
// do something})}}}Copy the code
It looks like nothing’s really changed here, just the method name. But there is a small problem, do I need to reference fetch every time I create a new page? Trouble! So the fetch method can be directly mounted to the VUE instance, which can then be called directly from within the component, solving another minor problem
// fetch.js
class Fetch {
// Provide an installation interface for Vue
install(vue) {
Object.assign(vue.prototype, {
$fetch: this.fetch
});
}
fetch(method, url, payload) {
if(method === 'get') {return axios['get'](url, {params: payload})
} else {
return axios[method](url, payload)
}
}
}
export default new Fetch();
// main.js
import Vue from 'vue'
import Fetch from '@/path/to/fetch'
Vue.use(Fetch)
// test.vue
export default {
methods: {
getData(){
this.$fetch('get'.'/v1/systemInfo', {... }).then(res= > {
// code})}}}Copy the code
How to gracefully solve pain points two, three, four?
Let’s review:
- Request encapsulation (
Pain points a
) - Check the API documentation every time (
(2
) - The interface path to call (
Pain points three
) - View the request type (
Pain points four
)
The first pain point may not all exist, so the second three four should be a common problem, but also this paper mainly want to solve. Request a path in order not to go through the document every time. As a standard front-end configuration, we can unify this information so that we don’t have to check it every time. We should care about the format of the data returned, the parameters passed in, etc. Imagine how happy we would be every time we made this request!
this.$fetch('system.getVersion').then(res= > {
// code
})
/* * You can specify the module name, interface name when calling * * you don't need to know how to request, request path */
Copy the code
To meet the above requirements, we definitely need to use configuration files to record the above information, although we do not need to care about, but the program needs to care about! The./apiModules are used to store this information.
// ./apiModules/system.js
export default {
getVersion: {
url: 'path/to/getVersion'.method: 'get'
},
modVersion: {
url: 'path/to/modVersion'.method: 'post'}}// ./apiModules/user.js
export default {
getInfo: {
url: 'path/to/getInfo'.method: 'get'}}// Of course, the above configuration fields can be customized according to requirements, such as the same apiName to call different interfaces according to the user role, just need to write corresponding judgment in the fetch, very convenient!
Copy the code
So we need to modify the fetch file again
import axios from "./config";
// Generate fetchCfg from the./apiModules folder -- implementation method webpack-require.context()
// fetchCfg = {
// system,
// user
// };
const fetchCfg = {};
// Require. Context allows webpack to automatically reference files in the specified folder
// We store it on fetchCfg for use by the FETCH method
const requireContext = require.context('./apiModules'.false, /\.js$/)
requireContext.keys().forEach(path= > {
let module = path.replace(".js"."").replace(". /"."")
fetchCfg[module] = requireContext(path).default
})
This function is responsible for parsing the module passed into the fetch and apiName * @param {String} param */
const fetchParam = param= > {
var valid = /[a-z]+(\.[a-z])+/.test(param);
if(! valid) {throw new Error(
"[Error in fetch]: The format of the fetch parameter is moduleName. ApiName"
);
} else {
return {
moduleName: param.split(".") [0].apiName: param.split(".") [1]}; }};class Fetch {
// Provide an installation interface for Vue
install(vue) {
Object.assign(vue.prototype, {
$fetch: this.fetch
});
}
@param {*} module corresponds to the name of the FETCH configuration. @param {*} apiName Specifies the name of a request configuration under the module * /
fetch(moduleInfo, payload) {
let prefix = '/api'
let moduleName = fetchParam(moduleInfo)["moduleName"];
let apiName = fetchParam(moduleInfo)["apiName"];
// Determine that the incoming module is not found
if(! fetchCfg.hasOwnProperty(moduleName)){throw new Error(
'[Error in fetch]: module not found in API configuration file ->${moduleName}`
);
}
// The corresponding interface is not found
if(! fetchCfg[moduleName].hasOwnProperty(apiName)){throw new Error(
'[Error in fetch]: in the module${moduleName}Interface not found in ->${apiName}`
);
}
let fetchInfo = fetchCfg[moduleName][apiName];
let method = fetchInfo["method"];
let url = `${prefix}/${fetchInfo["url"]}`;
if (method === "get") {
return axios[method](url, {
params: payload
});
} else {
returnaxios[method](url, payload); }}}export default new Fetch();
Copy the code
Through the above methods, gracefully solved two, three, four, three pain points!
The icing on the cake API configuration file parsing script
Finally, the Parse folder is a nice addition. If you happen to have a swagger or Postman backend, you can export structured files to you, such as JSON, and then you can get the above API configuration information with a simple Node script conversion. Once the API has been modified on the back end, we can run the script again to apply all the changes to the project without having to manually modify the API file, and even if we do, we don’t have to modify it in every business file
Here is my script to read the API layer Postman document, which can also be automated in many ways. For example, if the document is hosted in Git, you can write a shell script in advance every time the API updates the document, synchronize the git updates locally, and then start the Node script (which can be called with NPM from the script tag in package.json) to read/write the document. You might not be able to do that the first time you write the script, but once you’ve written it and you’ve made arrangements with your backend partners, will your work go much faster?
// parse.js
/** * README * reads the middle layer JSON file, generates the API configuration */
let fs = require("fs");
let path = require("path");
let dosJson = require("./api.json");
var jsFile = fs.createWriteStream(path.resolve(__dirname, "./api/ddos.js"), {
encoding: "utf8"
});
function parsePostManJson(json) {
Object.keys(json).map(key= > {
// Add a comment
if (key === "name") {
jsFile.write(` / /${json[key]}`)
console.log(` / /${json[key]}`);
}
if(key === "request") {let urlName = json[key].url.path[json[key].url.path.length - 1];
let url = json[key].url.raw.replace("{{HOST}}"."");
let method = json[key].method;
let params = "";
if(method === "GET"){
params = ` / /${url.split("?") [1]? url.split("?") [1] : ""}`;
url = url.split("?") [0];
}
// let content = `${method === 'GET' ? params : ""}`
let content = `
${urlName}: {
url: "${url}",
method: "${method.toLowerCase()}",
custom: true
},
`
console.log(content);
jsFile.write(content)
}
if(key === "item" && json[key].constructor === Array){
json[key].map(itemJson= >{ parsePostManJson(itemJson); }}})); } jsFile.write(`export default {`)
parsePostManJson(dosJson);
jsFile.write(`} `)
jsFile.end();
jsFile.on('finish'.function(){
console.log('Write done');
})
jsFile.on('error'.function(){
console.log('Write failed');
})
Copy the code
Output API file, but also added some comments, if there is a need to also can directly write the parameter format, later do not have to open the online document to view, is not very convenient?
// ddos.js
export default {
// Get the ddos mode
getDDosCfg: {
url: "/getDDosCfg".method: "post".custom: true.napi: true
},
// DDos Integration // Data report statistics // Obtain equipment room overview information
statisticsInfo: {
url: "/admin/Ddos/Statistic/statisticsInfo".method: "post".custom: true}};Copy the code
To summarize
Ha-ha, it is not easy to see here patiently, you are really a small bar ~ (⊙﹏⊙)
So, in the development, we should try to think about a problem, that is, how to simplify the tedious things. When we encounter the tedious and repetitive problems, we should have the idea to solve them. If we can do it programmatically, we try not to rebrick it. In this way, we can not only get a little happiness in the busy development, but also improve our coding ability slowly
The above solution is just an idea, the specific code implementation can be based on the project framework, the actual reference to the request library, business requirements to encapsulate. Of course, if your business needs are similar to mine, the above code can meet the business needs. I have put the code on Github and welcome you to use it for your reference.