Web應用程序的通用數據訪問層。
通常,在服務器上,您可以直接調用API或數據庫以獲取某些數據。但是,在客戶端,您不能總是以相同的方式調用您的服務(即跨域政策)。相反,需要向已轉發到您的服務的服務器提出XHR/Fetch請求。
在兩個環境中必須以不同的方式編寫代碼是重複的,並且容易出錯。 Fetchr在數據服務調用上提供了一個抽象層,因此您可以使用服務器和客戶端上的相同API獲取數據。
npm install fetchr --save重要的是:在瀏覽器上, Fetchr完全依賴Fetch API。如果您需要支持舊瀏覽器,則還需要安裝多填充(例如https://github.com/github/fetch)。
請按照以下步驟正確設置fetchr。這假設您正在使用Express框架。
在服務器端,將Fetchr中間件添加到自定義API端點的Express應用中。
Fetchr中間件期望您在使用Fetchr中間件之前使用body-parser中間件(或填充req.body的替代中間件)。
import express from 'express' ;
import Fetcher from 'fetchr' ;
import bodyParser from 'body-parser' ;
const app = express ( ) ;
// you need to use body-parser middleware before fetcher middleware
app . use ( bodyParser . json ( ) ) ;
app . use ( '/myCustomAPIEndpoint' , Fetcher . middleware ( ) ) ;在客戶端, xhrPath選項必須匹配上一步中中間件的路徑
xhrPath是一種可選的配置屬性,允許您自定義服務的端點,默認為/api 。
import Fetcher from 'fetchr' ;
const fetcher = new Fetcher ( {
xhrPath : '/myCustomAPIEndpoint' ,
} ) ;您將需要註冊要在應用程序中使用的任何數據服務。您服務的接口將是必須定義resource屬性和至少一個CRUD操作的對象。當您調用CRUD操作之一時,將使用resource屬性。
// app.js
import Fetcher from 'fetchr' ;
import myDataService from './dataService' ;
Fetcher . registerService ( myDataService ) ; // dataService.js
export default {
// resource is required
resource : 'data_service' ,
// at least one of the CRUD methods is required
read : async function ( { req , resource , params , config } ) {
return { data : 'foo' } ;
} ,
// other methods
// create: async function({ req, resource, params, body, config }) {},
// update: async function({ req, resource, params, body, config }) {},
// delete: async function({ req, resource, params, config }) {}
} ;數據服務可能需要訪問每個單獨的請求,例如,以獲取當前登錄的用戶會話。因此,必鬚根據請求對Fetcher進行實例化。
在Serveride上,這需要在Express Mifdreware中根據請求實例化Fetcher。在客戶端,這只需要在頁面加載上發生。
// app.js - server
import express from 'express' ;
import Fetcher from 'fetchr' ;
import myDataService from './dataService' ;
const app = express ( ) ;
// register the service
Fetcher . registerService ( myDataService ) ;
// register the middleware
app . use ( '/myCustomAPIEndpoint' , Fetcher . middleware ( ) ) ;
app . use ( function ( req , res , next ) {
// instantiated fetcher with access to req object
const fetcher = new Fetcher ( {
xhrPath : '/myCustomAPIEndpoint' , // xhrPath will be ignored on the serverside fetcher instantiation
req : req ,
} ) ;
// perform read call to get data
fetcher
. read ( 'data_service' )
. params ( { id : 42 } )
. then ( ( { data , meta } ) => {
// handle data returned from data fetcher in this callback
} )
. catch ( ( err ) => {
// handle error
} ) ;
} ) ; // app.js - client
import Fetcher from 'fetchr' ;
const fetcher = new Fetcher ( {
xhrPath : '/myCustomAPIEndpoint' , // xhrPath is REQUIRED on the clientside fetcher instantiation
} ) ;
fetcher
. read ( 'data_api_fetcher' )
. params ( { id : 42 } )
. then ( ( { data , meta } ) => {
// handle data returned from data fetcher in this callback
} )
. catch ( ( err ) => {
// handle errors
} ) ;
// for create you can use the body() method to pass data
fetcher
. create ( 'data_api_create' )
. body ( { some : 'data' } )
. then ( ( { data , meta } ) => {
// handle data returned from data fetcher in this callback
} )
. catch ( ( err ) => {
// handle errors
} ) ; 請參閱簡單的示例。
服務客戶透明的服務通話成為提取請求。在常見的接聽電話上設置緩存標頭是一個好主意。您可以通過在服務回調中提供第三個參數來做到這一點。如果您想查看剛剛調用的服務設置的標題,只需檢查回調中的第三個參數即可。
注意:如果您使用的是承諾,則元數據將在解決值的meta屬性上可用。
// dataService.js
export default {
resource : 'data_service' ,
read : async function ( { req , resource , params , config } ) {
return {
data : 'response' , // business logic
meta : {
headers : {
'cache-control' : 'public, max-age=3600' ,
} ,
statusCode : 200 , // You can even provide a custom statusCode for the fetch response
} ,
} ;
} ,
} ; fetcher
. read ( 'data_service' )
. params ( { id : ### } )
. then ( ( { data , meta } ) {
// data will be 'response'
// meta will have the header and statusCode from above
} ) ;有一種便利方法稱為fetcher.getServiceMeta在fetchr實例上。此方法將返回以數組格式到目前為止發生的所有調用的元數據。在服務器中,這將包括用於當前請求的所有服務調用。在客戶端中,這將包括當前會話的所有服務調用。
通常,您會為整個瀏覽器會話實例化Fetcher的一些默認選項,但是在某些情況下,您需要在同一會話中更新這些選項。
您可以使用updateOptions方法來做到這一點:
// Start
const fetcher = new Fetcher ( {
xhrPath : '/myCustomAPIEndpoint' ,
xhrTimeout : 2000 ,
} ) ;
// Later, you may want to update the xhrTimeout
fetcher . updateOptions ( {
xhrTimeout : 4000 ,
} ) ; 當您的Fetchr Crud方法發生錯誤時,您應該扔一個錯誤對象。錯誤對象應包含一個statusCode (默認500)和output屬性,其中包含一個將發送給客戶端的JSON序列化對象。
export default {
resource : 'FooService' ,
read : async function create ( req , resource , params , configs ) {
const err = new Error ( 'it failed' ) ;
err . statusCode = 404 ;
err . output = { message : 'Not found' , more : 'meta data' } ;
err . meta = { foo : 'bar' } ;
throw err ;
} ,
} ;並在您的服務電話中:
fetcher
. read ( 'someData' )
. params ( { id : '42' } )
. catch ( ( err ) => {
// err instanceof FetchrError -> true
// err.message -> "Not found"
// err.meta -> { foo: 'bar' }
// err.name = 'FetchrError'
// err.output -> { message: "Not found", more: "meta data" }
// err.rawRequest -> { headers: {}, method: 'GET', url: '/api/someData' }
// err.reason -> BAD_HTTP_STATUS | BAD_JSON | TIMEOUT | ABORT | UNKNOWN
// err.statusCode -> 404
// err.timeout -> 3000
// err.url -> '/api/someData'
} ) ; 在客戶端創建fetchr請求時,返回具有abort方法的對象。如果您想在請求完成之前中止該請求,這將很有用。
const req = fetcher
. read ( 'someData' )
. params ( { id : 42 } )
. catch ( ( err ) => {
// err.reason will be ABORT
} ) ;
req . abort ( ) ; xhrTimeout是一個可選的配置屬性,允許您為所有客戶端請求設置超時(在MS中),默認為3000 。在客戶端,XHRPATH和XHRTIMEOUT將用於所有請求。在Serveride上,不需要XHRPATH和XHRTIMEOUT,並且被忽略。
import Fetcher from 'fetchr' ;
const fetcher = new Fetcher ( {
xhrPath : '/myCustomAPIEndpoint' ,
xhrTimeout : 4000 ,
} ) ;如果要設置每個請求的超時,則可以使用timeout屬性調用clientConfig :
fetcher
. read ( 'someData' )
. params ( { id : 42 } )
. clientConfig ( { timeout : 5000 } ) // wait 5 seconds for this request before timing out
. catch ( ( err ) => {
// err.reason will be TIMEOUT
} ) ; 對於某些應用程序,在某些情況下,您可能需要處理請求中通過的服務參數,然後再將其發送到實際服務。通常,您將在服務本身中處理它們。但是,如果您需要在許多服務(即安全安全)上執行處理,則可以使用paramsProcessor選項。
paramsProcessor是傳遞到Fetcher.middleware方法的函數。它傳遞了三個參數,請求對象,serviceInfo對象和服務參數對象。然後, paramsProcessor函數可以在需要時修改服務參數。
這是一個示例:
/**
Using the app.js from above, you can modify the Fetcher.middleware
method to pass in the paramsProcessor function.
*/
app . use (
'/myCustomAPIEndpoint' ,
Fetcher . middleware ( {
paramsProcessor : function ( req , serviceInfo , params ) {
console . log ( serviceInfo . resource , serviceInfo . operation ) ;
return Object . assign ( { foo : 'fillDefaultValueForFoo' } , params ) ;
} ,
} ) ,
) ; 對於某些應用程序,在某些情況下,您需要在將響應傳遞給客戶端之前修改響應。通常,您將在服務本身中應用您的修改。但是,如果您需要修改許多服務中的響應(即添加調試信息),則可以使用responseFormatter選項。
responseFormatter是傳遞到Fetcher.middleware方法的函數。它通過了三個參數,即請求對象,響應對象和服務響應對象(即從您的服務返回的數據)。然後, responseFormatter功能可以修改服務響應以添加其他信息。
看看下面的示例:
/**
Using the app.js from above, you can modify the Fetcher.middleware
method to pass in the responseFormatter function.
*/
app . use (
'/myCustomAPIEndpoint' ,
Fetcher . middleware ( {
responseFormatter : function ( req , res , data ) {
data . debug = 'some debug information' ;
return data ;
} ,
} ) ,
) ;現在,當執行請求時,您的響應將包含上面添加的debug屬性。
Fetchr通過允許您將完整的Origin主機傳遞到corsPath選項中來提供CORS支持。
例如:
import Fetcher from 'fetchr' ;
const fetcher = new Fetcher ( {
corsPath : 'http://www.foo.com' ,
xhrPath : '/fooProxy' ,
} ) ;
fetcher . read ( 'service' ) . params ( { foo : 1 } ) . clientConfig ( { cors : true } ) ;此外,您還可以自定義如何通過執行read呼叫時通過在constructGetUri屬性中構建GET URL:
import qs from 'qs' ;
function customConstructGetUri ( uri , resource , params , config ) {
// this refers to the Fetcher object itself that this function is invoked with.
if ( config . cors ) {
return uri + '/' + resource + '?' + qs . stringify ( this . context ) ;
}
// Return `falsy` value will result in `fetcher` using its internal path construction instead.
}
import Fetcher from 'fetchr' ;
const fetcher = new Fetcher ( {
corsPath : 'http://www.foo.com' ,
xhrPath : '/fooProxy' ,
} ) ;
fetcher . read ( 'service' ) . params ( { foo : 1 } ) . clientConfig ( {
cors : true ,
constructGetUri : customConstructGetUri ,
} ) ; 您可以通過在其前面添加中間件來保護Fetchr中間件路徑免受CSRF攻擊:
app.use('/myCustomAPIEndpoint', csrf(), Fetcher.middleware());
您可以將https://github.com/expressjs/csurf使用。
接下來,您需要確保通過我們的請求發送CSRF令牌,以便可以驗證它們。為此,請將令牌傳遞為options.context對象的鍵:
const fetcher = new Fetcher ( {
xhrPath : '/myCustomAPIEndpoint' , // xhrPath is REQUIRED on the clientside fetcher instantiation
context : {
// These context values are persisted with client calls as query params
_csrf : 'Ax89D94j' ,
} ,
} ) ;此_csrf將作為查詢參數發送在所有客戶端請求中,以便可以在服務器上驗證。
調用fetcher服務時,您可以傳遞可選的配置對象。
從客戶端進行此調用時,配置對像用於設置一些請求選項,可用於覆蓋默認選項:
//app.js - client
const config = {
timeout : 6000 , // Timeout (in ms) for each request
unsafeAllowRetry : false , // for POST requests, whether to allow retrying this post
} ;
fetcher . read ( 'service' ) . params ( { id : 1 } ) . clientConfig ( config ) ;對於服務器的請求,簡單地將配置對像傳遞到正在調用的服務中。
您可以通過在全局或請求配置中指定retry配置來將fetchr設置為自動重試失敗的請求:
// Globally
const fetchr = new Fetchr ( {
retry : { maxRetries : 2 } ,
} ) ;
// Per request
fetchr . read ( 'service' ) . clientConfig ( {
retry : { maxRetries : 1 } ,
} ) ;通過上述配置,Fetchr將兩次重試所有失敗的請求,但在調用read('service')時只有一次。
您可以進一步自定義重試機制的工作原理。這些都是所有設置及其默認值:
const fetchr = new Fetchr ( {
retry : {
maxRetries : 2 , // amount of retries after the first failed request
interval : 200 , // maximum interval between each request in ms (see note below)
statusCodes : [ 0 , 408 ] , // response status code that triggers a retry (see note below)
} ,
unsafeAllowRetry : false , // allow unsafe operations to be retried (see note below)
}間隔
每個請求之間的間隔尊重以下公式,基於此AWS Architecture博客文章中發表的指數退縮和完整的抖動策略:
Math . random ( ) * Math . pow ( 2 , attempt ) * interval ; attempt是從0開始的當前重試嘗試的數量。默認interval為200ms。
狀態代碼
由於歷史原因,Fetchr僅重新檢索408個響應,根本沒有響應(例如,由狀態代碼0表示的網絡錯誤)。但是,您可能還會發現對其他代碼也重試(502、503、504可能是自動恢復的良好候選者)。
Undafeallowretry
默認情況下,fetchr僅重新檢驗read請求。這是出於安全原因而進行的:從數據庫中閱讀兩次條目並不像創建兩次條目那樣糟糕。但是,如果您的應用程序或資源不需要這種保護,則可以通過將unsafeAllowRetry設置為true允許重試,Fetchr將重試所有操作。
默認情況下,Fetchr將所有上下文值附加到請求URL作為查詢參數。 contextPicker允許您對哪些上下文變量作為查詢參數(根據請求方法( GET或POST )的不同)提供更大的控制。當您想限制GET URL中的變量數量以便不要意外緩存胸圍時,這很有用。
contextPicker遵循與lodash/pickBy中的predicate參數相同的格式,並帶有兩個參數: (value, key) 。
const fetcher = new Fetcher ( {
context : {
// These context values are persisted with client calls as query params
_csrf : 'Ax89D94j' ,
device : 'desktop' ,
} ,
contextPicker : {
GET : function ( value , key ) {
// for example, if you don't enable CSRF protection for GET, you are able to ignore it with the url
if ( key === '_csrf' ) {
return false ;
}
return true ;
} ,
// for other method e.g., POST, if you don't define the picker, it will pick the entire context object
} ,
} ) ;
const fetcher = new Fetcher ( {
context : {
// These context values are persisted with client calls as query params
_csrf : 'Ax89D94j' ,
device : 'desktop' ,
} ,
contextPicker : {
GET : [ 'device' ] , // predicate can be an array of strings
} ,
} ) ; 調用Fetcher服務時,您可以添加自定義請求標頭。
當您將headers選項添加到“ clientconfig”時,請求包含自定義標頭。
const config = {
headers : {
'X-VERSION' : '1.0.0' ,
} ,
} ;
fetcher . read ( 'service' ) . params ( { id : 1 } ) . clientConfig ( config ) ;當您將headers選項添加到“ Fetcher”的構造函數參數中時,所有請求都包含自定義標頭。
import Fetcher from 'fetchr' ;
const fetcher = new Fetcher ( {
headers : {
'X-VERSION' : '1.0.0' ,
} ,
} ) ; 要收集Fetcher Service的成功/失敗/延遲統計信息,您可以為Fetchr配置statsCollector 。 statsCollector函數將使用一個辯論: stats 。 stats對象將包含以下字段:
create|read|update|delete import Fetcher from 'fetchr' ;
const fetcher = new Fetcher ( {
xhrPath : '/myCustomAPIEndpoint' ,
statsCollector : function ( stats ) {
// just console logging as a naive example. there is a lot more you can do here,
// like aggregating stats or filtering out stats you don't want to monitor
console . log (
'Request for resource' ,
stats . resource ,
'with' ,
stats . operation ,
'returned statusCode:' ,
stats . statusCode ,
' within' ,
stats . time ,
'ms' ,
) ;
} ,
} ) ; app . use (
'/myCustomAPIEndpoint' ,
Fetcher . middleware ( {
statsCollector : function ( stats ) {
// just console logging as a naive example. there is a lot more you can do here,
// like aggregating stats or filtering out stats you don't want to monitor
console . log (
'Request for resource' ,
stats . resource ,
'with' ,
stats . operation ,
'returned statusCode:' ,
stats . statusCode ,
' within' ,
stats . time ,
'ms' ,
) ;
} ,
} ) ,
) ; 該軟件可以在Yahoo!下免費使用。 Inc. BSD許可證。有關許可文本和版權信息,請參見許可證文件。