RxJs How to set default request headers?

2019-04-15 10:35发布

问题:

Not sure is there any way to set default request headers in rxjs like we do with axios js as-

axios.defaults.headers.common['Authorization'] = 'c7b9392955ce63b38cf0901b7e523efbf7613001526117c79376122b7be2a9519d49c5ff5de1e217db93beae2f2033e9';

Here is my epic code where i want to set request headers -

export default function epicFetchProducts(action$, store) {
    return action$.ofType(FETCH_PRODUCTS_REQUEST)
    .mergeMap(action =>
        ajax.get(`http://localhost/products?${action.q}`)
      .map(response => doFetchProductsFulfilled(response))
    );
}

Please help.

回答1:

It's not possible to set default headers for all ajax requests using RxJS's ajax utilities.

You can however provide headers in each call, or create your own simple wrapper that provides them by default.

utils/ajax.js

const defaultHeaders = {
  Authorization: 'c7b9392955ce63b38cf090...etc'
};

export const get = (url, headers) =>
  ajax.get(url, Object.assign({}, defaultHeaders, headers));

my-example.js

import * as ajax from './utils/ajax';

// Usage is the same, but now with defaults
ajax.get(`http://localhost/products?${action.q}`;)


回答2:

I'm using redux-observable but this applies to rxjs; maybe the next answer its too over-engineered, but I needed to get dinamically the headers depending of certain factors, without affecting the unit testing (something decoupled from my epics too), and without changing the sintax of ajax.get/ajax.post etc, this is what I found:

ES6 has proxies support, and after reading this and improving the solution here, I'm using a High Order Function to create a Proxy in the original rxjs/ajax object, and return the proxified object; below is my code:

Note: I'm using typescript, but you can port it to plain ES6.

AjaxUtils.ts

export interface AjaxGetHeadersFn {
    (): Object;
}

// the function names we will proxy
const getHeadersPos = (ajaxMethod: string): number => {
    switch (ajaxMethod) {
        case 'get':
        case 'getJSON':
        case 'delete':
            return 1;
        case 'patch':
        case 'post':
        case 'put':
            return 2;
        default:
            return -1;
    }
};

export const ajaxProxy = (getHeadersFn: AjaxGetHeadersFn) =>
    <TObject extends object>(obj: TObject): TObject => {
        return new Proxy(obj, {
            get(target: TObject, propKey: PropertyKey) {
                const origProp = target[propKey];
                const headersPos = getHeadersPos(propKey as string);

                if (headersPos === -1 || typeof origProp !== 'function') {
                    return origProp;
                }

                return function (...args: Array<object>) {
                    args[headersPos] = { ...args[headersPos], ...getHeadersFn() };
                    // @ts-ignore
                    return origProp.apply(this, args);
                };
            }
        });
    };

You use it this way:

ConfigureAjax.ts

import { ajax as Ajax } from 'rxjs/ajax'; // you rename it

// this is the function to get the headers dynamically
// anything, a function, a service etc.
const getHeadersFn: AjaxGetHeadersFn = () => ({ 'Bearer': 'BLABLABLA' });

const ajax = ajaxProxy(getHeadersFn)(Ajax); // proxified object
export default ajax;

Anywhere in you application you import ajax from ConfigureAjax.ts and use it as normal.

If you are using redux-observable you configure epics this way (injecting ajax object as a dependency more info here):

ConfigureStore.ts

import ajax from './ConfigureAjax.ts'

const rootEpic = combineEpics(
    fetchUserEpic
)({ ajax });

UserEpics.ts

// the same sintax ajax.getJSON, decoupled and
// under the covers with dynamically injected headers
const fetchUserEpic = (action$, state$, { ajax }) => action$.pipe(
  ofType('FETCH_USER'),
  mergeMap(({ payload }) => ajax.getJSON(`/api/users/${payload}`).pipe(
    map(response => ({
      type: 'FETCH_USER_FULFILLED',
      payload: response
    }))
  )
);

Hope it helps people looking for the same :D