ไคลเอ็นต์ HTTP ที่ประกาศตาม Axios สำหรับเบราว์เซอร์และ node.js
เขียนโดยโปรแกรมเมอร์ Java ที่เคยชินกับการประกาศไคลเอ็นต์ HTTP
เป้าหมายคือการจัดหาไคลเอนต์ http ที่เรียบง่ายและกระชับในสภาพแวดล้อม javascript/typescript ทั้งหมด (สภาพแวดล้อม es6 จริง ๆ แล้ว) ( ยัง! )
เนื่องจาก babel-plugin-transform-decorators-legacy
ยังไม่รองรับตัวตกแต่งพารามิเตอร์ ดังนั้น RetrofitJs จึงทำงานบนสภาพแวดล้อม TypeScript เท่านั้นในตอนนี้
อ้างข้อเสนอที่นี่:
คุณสามารถตกแต่งทั้งคลาสได้ เช่นเดียวกับการประกาศฟิลด์ getters ตัวตั้งค่า และเมธอด อาร์กิวเมนต์และการประกาศฟังก์ชันไม่สามารถตกแต่งได้ -- จากผู้ตกแต่งข้อเสนอ
และมีคนยื่นคำขอนี้ -> ตัวตกแต่งพารามิเตอร์ด้วย
ตอนนี้ให้เรามุ่งเน้นไปที่การประกาศอินเทอร์เฟซ http ของตัวเองมากกว่ารายละเอียด http เช่นเดียวกับที่ชุดติดตั้งเพิ่มทำ เขียนให้น้อยลง ทำอะไรให้มากขึ้น
สุดท้ายนี้ ขอขอบคุณทุกคนที่เขียนหรืออัปเดต Axios ( javascript http client ) และ Retrofit ( java http client ) เป็นอย่างมาก สิ่งเหล่านี้เป็นโครงการที่ยอดเยี่ยม มันสอนผมเยอะมาก
โปรแกรมนี้ไม่สามารถทำงานบน IE8/9/10/11 และสภาพแวดล้อมอื่นๆ (โดยปกติคือเบราว์เซอร์) ที่ไม่รองรับ es6 ในภาษาเนทีฟ เนื่องจากขึ้นอยู่กับวัตถุ Proxy
(es6) และคุณสมบัติ Decorator
(stage 2)
กล่าวอีกนัยหนึ่ง RetrofitJs สามารถทำงานบนสภาพแวดล้อมใดก็ได้ที่รองรับ es6 ในภาษาเนทีฟ
เกี่ยวกับวัตถุ Proxy
ฉันไม่พบ polyFill ใด ๆ ที่จะรองรับ แต่สำหรับ Decorator
คุณสามารถใช้มันโดยสิ่งที่ babel support เช่น babel-plugin-transform-decorators-legacy
ใช้ npm:
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 ( ) ;
เช่นเดียวกับชุดติดตั้งเพิ่ม RetrofitJs ยังใช้งานโดย interceptor chain Interceptor ทั้งหมดเรียงลำดับตามฟิลด์ 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)
คุณสามารถใช้งาน Interceptor ของคุณเองได้อย่างง่ายดายเช่นนี้
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
คำขอปัจจุบันจะถูกโอนไปยัง interceptor ถัดไป มิฉะนั้นเครื่องสกัดกั้นที่เหลือจะไม่ทำงาน และกระบวนการทั้งหมดจะสิ้นสุดลงและตัวจัดการข้อผิดพลาดที่ใช้งานอยู่หากมีข้อผิดพลาดเกิดขึ้นจากห่วงโซ่ดัก
ในความเป็นจริง ปลั๊กอินเป็นตัวดัก คุณสามารถเขียนปลั๊กอินของคุณเองสำหรับ RetrofitJs ได้
Retrofit.use( new MyInterceptor implements Interceptor {
public order: number = 20;
public init( config: RetrofitConfig ): void {
}
public intercept( chain: Chain ): Promise<ResponseInterface<any>> {
}
} );
มันจะเพิ่มตัวสกัดกั้นทั้งหมดไปยัง Retrofit
เมื่อคุณสร้างอินสแตนซ์
ไม่มีการจำกัดฟิลด์ลำดับของปลั๊กอิน ในความเป็นจริง ตัวเลขถูกสงวนไว้สำหรับปลั๊กอินและตัวดักเริ่มต้น
และนี่คือตัวดักเริ่มต้นทั้งหมด:
เครื่องสกัดกั้น | คำสั่ง |
---|---|
เรียลคอล | 0 |
ลองอีกครั้งRequestInterceptor | 1 |
LoggerInterceptor | 5 |
Interceptor นี้ถูกปิดใช้งานตามค่าเริ่มต้น โดยจะลองอีกครั้งตามเวลาสุ่มเมื่อคำขอ idempotent มีความผิดปกติของเครือข่าย เช่นการหมดเวลาการเชื่อมต่อเมื่อคุณส่งคำขอ 'GET'
หากคุณต้องการใช้ คุณควรตั้ง maxTry
และ timeout
ใน RetrofitConfig
คุณสามารถตั้งค่าใน RetrofitConfig
:
{
"maxTry": "number",
"retryCondition": "RetryCondition"
}
RetryCondition
เป็นอินเทอร์เฟซ interceptor จะพยายามส่งคำขออีกครั้งหาก RetryCondition.handler
ส่งคืนค่าจริง
Interceptor นี้ปิดใช้งานตามค่าเริ่มต้น โดยจะบันทึกคำขอ / การตอบสนองทั้งหมด (หรือข้อผิดพลาด) และพิมพ์บนคอนโซล
คุณสามารถตั้งค่าใน RetrofitConfig
:
{
"debug": "boolean"
}
คุณสามารถยกเลิกคำขอได้อย่างง่ายดาย
let tick = testingClient . demo1 ( ... args ) ;
// Careful the different between micro task and macro task
setTimeout ( ( ) => tick . cancel ( "your message" ) , 3000 ) ;
ระวัง cancel
api เป็นวิธีการของวัตถุ RetrofitPromise
คุณไม่สามารถเรียกด้วยวัตถุสัญญาอื่น ๆ นี่คือตัวอย่าง:
// It is wrong.
tick . then ( ( ) => { ... you code } ) . cancel ( "message" ) ;
เมื่อคุณยกเลิกคำขอ มันจะถูกโยน a RequestCancelException
คุณสามารถจัดการหรือละเว้นในตัวจัดการข้อผิดพลาดได้
หากคุณต้องการจัดการข้อยกเว้นทั้งหมดแบบเดียวกัน เพียงใช้ ErrorHandler
class MyErrorHandler implement ErrorHandler {
public handler ( realReason : any , exception : Exception ) : void {
// your code
}
}
realReason
เป็นพารามิเตอร์ที่กำหนดโดย axios และ exception
คืออินสแตนซ์ของ Exception
คุณสามารถประมวลผลข้อยกเว้นทั้งหมดแบบสม่ำเสมอได้อย่างง่ายดาย
นี่คือข้อยกเว้นทั้งหมด:
ข้อยกเว้น | คำอธิบาย |
---|---|
ขอยกเลิกข้อยกเว้น | การยกเลิกผู้ใช้ |
ConnectException | สัญญาณ ECONNREFUSED เปิดใช้งานแล้ว |
SocketException | สัญญาณ ECONNRESET เปิดใช้งานแล้ว |
คำขอหมดเวลาข้อยกเว้น | เปิดใช้งานสัญญาณ 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 {
}
มันจะแทนที่การตั้งค่ามัณฑนากร (แต่ไม่รวมการกำหนดค่าส่วนกลาง) โดยฟิลด์ที่มีการกำหนดค่าพารามิเตอร์
การกำหนดค่าแบบไดนามิกจะมีผลเฉพาะในคำขอเท่านั้น แต่จะไม่มีผลกับการกำหนดค่า Interceptor เนื่องจาก Interceptor จะเริ่มทำงานเมื่อคุณเรียก 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
จะบอก RetrofitJs ว่าควรส่งคืนประเภทใด
นี่คือประเภทคำตอบที่รองรับทั้งหมด:
พิมพ์ | ค่า |
---|---|
วัตถุ | ResponseType.JSON ( ค่าเริ่มต้น ) |
เชือก | ResponseType.DOCUMENT, ResponseType.ข้อความ |
ลำธาร | ResponseType.สตรีม |
บัฟเฟอร์ | ResponseType.ARRAY_BUFFER |
นี่คือบทสุดท้ายอย่างที่คุณเห็น RetrofitJs มีเพียงแพลตฟอร์มเท่านั้น และอินเทอร์เฟซ http ทั้งหมดจะต้องเขียนด้วยตัวเอง
นอกจากนี้คุณยังสามารถ เขียนอินเทอร์เฟซทั่วไปและขยายได้ จากนั้นตัวรวบรวมข้อมูลจะทำงานดังนี้:
กล่าวโดยสรุป ห่วงโซ่ลำดับความสำคัญของข้อมูลเป็นไปตามนี้: @Config > method > this class > super class
เอ็มไอที