Encode JSON-RPC GET request params as JSON

This commit is contained in:
Jerko Steiner 2019-08-04 14:07:25 +07:00
parent 81225b77a8
commit e2f6c5f798
4 changed files with 64 additions and 48 deletions

View File

@ -50,7 +50,7 @@ export function jsonrpc<Context>(
service: T, service: T,
methods: F[], methods: F[],
) { ) {
const callRpcService = createRpcService(service, methods) const rpcService = createRpcService(service, methods)
const router = Router() const router = Router()
@ -76,13 +76,19 @@ export function jsonrpc<Context>(
}) })
throw err throw err
} }
callRpcService(req.query, getContext(req)) const request = {
id: req.query.id,
jsonrpc: req.query.jsonrpc,
method: req.query.method,
params: JSON.parse(req.query.params),
}
rpcService.invoke(request, getContext(req))
.then(response => handleResponse(response, res)) .then(response => handleResponse(response, res))
.catch(next) .catch(next)
}) })
router.post('/', (req, res, next) => { router.post('/', (req, res, next) => {
callRpcService(req.body, getContext(req)) rpcService.invoke(req.body, getContext(req))
.then(response => handleResponse(response, res)) .then(response => handleResponse(response, res))
.catch(next) .catch(next)
}) })

View File

@ -69,51 +69,57 @@ export function createSuccessResponse<T>(id: number | string, result: T)
} }
} }
export const createRpcService = export const createRpcService = <T, M extends FunctionPropertyNames<T>>(
<T, M extends FunctionPropertyNames<T>>( service: T,
service: T, methods: M[],
methods: M[], ) => {
) => async <Context>(req: IRequest<M, ArgumentTypes<T[M]>>, context: Context) return {
: Promise<ISuccessResponse<RetType<T[M]>> | null> => { async invoke<Context>(
const {id, method, params} = req req: IRequest<M, ArgumentTypes<T[M]>>,
context: Context,
): Promise<ISuccessResponse<RetType<T[M]>> | null> {
const {id, method, params} = req
if ( if (
req.jsonrpc !== '2.0' || req.jsonrpc !== '2.0' ||
typeof method !== 'string' || typeof method !== 'string' ||
!Array.isArray(params) !Array.isArray(params)
) { ) {
throw createError(ERROR_INVALID_REQUEST, { console.log(req.jsonrpc, method, params)
id, throw createError(ERROR_INVALID_REQUEST, {
data: null, id,
statusCode: 400, data: null,
}) statusCode: 400,
} })
}
const isNotification = req.id === null || req.id === undefined const isNotification = req.id === null || req.id === undefined
const rpcService = pick(service, methods) const rpcService = pick(service, methods)
if ( if (
!rpcService.hasOwnProperty(method) || !rpcService.hasOwnProperty(method) ||
typeof rpcService[method] !== 'function' typeof rpcService[method] !== 'function'
) { ) {
throw createError(ERROR_METHOD_NOT_FOUND, { throw createError(ERROR_METHOD_NOT_FOUND, {
id, id,
data: null, data: null,
statusCode: 404, statusCode: 404,
}) })
} }
let retValue = (rpcService[method] as any)(...params) let retValue = (rpcService[method] as any)(...params)
if (typeof retValue === 'function') { if (typeof retValue === 'function') {
retValue = retValue(context) retValue = retValue(context)
} }
if (!isPromise(retValue) && isNotification) { if (!isPromise(retValue) && isNotification) {
return null return null
} }
retValue = await retValue retValue = await retValue
return createSuccessResponse(req.id as any, retValue) return createSuccessResponse(req.id as any, retValue)
},
} }
}

View File

@ -15,7 +15,8 @@ describe('remote', () => {
interface IService { interface IService {
add(a: number, b: number): number add(a: number, b: number): number
fetchItem(id: number): Promise<string> fetchItem(obj1: {a: number}, obj2: {b: number})
: Promise<{a: number, b: number}>
} }
const IServiceKeys = keys<IService>() const IServiceKeys = keys<IService>()
@ -23,8 +24,9 @@ describe('remote', () => {
add(a: number, b: number) { add(a: number, b: number) {
return a + b return a + b
} }
async fetchItem(id: number): Promise<string> { async fetchItem(obj1: {a: number}, obj2: {b: number})
return Promise.resolve('id:' + id) : Promise<{a: number, b: number}> {
return Promise.resolve({...obj1, ...obj2})
} }
} }
@ -60,8 +62,8 @@ describe('remote', () => {
it('creates a proxy for remote service', async () => { it('creates a proxy for remote service', async () => {
const rpc = createRemoteClient<IService>( const rpc = createRemoteClient<IService>(
baseUrl, '/myService', IServiceKeys) baseUrl, '/myService', IServiceKeys)
const result = await rpc.fetchItem(5) const result = await rpc.fetchItem({a: 10}, {b: 20})
expect(result).toBe('id:5') expect(result).toEqual({a: 10, b: 20})
}) })
}) })

View File

@ -33,7 +33,9 @@ export function createRemoteClient<T>(
id, id,
jsonrpc: '2.0', jsonrpc: '2.0',
method, method,
params, params: reqMethod === 'post'
? params
: JSON.stringify(params),
}, },
}) })
if (response.data.error) { if (response.data.error) {