Forest: jednostavan okvir za deklarativne HTTP pozive
Danas ću vam preporučiti HTTP klijentski okvir koji direktno rešava probleme, može vrlo efikasno završiti integraciju sa trećim stranama.
Pre nego što predstavim glavnog lika ovog članka, prvo da upoznamo biblioteke HTTP komponenti u Java ekosistemu, otprilike mogu se podeliti u tri kategorije:
- JDK ugrađena HttpURLConnection standardna biblioteka;
- Apache HttpComponents HttpClient;
- OkHttp.
Korišćenje HttpURLConnection za slanje HTTP zahteva ima najveću prednost što ne treba uvoditi dodatne zavisnosti, ali korišćenje je vrlo teško, nedostaju podrške za upravljanje bazena veza, kontrolu mehanizama domena i druge karakteristike.
Najveća prednost korišćenja standardne biblioteke je što ne treba uvoditi dodatne zavisnosti, ali je korišćenje prilično teško, poput direktnog korišćenja JDBC za povezivanje sa bazom podataka, potreban je mnogo šablonskog koda. Hajde da pošaljemo jednostavan HTTP POST zahtev.
public class HttpUrlConnectionDemo {
public static void main(String[] args) throws IOException {
String urlString = "https://httpbin.org/post";
String bodyString = "password=123";
URL url = new URL(urlString);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
OutputStream os = conn.getOutputStream();
os.write(bodyString.getBytes("utf-8"));
os.flush();
os.close();
if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
InputStream is = conn.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder sb = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
System.out.println("Sadržaj odgovora:" + sb.toString());
} else {
System.out.println("Šifra odgovora:" + conn.getResponseCode());
}
}
}HTTP zahtev poslat putem HttpURLConnection je prilično primitivan, u suštini plitak omotač sloja mrežnog prenosa; nakon što dobijete HttpURLConnection objekat, možete dobiti izlazni tok, zatim poslati sadržaj koji treba poslati; zatim pročitati sadržaj odgovora sa serverske strane putem ulaznog toka; konačno odštampati.
Međutim, HttpURLConnection ne podržava HTTP/2.0, da bismo rešili ovaj problem, zvanična standardna biblioteka Java 9 dodala je napredniji HttpClient, zatim slanje POST zahteva izgleda mnogo više, ne samo podržava asinhrono, već i glatko lančano pozivanje.
public class HttpClientDemo {
public static void main(String[] args) throws URISyntaxException {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(new URI("https://postman-echo.com/post"))
.headers("Content-Type", "text/plain;charset=UTF-8")
.POST(HttpRequest.BodyPublishers.ofString("Erge je kul"))
.build();
client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenAccept(System.out::println)
.join();
}
}Apache HttpComponents HttpClient takođe podržava bogate karakteristike:
- Baziran na standardnom, čistom Java jeziku, implementira HTTP1.0 i HTTP1.1;
- Implementirane sve HTTP metode u proširenom objektno-orijentisanom obliku;
- Podržava HTTPS protokol za enkripciju (HTTP preko SSL protokola);
- Izlazni tok zahteva Request može izbeći da sadržaj tijela direktno iz soket bafera na server;
- Ulazni tok odgovora Response efikasno može direktno čitati sadržaj odgovora sa soket servera.
public class HttpComponentsDemo {
public static void main(String[] args) throws IOException, IOException, ParseException {
try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
HttpPost httpPost = new HttpPost("http://httpbin.org/post");
List<NameValuePair> nvps = new ArrayList<>();
nvps.add(new BasicNameValuePair("name", "Erge"));
httpPost.setEntity(new UrlEncodedFormEntity(nvps, Charset.forName("UTF-8")));
try (CloseableHttpResponse response2 = httpclient.execute(httpPost)) {
System.out.println(response2.getCode() + " " + EntityUtils.toString(response2.getEntity()));
}
}
}
}OkHttp je HTTP klijent sa visokom efikasnošću izvršenja:
- Podržava HTTP/2.0, kada više zahteva odgovara istoj adresi hosta, može deliti isti Socket;
- Bazen veza može smanjiti kašnjenje zahteva;
- Podržava Gzip kompresiju, smanjuje veličinu podataka o mrežnom prenosu;
- Podržava keširanje podataka Response, izbjegava duplirane mrežne zahteve;
public class OkHttpPostDemo {
public static final MediaType JSON = MediaType.get("application/json; charset=utf-8");
OkHttpClient client = new OkHttpClient();
String post(String url, String json) throws IOException {
RequestBody body = RequestBody.create(json, JSON);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
}
public static void main(String[] args) throws IOException {
OkHttpPostDemo example = new OkHttpPostDemo();
String json = "{'name':'Erge'}";
String response = example.post("https://httpbin.org/post", json);
System.out.println(response);
}
}Lagani HTTP klijentski okvir Forest koji se danas predstavlja je upravo baziran na HttpClient-u i OkHttp-u, eliminisao je sve razlike koje donose različite HTTP biblioteke komponenti.
Forest u bukvalnom smislu znači "šuma", ali više intendentno može se podeliti na "For" i "Rest" dve reči, to jest "za Rest" (Rest je arhitektonski stil baziran na HTTP-u). A zajedno čini šumu, šumu čini mnogo drveća i cvetaća (može se razumeti kao različite usluge), koje na površini izgledaju nezavisno, ali se podzemlju koreone mešusobno povezuju i ovise o sebi, ovako izgleda malo modernog distribuiranog servisnog stila. Konačno, ove dve reči čitaju unazad kao "Resultful".
Adresa projekta:
Iako broj zvezdica još uvek nije mnogo, ali trend broja zvezdica se penje, možete ga koristiti kao projekat za vežbanje, mislim da je i dalje dobar izbor.

