HTTP Request
WinJS provides a built-in plugin solution. Based onaxios andVueHookPlus'suseRequest, it offers a unified network request and error handling solution.
Note
useRequest is only compatible with Vue 3.
import { request, useRequest }from 'winjs';request;useRequest;Setup
- Install the plugin
$ npm add @winner-fed/plugin-request -D$ yarn add @winner-fed/plugin-request -D$ pnpm add @winner-fed/plugin-request -D$ bun add @winner-fed/plugin-request -D- Enable the plugin in the
.winrcconfiguration file
import { defineConfig }from 'win';export default defineConfig({ plugins: ['@winner-fed/plugin-request'], /** *@name Request Plugin *@description Request plugin based on axios *@doc https://winjs-dev.github.io/winjs-docs/plugins/request.html */ request: {},});Configuration
Build-time Configuration
export default { request: { dataField:'data' },};Build-time configuration allows you to configure thedataField for useRequest, with a default value ofdata. The main purpose of this configuration is to facilitate direct data consumption by useRequest. If you want to access the raw backend data when consuming data, you need to configuredataField as'' here.
For example, if your backend returns data in the following format:
{ success:true, data:123, code:1}Then useRequest can directly consumedata, with a value of 123, instead of{ success, data, code }.
Runtime Configuration
Insrc/app.ts, you can configure the request item to set unified and personalized request settings for your project.
import type { RequestConfig }from 'winjs';export const request: RequestConfig = { timeout:1000, // other axios options you want errorConfig: { errorHandler() { }, errorThrower() { } }, requestInterceptors: [], responseInterceptors: []};All configurations excepterrorConfig,requestInterceptors, andresponseInterceptors are directly passed through toaxios request configuration.Rules configured here will apply to allrequestanduseRequestmethods.
Below we'll introduce the runtime configuration options forplugin-request. At the end of this section, we'll provide a complete runtime configuration example and describe its functionality in detail.
errorConfig
If you want to set a unified error handling solution for your requests, you can configure it here.
TheerrorThrower receives data returned by your backend and needs to throw a custom error. You can process the backend data here as needed.
Ourrequest will catch the error thrown byerrorThrower and execute yourerrorHandler method, which receives two parameters: the first parameter is the caught error, and the second parameter is the request opts.
TheerrorHandler anderrorThrower here need to be used together. There's a complete example at the end of the documentation.
If you find this approach to error handling too cumbersome, you can implement your own error handling directly in interceptors.
Note
errorThrower is implemented usingresponseInterceptors, and its trigger condition is: whendata.success isfalse.
requestInterceptors
Adds request-phase interceptors to the request method.
Pass in an array where each element is an interceptor. They will be registered sequentially on the axios instance in order. The interceptor syntax is consistent with axios request interceptors - it needs to receive the request config as a parameter and return it.
We recommend usingRequestConfig, which helps you write your interceptors in a standardized way.
e.g.
const request: RequestConfig = { requestInterceptors: [ // Write a function directly as an interceptor (url,options)=> { // do something return { url, options } }, // A tuple, first element is the request interceptor, second element is error handling [(url,options)=> { return { url, options } }, (error)=> { return Promise.reject(error) }], // Array, omitting error handling [(url,options)=> { return { url, options } }] ]}responseInterceptors
Adds response-phase interceptors to the request method.
Pass in an array where each element is an interceptor. They will be registered sequentially on the axios instance in order. The interceptor syntax is consistent with axios response interceptors. It receives axios response as a parameter and returns it.
We recommend usingRequestConfig, which helps you write your interceptors in a standardized way.
e.g.
const request: RequestConfig = { responseInterceptors: [ // Write a function directly as an interceptor (response)=> { // No need for async processing to read response body content, can be read directly from data, some fields can be found in config const {data = {}as any,config }= response; // do something return response }, // A tuple, first element is the request interceptor, second element is error handling [(response)=> { return response }, (error)=> { return Promise.reject(error) }], // Array, omitting error handling [(response)=> { return response }] ]}Note
We will register interceptors sequentially according to your array order, but their execution order follows axios: for requests, later additions execute first; for responses, later additions execute last.
API
request
Throughimport { request } from '@@/plugin-request' you can use the built-in request method.
Theoptions thatrequest receives, in addition to passing through allaxios config, we've also added several additional properties:skipErrorHandler,getResponse,requestInterceptors, andresponseInterceptors.
Example:
request('/api/user', { params: { name:1 }, timeout:2000, // other axios options skipErrorHandler:true, getResponse:false, requestInterceptors: [], responseInterceptors: [],}When you want a specific request to skip error handling, you can setskipErrorHandler totrue.
By default, request returns your backend data. If you want to get the complete axios response structure, you can pass{ getResponse: true }.
The syntax forrequestInterceptors andresponseInterceptors is the same as the interceptor syntax in runtime configuration. They register interceptors for the request. The difference is that interceptors registered here are "one-time". Additionally, interceptors written here will be registered after those in runtime configuration.
Note
When you use errorHandler, response interceptors registered here will be ineffective because errors will be thrown in errorHandler.
RequestConfig
This is an interface definition that can help you better configure runtime settings.
import type { RequestConfig }from 'winjs';export const request: RequestConfig = {};Note that you should addtype when importing.
useRequest
The plugin has built-inVueHooks/useRequest. You can use this Hook within components to consume data simply and conveniently. Example:
import { useRequest }from 'winjs';export default function Page() { const {data,error,loading }= useRequest(()=> { return services.getUserList('/api/test'); }); if (loading) { return <div>loading ... </div>; } if (error) { return <div>{ error.message }< /div>; } return <div>{ data.name }< /div>;};In the code above,data is not the data returned by your backend, but the internaldata (because the build-time configuration default is 'data').
Runtime Configuration Example
Here's a complete runtime configuration example to help you better set up personalized request solutions for your project.
import { RequestConfig }from './request';// Error handling solution: error typesenum ErrorShowType { SILENT = 0, WARN_MESSAGE = 1, ERROR_MESSAGE = 2, NOTIFICATION = 3, REDIRECT = 9,}// Response data format agreed upon with backendinterface ResponseStructure { success: boolean; data: any; errorCode?: number; errorMessage?: string; showType?: ErrorShowType;}// Runtime configurationexport const request: RequestConfig = { // Unified request settings timeout:1000, headers: {'X-Requested-With':'XMLHttpRequest' }, // Error handling errorConfig: { // Error throwing errorThrower: (res: ResponseStructure)=> { const {success,data,errorCode,errorMessage,showType }= res; if (!success) { const error: any = new Error(errorMessage); error.name= 'BizError'; error.info= { errorCode, errorMessage, showType, data }; throw error;// Throw custom error } }, // Error receiving and handling errorHandler: (error: any,opts: any)=> { if (opts?.skipErrorHandler)throw error; // Errors thrown by our errorThrower if (error.name=== 'BizError') { const errorInfo: ResponseStructure | undefined = error.info; if (errorInfo) { const {errorMessage,errorCode }= errorInfo; switch (errorInfo.showType) { case ErrorShowType.SILENT: // do nothing break; case ErrorShowType.WARN_MESSAGE: message.warn(errorMessage); break; case ErrorShowType.ERROR_MESSAGE: message.error(errorMessage); break; case ErrorShowType.NOTIFICATION: notification.open({ description: errorMessage, message: errorCode, }); break; case ErrorShowType.REDIRECT: // TODO: redirect break; default: message.error(errorMessage); } } }else if (error.response) { // Axios errors // Request was made and server responded with a status code outside the 2xx range message.error(`Response status:${error.response.status}`); }else if (error.request) { // Request was made but no response received // `error.request` is an instance of XMLHttpRequest in the browser, // and an instance of http.ClientRequest in node.js message.error('None response! Please retry.'); }else { // Something happened in setting up the request message.error('Request error, please retry.'); } }, }, // Request interceptors requestInterceptors: [ (config)=> { // Intercept request configuration for personalized processing const url = config.url.concat('?token = 123'); return {...config, url }; } ], // Response interceptors responseInterceptors: [ (response)=> { // Intercept response data for personalized processing const {data }= response; if (!data.success) { message.error('Request failed!'); } return response; } ]};You can also implement your own error handling by writing response interceptors,not necessarily limited to errorConfig.
