Sekali waktu dalam satu jauh, jauh ... proyek, saya perlu melakukan pemrosesan permintaan http di Netty . Sayangnya, tidak ada mekanisme standar yang nyaman untuk memetakan permintaan http di Netty (dan kerangka kerja ini sama sekali tidak untuk itu), oleh karena itu, diputuskan untuk menerapkan mekanisme kami sendiri.
Jika pembaca mulai khawatir tentang nasib proyek, maka itu tidak layak, semuanya baik-baik saja dengannya, karena di masa depan, diputuskan untuk menulis ulang layanan web pada kerangka kerja yang lebih tajam untuk layanan tenang, tanpa menggunakan sepeda Anda sendiri. Tetapi pencapaian itu tetap ada, dan itu bisa bermanfaat bagi seseorang, jadi saya ingin membagikannya.
Netty adalah kerangka kerja untuk mengembangkan aplikasi jaringan berkinerja tinggi. Anda dapat membaca lebih lanjut tentang itu di situs web proyek.
Netty menyediakan fungsionalitas yang sangat nyaman untuk membuat server socket, tetapi menurut saya, fungsionalitas ini tidak terlalu nyaman untuk membuat server REST.
Meminta pemrosesan menggunakan mesin Netty standar
Untuk memproses permintaan di Netty, Anda perlu mewarisi dari kelas ChannelInboundHandlerAdapter
dan mengganti metode channelRead
.
public class HttpMappingHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { } }
Untuk mendapatkan informasi yang diperlukan untuk memproses permintaan http, objek msg
dapat dilemparkan ke HttpRequest
.
HttpRequest request = (HttpRequest) msg;
Setelah itu, Anda dapat memperoleh informasi apa pun dari permintaan ini. Misalnya, URL permintaan.
String uri = request.uri();
Jenis permintaan.
HttpMethod httpMethod = request.method();
Dan isinya.
ByteBuf byteBuf = ((HttpContent) request).content();
Konten tersebut mungkin, misalnya, json diteruskan dalam isi permintaan POST
. ByteBuf
adalah kelas dari perpustakaan Netty , jadi parser json tidak mungkin bekerja dengannya, tetapi ia dapat dengan mudah dilemparkan ke string.
String content = byteBuf.toString(StandardCharsets.UTF_8);
Itu, secara umum, itu saja. Dengan menggunakan metode di atas, Anda dapat memproses permintaan http. Benar, semuanya harus diproses di satu tempat, yaitu dalam metode channelRead
. Bahkan jika kami memisahkan logika pemrosesan permintaan ke berbagai metode dan kelas, Anda masih harus memetakan URL ke metode ini di satu tempat di satu tempat.
Meminta Pemetaan
Yah, seperti yang Anda lihat, adalah mungkin untuk mengimplementasikan pemetaan permintaan http menggunakan fungsionalitas Netty standar. Benar, itu tidak akan nyaman. Saya ingin memisahkan pemrosesan http-request dengan metode yang berbeda (misalnya, seperti yang dilakukan pada musim semi ). Dengan bantuan refleksi, upaya dilakukan untuk menerapkan pendekatan serupa. Perpustakaan num ternyata dari ini. Kode sumbernya dapat ditemukan di sini .
Untuk menggunakan pemetaan kueri menggunakan pustaka num , cukup mewarisi dari kelas AbstractHttpMappingHandler
, setelah itu Anda bisa membuat metode penangan permintaan di kelas ini. Persyaratan utama untuk metode ini adalah mereka mengembalikan FullHttpResponse
atau turunannya. Anda dapat menunjukkan dengan http apa permintaan metode ini akan dipanggil menggunakan anotasi:
Nama anotasi menunjukkan jenis permintaan apa yang akan dipanggil. Empat jenis permintaan didukung: GET
, POST
, PUT
dan DELETE
. Sebagai parameter value
dalam anotasi, Anda harus menentukan URL, ketika diakses, metode yang diinginkan akan dipanggil.
Contoh bagaimana penangan GET
terlihat, yang mengembalikan string Hello, world!
.
public class HelloHttpHandler extends AbstractHttpMappingHandler { @Get("/test/get") public DefaultFullHttpResponse test() { return new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, OK, Unpooled.copiedBuffer("Hello, world!", StandardCharsets.UTF_8)); } }
Parameter Permintaan
Melewati parameter dari permintaan ke metode handler juga dilakukan menggunakan anotasi. Untuk melakukan ini, gunakan salah satu anotasi berikut:
@PathParam
@QueryParam
@RequestBody
@PathParam
Untuk melewati parameter jalur, penjelasan @PathParam
. Saat menggunakannya sebagai parameter nilai anotasi, Anda harus menentukan nama parameter. Selain itu, nama parameter juga harus ditentukan dalam URL permintaan.
Contoh bagaimana penangan GET
akan melihat ke mana parameter jalur id
dilewati dan parameter ini kembali.
public class HelloHttpHandler extends AbstractHttpMappingHandler { @Get("/test/get/{id}") public DefaultFullHttpResponse test(@PathParam(value = "id") int id) { return new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, OK, Unpooled.copiedBuffer(id, StandardCharsets.UTF_8)); } }
@QueryParam
Untuk melewati parameter kueri, penjelasan @QueryParam
. Saat menggunakannya sebagai parameter nilai anotasi, Anda harus menentukan nama parameter. Penjilidan parameter dapat dikontrol menggunakan parameter anotasi yang required
.
Contoh bagaimana penangan GET
akan terlihat, ke mana message
parameter kueri diteruskan dan yang mengembalikan parameter ini.
public class HelloHttpHandler extends AbstractHttpMappingHandler { @Get("/test/get") public DefaultFullHttpResponse test(@QueryParam(value = "message") String message) { return new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, OK, Unpooled.copiedBuffer(message, StandardCharsets.UTF_8)); } }
@RequestBody
Untuk mengirimkan isi permintaan POST
, anotasi @RequestBody
. Karena itu, hanya diperbolehkan menggunakannya dalam permintaan POST
. Diasumsikan bahwa data format json akan dikirim sebagai badan permintaan. Oleh karena itu, untuk menggunakan @RequestBody
perlu untuk meneruskan implementasi antarmuka JsonParser
ke konstruktor kelas handler, yang akan terlibat dalam JsonParser
parsing data dari badan permintaan. Juga, perpustakaan sudah memiliki implementasi standar JsonParserDefault
. Implementasi ini menggunakan jackson
sebagai parser.
Contoh bagaimana penangan permintaan POST
akan terlihat di mana ada badan permintaan.
public class HelloHttpHandler extends AbstractHttpMappingHandler { @Post("/test/post") public DefaultFullHttpResponse test(@RequestBody Message message) { return new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, OK, Unpooled.copiedBuffer("{id: '" + message.getId() +"', msg: '" + message.getMessage() + "'}", StandardCharsets.UTF_8)); } }
Kelas Message
adalah sebagai berikut.
public class Message { private int id; private String message; public Message() { } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }
Menangani kesalahan
Jika selama pemrosesan permintaan Exception
terjadi dan tidak dicegat dalam kode metode penangan, maka jawaban dengan kode ke-500 akan dikembalikan. Untuk menulis beberapa logika untuk penanganan kesalahan, itu sudah cukup untuk mendefinisikan kembali metode exceptionCaught
di kelas handler.
public class HelloHttpHandler extends AbstractHttpMappingHandler { @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { super.exceptionCaught(ctx, cause); ctx.writeAndFlush(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.INTERNAL_SERVER_ERROR)); } }
Kesimpulan
Itu, secara umum, itu saja. Saya harap ini menarik dan akan bermanfaat bagi seseorang.
Kode contoh server Netty http menggunakan perpustakaan num tersedia di sini .