Forest je u suštini okvir za obradu prednje strane, dalja omotačina klijentskog HTTP API okvira.

Prednji deo:
- Slanje HTTP zahteva na RPC način, pogodno za dekupliranje
- Podržava GET, HEAD, POST i sve druge metode zahteva
- Podržava Spring i Springboot integraciju
- Automatska analiza JSON stringova u Java objekte
- Automatska analiza XML teksta u Java objekte
- Podržava fleksibilne šablonske izraze
- Podržava interseptore za obradu različitih faza životnog ciklusa zahteva
- Podržava prilagođene napomene
Zadnji deo:
- Podržava OkHttp
- Podržava Httpclient
Forest je lako za početi, ne treba pozivati HTTP niže-level interfejse, poput RPC okvira poput Dubbo-a, samo treba definisati interfejs, pozvati interfejs. Za nekoliko minuta možete završiti definisanje, slanje, prijem odgovora, analizu podataka, obradu grešaka, štampanje dnevnika i druge procese.
Konfiguracija je lagana, sledi princip "konvencija preko konfiguracije", samo treba konfigurisati kada je potrebno, ako se ne konfiguriše neće uticati na normalno pozivanje Forest zahteva.
Jednostavan i elegentan, detalje HTTP zahteva omotava u obliku Java interfejsa + napomene, ne treba brinuti o specifičnom procesu slanja HTTP zahteva. Time što informacije HTTP zahteva i poslovni kod dekuplira, olakšava upravljanje velikim brojem HTTP URL-ova, zaglavlja, tela i drugih informacija.
Proširenje je fleksibilno, dozvoljava prilagođene interseptore, čak i prilagođene napomene, time se proširuju Forest-ove mogućnosti.
Forest ne zahteva da pišete specifičan proces HTTP pozivanja, samo treba definisati interfejs, a zatim putem Forest napomena dodati informacije HTTP zahteva u metode interfejsa. Strana koja šalje zahtev može automatski slati zahteve i primati odgovore na zahteve pozivanjem definisanog interfejsa.
Forest može to da uradi zato što kroz dinamički proxy generiše konkretnu implementaciju prethodno definisanog interfejsa, zatim organizuje, verifikuje informacije HTTP zahteva, veže dinamičke podatke, pretvara oblike podataka, SSL verifikacioni potpis, poziva niže-level HTTP API da izvrši stvarni zahtev, čeka odgovor, neuspeli pokušaj ponavljanja, pretvara podatke odgovora u Java tipove i druge prljave poslove sve radi implementacija dinamičkog proxyja.
Neću više govoriti suviše, krenimo direktno u praksu.
Prvi korak, dodajte Maven zavisnost.
<dependency>
<groupId>com.dtflys.forest</groupId>
<artifactId>forest-core</artifactId>
<version>1.5.1</version>
</dependency>Drugi korak, izgradite HTTP zahtev.
U Forest-u, sve informacije HTTP zahteva moraju biti vezane za metodu određenog interfejsa, ne treba pisati specifičan kod za slanje zahteva. Strana koja šalje zahtev poziva metodu interfejsa koja je prethodno definisala informacije HTTP zahteva.
public interface ForRestClient {
@Request(
url = "http://httpbin.org/post",
type = "POST"
)
String simplePost(@Body("name") String name);
}Kroz @Post napomenu, vezuje metodu simplePost() u ForRestClient interfejsu za HTTP zahtev, koristeći POST način, možete koristiti @Body napomenu za modifikaciju parametara da biste vezali podatke parametara za telo HTTP zahteva. Zatim vraća podatke odgovora kao String pozivaocu.
Treći korak, pozovite interfejs.
public class ForRestDemo {
public static void main(String[] args) {
// Instancirajte Forest konfiguracioni objekat
ForestConfiguration configuration = ForestConfiguration.configuration();
configuration.setBackendName("httpclient");
// Kroz Forest konfiguracioni objekat instancirajte Forest zahtevni interfejs
ForRestClient myClient = configuration.createInstance(ForRestClient.class);
// Pozovite Forest zahtevni interfejs i dobijte rezultat odgovora
String result = myClient.simplePost("Erge");
System.out.println(result);
}
}ForestConfiguration je globalna konfiguraciona klasa Forest-a, sve globalne osnovne konfiguracije Forest-a ovlašćenja ovom klasom se upravljaju.
Možete videti informacije o dnevniku nakon pokretanja:
{
"args": {},
"data": "",
"files": {},
"form": {
"name": "Erge"
},
"headers": {
"Content-Length": "23",
"Content-Type": "application/x-www-form-urlencoded",
"Host": "httpbin.org",
"User-Agent": "Apache-HttpClient/4.5.2 (Java/11.0.8)",
"X-Amzn-Trace-Id": "Root=1-60b533aa-58b41e4967803d99593c53a0"
},
"json": null,
"origin": "161.81.21.32",
"url": "http://httpbin.org/post"
}U ovom trenutku, jednostavan primer Forest upotrebe je prošao.
Ako je u pitanju Spring Boot projekat, tada ne treba ForestConfiguration, samo treba dodati @ForestScan napomenu na startnoj klasi ili konfiguracionoj klasi.
@SpringBootApplication
@Configuration
@ForestScan(basePackages = "com.yoursite.client")
public class MyApp {
...
}Forest pored GET i POST podržava i druge HTTP metode zahteva, kao što su PUT, HEAD, OPTIONS, DELETE. Pri izgradnji interfejsa samo treba koristiti odgovarajuću napomenu, na primer PUT:
// PUT zahtev
@Put("http://localhost:8080/hello")
String simplePut();U metodama PUT i POST zahteva obično se koristi telo HTTP zahteva za prenos podataka, u Forest-u možete koristiti @Body, @JSONBody, @XMLBody i druge načine za postavljanje podataka tela.
/**
* Direktno modifikujte JSON string
*/
@Post("http://localhost:8080/hello/user")
String helloUser(@JSONBody String userJson);Forest zahtev automatski će deserijalizovati vraćene podatke odgovora u odgovarajući tip podataka, u dva koraka.
Prvi korak: definišite dataType svojstvo
dataType svojstvo određuje tip vraćenih podataka odgovora na ovaj zahtev, opcioni tipovi podataka su tri: text, json, xml, podrazumevano je text.
/**
* Kada je dataType json ili xml, Forest će vršiti odgovarajuću deserijalizaciju
*/
@Request(
url = "http://localhost:8080/text/data",
dataType = "json"
)
Map getData();Drugi korak: odredite ciljni tip deserijalizacije
Deserijalizacija zahteva ciljni tip, a taj tip je u stanci tip povratne vrednosti metoda, ako je povratna vrednost String deserijaliziraće se u String string, ako je povratna vrednost Map deserijaliziraće se u HashMap objekat, takođe može biti određen kao prilagođena Class tip.
Ako postoji takva User klasa:
public class User {
private String username;
private String score;
// Setter i Getter ...
}Vraćeni podaci su JSON string:
{"username": "Foo", "score": "82"}Tada interfejs zahteva treba definisati ovako:
/**
* dataType svojstvo označava da je vraćeni tip podataka JSON
*/
@Get(
url = "http://localhost:8080/user?id=${0}",
dataType = "json"
)
User getUser(Integer id)Dodatno, treba da razumete Gzip, to je trenutno popularan algoritam za kompresiju datoteka, ima vrlo široku primenu. Posebno kada Gzip koristi za kompresiju tekstualnih datoteka efekat je naročito očit, može smanjiti veličinu datoteke za preko 70%. Mnogo HTTP servera podržava Gzip, na primer Tomcat, podaci kompresovani kroz ove servere mogu smanjiti protok mrežnog prenosa, povećati brzinu odgovora klijenta.
Forest počevši od verzije 1.5.2-BETA podržava Gzip dekompresiju, način dekompresije je vrlo jednostavan, samo dodajte @DecompressGzip napomenu na metod ili interfejs klasu.
/**
* Dodajte mogućnost Gzip dekompresije metodu zahteva
*/
@Get("/transaction")
@DecompressGzip
String transaction(String infno);Još važnija tačka je što Forest može ostvariti asinhroni zahtev postavljanjem async svojstva @Request napomene na true.
@Request(
url = "http://localhost:8080/hello/user?username=${0}",
async = true,
headers = {"Accept:text/plain"}
)
void asyncGet(String username, OnSuccess<String> onSuccess);Prilikom asinhronog zahteva, kroz OnSuccess<T> povratnu funkciju se prima odgovarajući podatak, a ne kroz povratnu vrednost metoda interfejsa, stoga se tip povratne vrednosti obično definiše kao void.
Prilikom pozivanja metoda ovog interfejsa, možete koristiti sledeći način:
myClient.send("foo", (String resText, ForestRequest request, ForestResponse response) -> {
// Povratni poziv uspešnog odgovora
System.out.println(resText);
},
(ForestRuntimeException ex, ForestRequest request, ForestResponse response) -> {
// Povratni poziv izuzetka
System.out.println(ex.getMessage());
});Pored gore navedenih funkcionalnosti, Forest podržava i naprednije načine korišćenja:
- HTTPS
- Otpremanje i preuzimanje datoteka
- Interseptori
- Korišćenje proksija
- Prilagođene napomene
Možete pogledati zvaničnu dokumentaciju Forest-a, zatim prakticirati lokalno, i dalje možete naučiti mnogo znanja, posebno HTTPS i otpremanje i preuzimanje datoteka, samo uz jednostavnu konfiguraciju možete završiti, lično mislim da je vredno za učenje i referencu.
Duh otvorenog koda je dragocen, dobar otvoreni kod potreban je doprinos i podrška svih. Nadam se da će ovaj članak doneti novi izbor pri izboru HTTP klijentskog okvira, tačno, to je Forest.
Ovaj članak nije samo predstavio ovaj lagan HTTP klijentski okvir Forest, već je pregledao i njegovu niže-level implementaciju: HttpClient i OkHttp, nadam se da će vam pomoći.
