HttpClient
Countless services rely on the HTTP-based communication nowadays, and it is a very common application scenario that web applications call back-end HTTP services.
The framework built in HttpClient based on urllib, you can quickly complete any HTTP request.
# Using HttpClient by app
HttpClient will initialize to app.httpclient
automatically during the application's initialization.
Also added an method app.curl(url, options)
, which is equivalent to the app.httpclient.request(url, options)
.
So you can easily use app.curl
to complete a HTTP request.
// app.js |
# Using HttpClient by Context
Framework also provides ctx.curl(url, options)
and ctx.httpclient
in Context, same as app.
So it's very easy to use ctx.curl()
to complete a HTTP request in the Context (such as in the controller)
// app/controller/npm.js |
# Basic HTTP Request
HTTP has been widely used and have several methods to make request, but the methods are similar. We start with the basic four request methods then move to some more complex scenario.
In the following example, we will complete the request of https://httpbin.org in the controller.
# GET
Reading data almost uses GET request. It is the most common type and widely used in the world of HTTP. And it is also easier to construct a request parameter.
// app/controller/npm.js |
- GET request might not need to set
options.method
. HttpClient Defalut method is set toGET
- Return
result
will contains 3 attributes:status
,headers
anddata
status
: response status,for example200
,302
,404
,500
and etcheaders
: response header,similar to{ 'content-type': 'text/html', ... }
data
: response body,default HttpClient doesn't do anything and returns as Buffer directly. Once theoptions.dataType
is set,HttpClient will process thedata
based on the parameters
For the complete request parameter options
and return value result
, refer to below section options Parameters in Detail
# POST
The scenario of creating data generally uses the POST request with body parameter, one more parameter compared to GET.
Take sending JSON boy as example:
// app/controller/npm.js |
The following will explain POST to achieve Form function of form submission and file upload in detail.
# PUT
Similar to POST, but PUT is better for data updating and replacement. Almost the same parameters as POST except setting method as PUT
.
// app/controller/npm.js |
# DELETE
DELETE request is to delete the data, request body don't need to add request body but HttpClient don't have the limitation.
// app/controller/npm.js |
# Advanced HTTP request
In some real application scenarios, still have some more complex HTTP requests.
# Form Submission
Interfaces of Browser-Oriented Form Submission (without files), usually require content-type: application/x-www-form-urlencoded
for the data requesting.
// app/controller/npm.js |
# Uploading Files by Multipart
Once form submission contains files, submission of requesting data must be multipart/form-data
We need to introduce third party module formstream to generate form
objects that can be consumed by HttpClient.
// app/controller/npm.js |
Of course, you can add more files to achieve the requirements of upload multiple files at one time by form.file()
form.file('file1', file1); |
# Uploading files in Stream Mode
In fact, Stream is the leading in the world of Node.js.
If the server supports streaming, the most friendly way is to send the Stream directly. Actually, Stream will be sent in Transfer-Encoding: chunked
transmission coding format, which is implemented by HTTP module automatically.
// app/controller/npm.js |
# options Parameters in Detail
Due to the complexity of HTTP Request, the options parameters of httpclient.request(url, options)
quite large. The actual usage of each optional parameter will be shown with descriptions and coding as below.
# Default HttpClient Global Configuration
// config/config.default.js |
Application can overrides the configuration by config/config.default.js
# data: Object
The request data will select the correct processing method automatically based on the method
.
- GET,HEAD: processed by
querystring.stringify(data)
then append to the query parameters of url. - POST,PUT, DELETE and etc: further judgments and process according to
contentType
.contentType = json
: processed byJSON.stringify(data)
and set it as body before sending.- others: processed by
querystring.stringify(data)
and set it as body before sending
// GET + data |
# dataAsQueryString: Boolean
Once dataAsQueryString=true
is set, even under POST, it will forces options.data
to be processed by querystring.stringify
then append to the url
query parameters
The application scenarios that sending data using stream
and pass additional request parameters by url
query can be well resolved.
ctx.curl(url, { |
# content: String|Buffer
Set request Context, if the parameter is set, it will ignore the data
parameters
ctx.curl(url, { |
# stream: ReadStream
Set request context's readable stream, default null
.
If the parameter is set , HttpClient will ignore data
and content
ctx.curl(url, { |
# writeStream: WriteStream
Set receive response data's writeable stream, default null
.
Once the parameter is set, response result.data
is set to null
because all data are written to writeStream
.
ctx.curl(url, { |
# consumeWriteStream: Boolean
Whether to wait for writeStream
completely finished as the response well received
This parameter is not recommended to modify the default value, unless we know it's side effect are acceptable. Otherwise, the writeStream
data is likely to be incomplete.
# method: String
Set request method, default GET
. Support all GET、POST、PUT、DELETE、PATCH
and so on all HTTP methods。
# contentType: String
Set request data format ,default undefined
,HttpClient will sets automatically based on the data
and content
parameters.
When data
is object, the default setting would be form
. Support json
format.
If need to send data
by JSON
ctx.curl(url, { |
# dataType: String
Set the response data format, default return the raw buffer formatted data without processing. Support text
and json
Note: If json
is set,a JSONResponseFormatError
error would be thrown if fails to parse the response data.
const jsonResult = await ctx.curl(url, { |
# fixJSONCtlChars: Boolean
Whether filter the special control characters in the response data (U+0000 ~ U+001F),default false
Typically, the JSON data returned by some CGI system might contains such special control characters, which can be filter automatically by setting the parameters.
ctx.curl(url, { |
# headers: Object
Custom request headers
ctx.curl(url, { |
# timeout: Number|Array
Timeout of request, default [ 5000, 5000 ]
, timeout of connection creation is 5s, and the timeout of receive response is 5s.
ctx.curl(url, { |
# agent: HttpAgent
Allows to override the default HttpAgent through this parameter. If you don't want to enable KeepAlive, set this parameter to false
.
ctx.curl(url, { |
# httpsAgent: HttpsAgent
Allows to override the default HttpsAgent through this parameter. If you don't want to enable KeepAlive, set this parameter to false
.
ctx.curl(url, { |
# auth: String
Parameter of Simple login authorization (Basic Authentication), will send the login information to the Authorization
request in clear form.
ctx.curl(url, { |
# digestAuth: String
Parameter of the Digest Authentication. If the parameter is set, it will attempt to generate the Authorization
request header for the 401 response automatically then try requesting for authorization once.
ctx.curl(url, { |
# followRedirect: Boolean
Whether to follow 3xx redirect response, default false
ctx.curl(url, { |
# maxRedirects: Number
Set the maximum number of automatic redirects to prevent the endless redirect loop, default 10 times.
The parameter should not be set too large and only works in the followRedirect=true
ctx.curl(url, { |
# formatRedirectUrl: Function(from, to)
formatRedirectUrl
allow us to customize the implementation of 302、301 other redirect URL splicing, default url.resolve (from, to)
.
ctx.curl(url, { |
# beforeRequest: Function(options)
HttpClient will attempt to invoke the beforeRequest
hook before requesting officially, allowing us to make the last modification of the request parameter here.
ctx.curl(url, { |
# streaming: Boolean
Whether to return the response stream directly, default false
After enable streaming, HttpClient will return immediately after getting the response object res,
At this moment result.headers
and result.status
can be read, but still cannot read the data
const result = await ctx.curl(url, { |
if res is not passed to body directly, then we must consume this stream and do well in error handling.
# gzip: Boolean
Whether to support gzip response format, default false
After enable gzip, HttpClient will set Accept-Encoding: gzip
header and extract the data with Content-Encoding: gzip
response header automatically.
ctx.curl(url, { |
# timing: Boolean
Whether to enable the time measurement for each phase, default false
After enable the timing, you can get the time measurements of HTTP request (in milliseconds) from the result.res.timing
.
Through these measurements, we can easily locate the slowest environment in the request, similar to the Chrome network timing.
Measurement timing's analysis of each stage:
- queuing: allocating socket time consuming
- dnslookup: DNS queries time consuming
- connected: socket three handshake success time consuming
- requestSent: requesting full data time consuming
- waiting: first byte to received response time consuming
- contentDownload: full response data time consuming
const result = await ctx.curl(url, { |
# ca,rejectUnauthorized,pfx,key,cert,passphrase,ciphers,secureProtocol
These are parameters are passed to the HTTPS modules,details refer to https.request(options, callback)
。
# Debugging Aid
Framework provides egg-development-proxyagent plugin to help developers to debug.
Install and enable pulgin:
$ npm i egg-development-proxyagent --save |
// config/plugin.js |
Open capture tools, we can use charles or fiddler, here we take to anyproxy demonstrate
$ npm install anyproxy -g |
Starting application using environment variables:
$ http_proxy=http://127.0.0.1:8888 npm run dev |
Then it works correctly, and all requests that go through HttpClient can be viewed in the consle of http://localhost:8002.
Note: the pulgin only start in local environments by defalut
# Known issues
# Connection Timeout
- Exception:
ConnectionTimeoutError
- Scene: usually occurred by the DNS query is slow, or the network is slow between the client and server
- Troubleshooting Suggestion: increase the
timeout
parameter appropriately.
# Service Response Timeout
- Exception:
ResponseTimeoutError
- Scene: usually occurred by network is slower between the client and server, and happens when the data is relatively large.
- Troubleshooting Suggestion: increase the
timeout
parameter appropriately.
# Service Disconnect
- Exception:
ResponseError, code: ECONNRESET
- Scene: usually the server actively disconnects the socket connection, causing the HTTP request link exceptions.
- Troubleshooting Suggestion: please check if server has network exception at that time
# Service is unreachable
- Exception:
RequestError, code: ECONNREFUSED, status: -1
- Scene: usually because the requested URL which attached IP or the port cannot connect successfully.
- Troubleshooting Suggestion: make sure the IP or port is set correctly
# Domain name is not existing
- Exception:
RequestError, code: ENOTFOUND, status: -1
- Scene: usually the domain name requested by URL cannot be resolved by DNS successfully.
- Troubleshooting Suggestion: make sure the domain name exists, and also check to see if the DNS service is properly configured.
# JSON Response data format error
- Exception:
JSONResponseFormatError
- scene: the
dataType=json
is set and this exception is thrown in response data that does not match JSON format. - Troubleshooting Suggestion: make sure that the server no matter what situations are returns the data in JSON format correctly.
# Global request
and response
events
In enterprise application scenarios, generally a unified tracer log is needed.
To facilitate monitoring HttpClient requests and responses on the app level, we agreed on global request
and response
to expose these two events.
init options |
# request
event occurs before the network operation
A request
event is triggered before the request is sent, allowing blocking of the request.
app.httpclient.on('request', req => { |
# response
event occurs after the end of network operation
After the end of request, a response
event is triggered, so that the external event can be subscribed to the log printing.
app.httpclient.on('response', result => { |
# Example
Full examples can be found on eggjs/exmaples/httpclient .