Cartographie des demandes pour Netty

Il était une fois dans un projet très, très loin ... , j'avais besoin de faire le traitement des requêtes http sur Netty . Malheureusement, il n'y avait pas de mécanismes standard standard pour mapper les requêtes http dans Netty (et ce cadre n'est pas du tout pour cela), par conséquent, il a été décidé de mettre en œuvre notre propre mécanisme.


Si le lecteur commence à s'inquiéter du sort du projet, alors ça ne vaut pas la peine, tout va bien pour lui, car à l'avenir, il a été décidé de réécrire le service Web sur un cadre plus affiné pour les services RESTful, sans utiliser vos propres vélos. Mais les réalisations sont restées, et elles peuvent être utiles à quelqu'un, donc je voudrais les partager.

Netty est un cadre de développement d'applications réseau hautes performances. Vous pouvez en savoir plus à ce sujet sur le site Web du projet.
Netty fournit des fonctionnalités très pratiques pour créer des serveurs socket, mais à mon avis, cette fonctionnalité n'est pas très pratique pour créer des serveurs REST.


Traitement des demandes à l'aide du moteur Netty standard


Pour traiter les demandes dans Netty, vous devez hériter de la classe ChannelInboundHandlerAdapter et remplacer la méthode channelRead .


 public class HttpMappingHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { } } 

Pour obtenir les informations nécessaires au traitement des requêtes http, l'objet msg peut être HttpRequest en HttpRequest .


 HttpRequest request = (HttpRequest) msg; 

Après cela, vous pouvez obtenir toutes les informations de cette demande. Par exemple, l'URL de la demande.


 String uri = request.uri(); 

Type de demande.


 HttpMethod httpMethod = request.method(); 

Et le contenu.


 ByteBuf byteBuf = ((HttpContent) request).content(); 

Le contenu peut être, par exemple, json passé dans le corps de la requête POST . ByteBuf est une classe de la bibliothèque Netty , il est donc peu probable que les analyseurs json fonctionnent avec elle, mais il peut très facilement être converti en chaîne.


 String content = byteBuf.toString(StandardCharsets.UTF_8); 

C'est, en général, tout. En utilisant les méthodes ci-dessus, vous pouvez traiter les requêtes http. Certes, tout devra être traité au même endroit, à savoir dans la méthode channelRead . Même si nous séparons la logique de traitement des demandes en différentes méthodes et classes, vous devez toujours mapper l'URL à ces méthodes quelque part en un seul endroit.


Mappage de demande


Eh bien, comme vous pouvez le voir, il est possible d'implémenter le mappage des requêtes http en utilisant la fonctionnalité Netty standard. Certes, ce ne sera pas très pratique. Je voudrais en quelque sorte séparer complètement le traitement des requêtes http par différentes méthodes (par exemple, comme cela se fait au printemps ). A l'aide de la réflexion, une tentative a été faite pour mettre en œuvre une approche similaire. La bibliothèque num s'est avérée à partir de cela. Son code source se trouve ici .
Pour utiliser le mappage de demande à l'aide de la bibliothèque num , il suffit d'hériter de la classe AbstractHttpMappingHandler , après quoi vous pouvez créer des méthodes de gestionnaire de demande dans cette classe. La principale exigence de ces méthodes est qu'elles retournent FullHttpResponse ou ses descendants. Vous pouvez montrer par quelle requête http cette méthode sera appelée à l'aide d'annotations:


  • @Get
  • @Post
  • @Put
  • @Delete

Le nom de l'annotation indique le type de demande qui sera appelé. Quatre types de demandes sont pris en charge: GET , POST , PUT et DELETE . En tant value paramètre de value dans l'annotation, vous devez spécifier l'URL, lorsque vous y accédez, la méthode souhaitée sera appelée.


Un exemple de ce à quoi ressemble le gestionnaire de GET , qui renvoie la chaîne 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)); } } 

Paramètres de demande


Le passage des paramètres de la requête à la méthode du gestionnaire s'effectue également à l'aide d'annotations. Pour ce faire, utilisez l'une des annotations suivantes:


  • @PathParam
  • @QueryParam
  • @RequestBody

@PathParam


Pour passer les paramètres de chemin, l'annotation @PathParam est @PathParam . Lorsque vous l'utilisez comme paramètre de value de l'annotation, vous devez spécifier le nom du paramètre. De plus, le nom du paramètre doit également être spécifié dans l'URL de la demande.


Un exemple de la façon dont le gestionnaire de GET recherchera dans lequel le paramètre id path est passé et que ce paramètre renvoie.


 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


Pour passer des paramètres de requête, l'annotation @QueryParam est @QueryParam . Lorsque vous l'utilisez comme paramètre de value de l'annotation, vous devez spécifier le nom du paramètre. La liaison des paramètres peut être contrôlée à l'aide du paramètre d'annotation required .


Un exemple de ce à quoi ressemblera le gestionnaire de GET , auquel le message paramètre de requête message transmis et qui renvoie ce paramètre.


 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


Pour transmettre le corps des requêtes POST , l'annotation @RequestBody est @RequestBody . Par conséquent, il est autorisé à l'utiliser uniquement dans les requêtes POST . Il est supposé que les données au format json seront transmises en tant que corps de demande. Par conséquent, pour utiliser @RequestBody il est nécessaire de passer l'implémentation de l'interface JsonParser au constructeur de la classe de gestionnaire, qui sera engagé dans l'analyse des données du corps de la demande. De plus, la bibliothèque a déjà une implémentation par défaut de JsonParserDefault . Cette implémentation utilise jackson comme analyseur.


Un exemple de ce à quoi ressemblera un gestionnaire de requêtes POST dans lequel se trouve un corps de requête.


 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)); } } 

La classe Message est la suivante.


 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; } } 

Gestion des erreurs


Si, lors du traitement des demandes, une Exception se produit et n'est pas interceptée dans le code des méthodes du gestionnaire, la réponse avec le 500e code sera retournée. Pour écrire une logique de gestion des erreurs, il suffit de redéfinir la méthode exceptionCaught dans la classe du gestionnaire.


 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)); } } 

Conclusion


C'est, en général, tout. J'espère que cela a été intéressant et sera utile à quelqu'un.




Le code d'exemple du serveur http Netty utilisant la bibliothèque num est disponible ici .

Source: https://habr.com/ru/post/fr435864/


All Articles