Декларативный HTTP-клиент на основе Axios для браузера и node.js.
Написано Java-программистом, привыкшим к декларативному HTTP-клиенту.
Цель состоит в том, чтобы предоставить простой и лаконичный http-клиент во всех средах javascript/typescript (на самом деле среда es6). ( еще нет! )
Потому что babel-plugin-transform-decorators-legacy
еще не поддерживает декоратор параметров, так что RetrofitJs пока работает только в среде TypeScript.
Процитируйте это предложение здесь:
Вы можете украсить весь класс, а также объявления полей, геттеров, сеттеров и методов. Аргументы и объявления функций не могут быть декорированы. -- от предложений-декораторов
а также кто-то выдвинул этот запрос -> Декораторы параметров?
Теперь давайте сосредоточимся на объявлении собственного http-интерфейса, а не на деталях http, как это сделал Retrofit: меньше пишите, делайте больше.
В заключение большое спасибо всем, кто написал или обновил Axios (http-клиент javascript) и Retrofit (http-клиент java), это замечательные проекты, они многому меня научили.
Эта программа не может работать в IE8/9/10/11 и других средах (обычно браузерах), которые не поддерживают es6 в исходном виде, поскольку это зависит от объекта Proxy
(es6) и функции Decorator
(этап 2).
Другими словами, RetrofitJs может работать в любой среде, которая поддерживает es6 в исходном виде.
Что касается объекта Proxy
, я не могу найти поддерживаемый полифил. Но что касается Decorator
, вы можете использовать его с помощью чего-то, что поддерживает Babel, например, babel-plugin-transform-decorators-legacy
.
Использование НПМ:
npm install retrofitjs
// This is typescript demo, also javascript demo( it is if you remove all type define )
// In the first step, you must create your retrofit object.
let client = Retrofit . getBuilder ( )
. setConfig < RetrofitConfig > ( { /** config, you can use retrofit config or axios config */ } )
. addInterceptor ( /** your interceptor */ )
. setErrorHandler ( /** define your error handler */ )
. build ( ) ;
// This is the part of define any interface what you need.
@ HTTP ( "/testing" )
@ Headers ( [ "Cache-Control: no-store" ] )
class TestingClient {
@ GET ( "/demo1/:callByWho/:when" )
public demo1 ( @ Path ( "callByWho" ) name : string , @ Path ( "when" ) time : number ) : RetrofitPromise < string > & void {
}
@ POST ( "/demo2/:file" )
public demo2 ( @ Path ( "file" ) file : string , @ Header ( "cookie" ) val : string , @ Config localConfig : AxiosConfig ) : RetrofitPromise < string > & void {
}
}
// The final step, create your client.
export let testingClient = client . create ( TestingClient ) ;
// When you are calling this method, it is a http call actually.
testingClient . demo1 ( "itfinally" , Date . now ( ) ) . then ( response => {
// any code
} ) . catch ( reason => {
// any code
} ) ;
// And you can also get axios instance.
let axios : AxiosInstance = client . getEngine ( ) ;
Как и Retrofit, RetrofitJs также реализуется с помощью цепочки перехватчиков. Все перехватчики сортируются по полю order
и сортируются от большего к меньшему .
// This is a interceptor interface
export interface Interceptor {
order : number ;
init ( config : RetrofitConfig ) : void ;
intercept ( chain : Chain ) : Promise < ResponseInterface < any > >
}
Будьте осторожны, перехватчик RealCall
является последним перехватчиком, и так и должно быть.
Это перехватчик по умолчанию, значение поля order
равно нулю. RetrofitJs использовал его для отправки всех http-запросов объектом Axios.
Во избежание конфликта номера полей порядка менее 256 (не включая 256) зарезервированы.
Вы можете легко реализовать свой собственный перехватчик, вот так.
class MyInterceptor implement Interceptor {
public order : number = 256 ;
public init ( config : RetrofitConfig ) : void {
// Initializing your interceptor
}
public intercept ( chain : Chain ) : Promise < ResponseInterface < any > > {
// Your code
return chain . proceed ( chain . request ( ) ) ;
}
}
Обратите внимание на chain.proceed( chain.request() )
, этот код решает, будет ли продолжаться запрос.
Если вы вызываете с помощью chain.proceed
, текущий запрос будет передан следующему перехватчику. В противном случае перехватчик rest не будет активен. И весь процесс будет завершен и активен обработчик ошибок, если какая-либо ошибка возникнет из цепочки перехватчиков.
Фактически, плагины являются перехватчиками. Вы можете написать свой собственный плагин для RetrofitJs.
Retrofit.use( new MyInterceptor implements Interceptor {
public order: number = 20;
public init( config: RetrofitConfig ): void {
}
public intercept( chain: Chain ): Promise<ResponseInterface<any>> {
}
} );
Он добавит все перехватчики в Retrofit
при создании экземпляра.
Нет ограничений на поле заказа плагинов. Фактически, эти номера зарезервированы для плагинов и перехватчиков по умолчанию.
И это все перехватчик по умолчанию:
перехватчик | заказ |
---|---|
RealCall | 0 |
Перехватчик повторного запроса | 1 |
РегистраторПерехватчик | 5 |
По умолчанию этот перехватчик отключен. Он будет повторять попытку через случайное время, если идемпотентный запрос вызывает сетевую аномалию. например, тайм-аут соединения при отправке запроса «GET».
Если вы хотите его использовать, вам следует установить maxTry
и timeout
в RetrofitConfig
.
Вы можете установить в RetrofitConfig
:
{
"maxTry": "number",
"retryCondition": "RetryCondition"
}
RetryCondition
— это интерфейс, перехватчик попытается отправить запрос еще раз, если RetryCondition.handler
вернет true.
Этот перехватчик отключен по умолчанию, он будет записывать все запросы/ответы (или ошибки) и выводить их на консоль.
Вы можете установить в RetrofitConfig
:
{
"debug": "boolean"
}
Вы можете легко отменить заявку.
let tick = testingClient . demo1 ( ... args ) ;
// Careful the different between micro task and macro task
setTimeout ( ( ) => tick . cancel ( "your message" ) , 3000 ) ;
Осторожно, API cancel
— это метод объекта RetrofitPromise
, вы не можете вызывать его с другим объектом обещания, это пример:
// It is wrong.
tick . then ( ( ) => { ... you code } ) . cancel ( "message" ) ;
Когда вы отменяете запрос, будет выдано исключение RequestCancelException
, которое вы можете обработать или игнорировать в обработчике ошибок.
Если вы хотите унифицированную обработку всех исключений, просто реализуйте ErrorHandler
.
class MyErrorHandler implement ErrorHandler {
public handler ( realReason : any , exception : Exception ) : void {
// your code
}
}
realReason
— это параметр, заданный axios, а exception
— это экземпляр Exception
, вы можете легко обработать все исключения в Uniform.
Это все исключение:
исключение | описание |
---|---|
RequestCancelException | Отмена пользователя |
ConnectException | Активирован сигнал ECONNREFUSED |
SocketException | Сигнал ECONNRESET активирован |
RequestTimeoutException | Активирован сигнал ECONNABORTED или ETIMEDOUT |
Исключение IO | остальная неизвестная ситуация |
Хотя обработчик ошибок может перехватывать все исключения, это не означает, что Promise.catch
не будет активен. Фактически, это необходимо по причине завершения обычного процесса, когда было выдано исключение.
class MyErrorHandler implement ErrorHandler {
public handler ( realReason : any , exception : Exception ) : void {
// Your code
}
}
testingClient . demo1 ( ... args ) . then ( response => {
// Normal business process
} ) . catch ( reason => {
// Active after error handler call
} ) ;
В одном знаке метода невозможно украсить один параметр мультидекоратором.
// It is wrong!
public demo1 < T > ( @ Field ( "key1" ) @ Header ( "header1" ) val1 : any ) : RetrofitPromise < T > & void {
}
Если вы хотите создать запрос URL-адреса, например https://127.0.0.1/demo?key1=val1&key2=val2
, просто сделайте это следующим образом:
public demo1 < T > ( @ Query ( "key1" ) val1 : any , @ QueryMap map1 : object ) : RetrofitPromise < T > & void {
}
@Query
объявляет запись значения ключа запроса.@QueryMap
объявляет запрос с несколькими ключами-значениями.Легко отправить форму.
@ FormUrlEncoded
public demo1 < T > ( @ Field ( "key1" ) val1 : any , @ FieldMap map1 : object ) : RetrofitPromise < T > & void {
}
@Field
и @FieldMap
действуют только в том случае, если метод объявлен @FormUrlEncoded
.
@FormUrlEncoded
заявляет, что это форма.@Field
объявляет запись значения ключа формы.@FieldMap
объявляет форму с несколькими ключами-значениями. Если вы хотите запросить тело в формате json, используйте @Body
для украшения параметра.
public demo1 < T > ( @ Body myBody : object ) : RetrofitPromise < T > & void {
}
@Body
нельзя использовать с @FormUrlEncoded
или @MultiPart
, поскольку в одном запросе используется одно тело. Также @Body
не может использовать более одного знака в одном методе.
// It is wrong!
public demo1 < T > ( @ Body myBody : object , @ Body otherBody : object ) : RetrofitPromise < T > & void {
}
Как и в приведенном выше случае, параметр myBody
будет игнорироваться.
Если вы хотите переопределить конфигурацию, используйте @Config
для украшения параметра.
public demo1 < T > ( @ Config config : RetrofitRequest ) : RetrofitPromise < T > & void {
}
Это будет переопределить настройку декоратора (но не включая глобальную конфигурацию) по полю, которое содержит конфигурация параметра.
Динамическая конфигурация эффективна только в запросе, но не в конфигурации перехватчика, поскольку перехватчик инициализируется при вызове Retrofit.getBuilder().build()
и инициализируется только один раз.
Вы можете легко загрузить файл с помощью RetrofitJs следующим образом:
@ MultiPart
@ PUT ( "/upload" )
public upload ( @ Part ( "file" ) file : any , @ PartMap anything : any ) : RetrofitPromise < void > & void {
}
Это способ браузера:
// document.getElementById( "file" ) is a input tag
client . upload ( document . getElementById ( "file" ) . files [ 0 ] ) ;
И это путь узла:
// create a file read stream as parameter, done.
client . upload ( fs . createReadStream ( "your file path" ) ) ;
Как и при отправке формы, @Part
и @PartMap
также действуют только в том случае, если метод объявлен @MultiPart
.
@MultiPart
заявляет, что это форма.@Part
объявляет запись значения ключа формы.@PartMap
объявляет форму с несколькими ключами-значениями.Также вы должны быть осторожны с настройкой максимального размера файла на вашем сервере. Загрузка всегда завершалась неудачно, если размер файла превышал ограничение вашего сервера.
Наконец, не используйте объект Buffer
в качестве параметра. Я пытаюсь использовать объект буфера для загрузки, но все не удалось, поскольку объект буфера имеет только данные, но не какое-либо описание, такое как имя файла, тип файла.
В браузере невозможно загрузить файл с помощью Ajax, поскольку Ajax всегда отвечает строковыми данными, но вы можете использовать тег iframe для активной загрузки браузера.
Вы можете скачать файл на узле следующим образом:
@ ResponseBody ( ResponseType . STREAM )
public demo1 ( ) : RetrofitPromise < Stream > & void {
}
@ResponseBody
сообщит RetrofitJ, какой тип следует вернуть.
Это все поддерживаемые типы ответов:
тип | ценить |
---|---|
объект | ResponseType.JSON (по умолчанию) |
нить | ТипОтвета.ДОКУМЕНТ, ТипОтвета.ТЕКСТ |
Транслировать | ResponseType.STREAM |
Буфер | ТипОтвета.ARRAY_BUFFER |
Это последняя глава, как видите, RetrofitJ предоставляет только платформу, и весь http-интерфейс должен быть написан вами самостоятельно.
Также вы можете написать общий интерфейс и расширить его , тогда сборщик информации будет работать следующим образом:
Короче говоря, цепочка приоритетов информации выглядит следующим образом: @Config > метод > этот класс > суперкласс.
Массачусетский технологический институт