recherche
Maisondéveloppement back-endGolangUne plongée approfondie dans le gin : le cadre de référence de Golang

A Deep Dive into Gin: Golang

Introduction

A Deep Dive into Gin: Golang

Gin est un framework web HTTP écrit en Go (Golang). Il dispose d'une API de type Martini, mais avec des performances jusqu'à 40 fois plus rapides que Martini. Si vous avez besoin de performances époustouflantes, procurez-vous du Gin.

Le site officiel de Gin se présente comme un framework web avec des « hautes performances » et une « bonne productivité ». Il mentionne également deux autres bibliothèques. Le premier est Martini, qui est également un framework Web et porte le nom d'une liqueur. Gin dit utiliser son API, mais est 40 fois plus rapide. L'utilisation de httprouter est une raison importante pour laquelle il peut être 40 fois plus rapide que Martini.
Parmi les « Fonctionnalités » du site officiel, huit fonctionnalités clés sont répertoriées, et nous verrons progressivement la mise en œuvre de ces fonctionnalités plus tard.

  • Rapide
  • Support middleware
  • Sans crash
  • Validation JSON
  • Regroupement d'itinéraires
  • Gestion des erreurs
  • Rendu intégré/Extensible

Commencez par un petit exemple

Regardons le plus petit exemple donné dans la documentation officielle.

package main

import "github.com/gin-gonic/gin"

func main() {
    r := gin.Default()
    r.GET("/ping", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })
    r.Run() // listen and serve on 0.0.0.0:8080
}

Exécutez cet exemple, puis utilisez un navigateur pour visiter http://localhost:8080/ping, et vous obtiendrez un "pong".
Cet exemple est très simple. Il peut être divisé en seulement trois étapes :

  1. Utilisez gin.Default() pour créer un objet Engine avec des configurations par défaut.
  2. Enregistrez une fonction de rappel pour l'adresse "/ping" dans la méthode GET du Moteur. Cette fonction renverra un "pong".
  3. Démarrez le moteur pour commencer à écouter le port et à fournir des services.

Méthode HTTP

À partir de la méthode GET dans le petit exemple ci-dessus, nous pouvons voir que dans Gin, les méthodes de traitement des méthodes HTTP doivent être enregistrées en utilisant les fonctions correspondantes portant les mêmes noms.
Il existe neuf méthodes HTTP, et les quatre plus couramment utilisées sont GET, POST, PUT et DELETE, qui correspondent respectivement aux quatre fonctions d'interrogation, d'insertion, de mise à jour et de suppression. Il convient de noter que Gin fournit également l'interface Any, qui peut directement lier toutes les méthodes de traitement de la méthode HTTP à une seule adresse.
Le résultat renvoyé contient généralement deux ou trois parties. Le code et le message sont toujours là et les données sont généralement utilisées pour représenter des données supplémentaires. S'il n'y a pas de données supplémentaires à renvoyer, elles peuvent être omises. Dans l'exemple, 200 est la valeur du champ code, et "pong" est la valeur du champ message.

Créer une variable de moteur

Dans l'exemple ci-dessus, gin.Default() a été utilisé pour créer le moteur. Cependant, cette fonction est un wrapper pour New. En fait, le moteur est créé via la nouvelle interface.

package main

import "github.com/gin-gonic/gin"

func main() {
    r := gin.Default()
    r.GET("/ping", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })
    r.Run() // listen and serve on 0.0.0.0:8080
}

Jetez simplement un bref coup d'œil au processus de création pour l'instant et ne vous concentrez pas sur la signification des différentes variables membres dans la structure du moteur. On peut voir qu'en plus de créer et d'initialiser une variable moteur de type Engine, New définit également Engine.pool.New sur une fonction anonyme qui appelle Engine.allocateContext(). La fonction de cette fonction sera discutée plus tard.

Enregistrer les fonctions de rappel d'itinéraire

Il existe une structure RouterGroup intégrée dans le moteur. Les interfaces liées aux méthodes HTTP du Moteur sont toutes héritées de RouterGroup. Le "Regroupement d'itinéraires" dans les points caractéristiques mentionnés sur le site officiel est réalisé via la structure RouterGroup.

func New() *Engine {
    debugPrintWARNINGNew()
    engine := &Engine{
        RouterGroup: RouterGroup{
            //... Initialize the fields of RouterGroup
        },
        //... Initialize the remaining fields
    }
    engine.RouterGroup.engine = engine // Save the pointer of the engine in RouterGroup
    engine.pool.New = func() any {
        return engine.allocateContext()
    }
    return engine
}

Chaque RouterGroup est associé à un chemin de base basePath. Le basePath du RouterGroup intégré dans le moteur est "/".
Il existe également un ensemble de fonctions de traitement Handlers. Toutes les requêtes sous les chemins associés à ce groupe exécuteront en outre les fonctions de traitement de ce groupe, qui sont principalement utilisées pour les appels middleware. Les gestionnaires sont nuls lorsque le moteur est créé et un ensemble de fonctions peut être importé via la méthode Use. Nous verrons cette utilisation plus tard.

type RouterGroup struct {
    Handlers    HandlersChain // Processing functions of the group itself
    basePath    string        // Associated base path
    engine      *Engine       // Save the associated engine object
    root        bool          // root flag, only the one created by default in Engine is true
}

La méthode handle de RouterGroup est la dernière entrée pour enregistrer toutes les fonctions de rappel de méthode HTTP. La méthode GET et les autres méthodes liées aux méthodes HTTP appelées dans l'exemple initial ne sont que des wrappers pour la méthode handle.
La méthode handle calculera le chemin absolu en fonction du basePath du RouterGroup et du paramètre de chemin relatif, et appellera en même temps la méthode combineHandlers pour obtenir le tableau final des gestionnaires. Ces résultats sont transmis en paramètres à la méthode addRoute du Moteur pour enregistrer les fonctions de traitement.

func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
    absolutePath := group.calculateAbsolutePath(relativePath)
    handlers = group.combineHandlers(handlers)
    group.engine.addRoute(httpMethod, absolutePath, handlers)
    return group.returnObj()
}

La méthode combineHandlers crée une tranche mergedHandlers, puis y copie les gestionnaires du RouterGroup lui-même, puis y copie les gestionnaires des paramètres et enfin renvoie mergedHandlers. C'est-à-dire que lors de l'enregistrement d'une méthode utilisant handle, le résultat réel inclut les gestionnaires du RouterGroup lui-même.

Utilisez Radix Tree pour accélérer la récupération d'itinéraire

Dans le point de fonctionnalité "Rapide" mentionné sur le site officiel, il est mentionné que le routage des requêtes réseau est implémenté sur la base de l'arbre radix (Radix Tree). Cette partie n'est pas implémentée par Gin, mais par httprouter mentionné dans l'introduction de Gin au début. Gin utilise httprouter pour réaliser cette partie de la fonction. La mise en œuvre de l’arbre de base ne sera pas évoquée ici pour le moment. Nous nous concentrerons uniquement sur son utilisation pour l’instant. Peut-être que nous écrirons plus tard un article séparé sur la mise en œuvre de l'arbre de base.
Dans le moteur, il existe une variable Trees, qui est une tranche de la structure MethodTree. C'est cette variable qui contient les références à tous les arbres de base.

package main

import "github.com/gin-gonic/gin"

func main() {
    r := gin.Default()
    r.GET("/ping", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })
    r.Run() // listen and serve on 0.0.0.0:8080
}

Le moteur maintient un arbre de base pour chaque méthode HTTP. Le nœud racine de cet arbre et le nom de la méthode sont enregistrés ensemble dans une variable methodTree, et toutes les variables methodTree sont dans des arbres.

func New() *Engine {
    debugPrintWARNINGNew()
    engine := &Engine{
        RouterGroup: RouterGroup{
            //... Initialize the fields of RouterGroup
        },
        //... Initialize the remaining fields
    }
    engine.RouterGroup.engine = engine // Save the pointer of the engine in RouterGroup
    engine.pool.New = func() any {
        return engine.allocateContext()
    }
    return engine
}

On voit que dans la méthode addRoute du Moteur, il utilisera d'abord la méthode get des arbres pour obtenir le nœud racine de l'arbre base correspondant à la méthode. Si le nœud racine de l'arbre de base n'est pas obtenu, cela signifie qu'aucune méthode n'a été enregistrée pour cette méthode auparavant, et un nœud d'arbre sera créé en tant que nœud racine de l'arbre et ajouté aux arbres.
Après avoir obtenu le nœud racine, utilisez la méthode addRoute du nœud racine pour enregistrer un ensemble de gestionnaires de fonctions de traitement pour le chemin d'accès. Cette étape consiste à créer un nœud pour le chemin et les gestionnaires et à le stocker dans l'arborescence de base. Si vous essayez d'enregistrer une adresse déjà enregistrée, addRoute générera directement une erreur de panique.
Lors du traitement d'une requête HTTP, il est nécessaire de retrouver la valeur du nœud correspondant via le chemin. Le nœud racine possède une méthode getValue chargée de gérer l'opération de requête. Nous le mentionnerons en parlant du traitement des requêtes HTTP par Gin.

Importer des fonctions de traitement du middleware

La méthode Use de RouterGroup peut importer un ensemble de fonctions de traitement middleware. La « prise en charge du middleware » dans les points de fonctionnalité mentionnés sur le site officiel est obtenue via la méthode d'utilisation.
Dans l'exemple initial, lors de la création de la variable de structure Engine, New n'a pas été utilisé, mais Default a été utilisé. Jetons un coup d'œil à ce que Default fait de plus.

package main

import "github.com/gin-gonic/gin"

func main() {
    r := gin.Default()
    r.GET("/ping", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })
    r.Run() // listen and serve on 0.0.0.0:8080
}

On voit que c'est une fonction très simple. En plus d'appeler New pour créer l'objet Engine, il appelle uniquement Use pour importer les valeurs de retour de deux fonctions middleware, Logger et Recovery. La valeur de retour de Logger est une fonction de journalisation et la valeur de retour de Recovery est une fonction de gestion de la panique. Nous allons ignorer cela pour l'instant et examiner ces deux fonctions plus tard.
Bien que le moteur intègre RouterGroup, il implémente également la méthode Use, mais il ne s'agit que d'un appel à la méthode Use de RouterGroup et de quelques opérations auxiliaires.

func New() *Engine {
    debugPrintWARNINGNew()
    engine := &Engine{
        RouterGroup: RouterGroup{
            //... Initialize the fields of RouterGroup
        },
        //... Initialize the remaining fields
    }
    engine.RouterGroup.engine = engine // Save the pointer of the engine in RouterGroup
    engine.pool.New = func() any {
        return engine.allocateContext()
    }
    return engine
}

On peut voir que la méthode Use de RouterGroup est également très simple. Il ajoute simplement les fonctions de traitement middleware des paramètres à ses propres gestionnaires via l'ajout.

Commencez à courir

Dans le petit exemple, la dernière étape consiste à appeler la méthode Run du Moteur sans paramètres. Après l'appel, l'ensemble du framework commence à fonctionner et la visite de l'adresse enregistrée avec un navigateur peut déclencher correctement le rappel.

type RouterGroup struct {
    Handlers    HandlersChain // Processing functions of the group itself
    basePath    string        // Associated base path
    engine      *Engine       // Save the associated engine object
    root        bool          // root flag, only the one created by default in Engine is true
}

La méthode Run ne fait que deux choses : analyser l'adresse et démarrer le service. Ici, l'adresse n'a en fait besoin que de transmettre une chaîne, mais afin d'obtenir l'effet de pouvoir passer ou ne pas passer, un paramètre variadique est utilisé. La méthode solveAddress gère les résultats de différentes situations d'addr.
Le démarrage du service utilise la méthode ListenAndServe du package net/http de la bibliothèque standard. Cette méthode accepte une adresse d'écoute et une variable de l'interface Handler. La définition de l'interface Handler est très simple, avec une seule méthode ServeHTTP.

func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
    absolutePath := group.calculateAbsolutePath(relativePath)
    handlers = group.combineHandlers(handlers)
    group.engine.addRoute(httpMethod, absolutePath, handlers)
    return group.returnObj()
}

Étant donné que le moteur implémente ServeHTTP, le moteur lui-même sera transmis ici à la méthode ListenAndServe. Lorsqu'il y aura une nouvelle connexion au port surveillé, ListenAndServe sera responsable de l'acceptation et de l'établissement de la connexion, et lorsqu'il y aura des données sur la connexion, il appellera la méthode ServeHTTP du gestionnaire pour le traitement.

Messages de processus

Le ServeHTTP du Moteur est la fonction de rappel pour le traitement des messages. Jetons un coup d'œil à son contenu.

func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain {
    finalSize := len(group.Handlers) + len(handlers)
    assert1(finalSize 



La fonction de rappel a deux paramètres. Le premier est w qui est utilisé pour recevoir la réponse à la demande. Écrivez les données de réponse dans w. L'autre est req qui contient les données de cette requête. Toutes les données nécessaires au traitement ultérieur peuvent être lues à partir de req.
La méthode ServeHTTP fait quatre choses. Tout d'abord, récupérez un contexte du pool, puis liez le contexte aux paramètres de la fonction de rappel, puis appelez la méthode handleHTTPRequest avec le contexte comme paramètre pour traiter cette requête réseau, et enfin remettez le contexte dans le pool.
Examinons d'abord uniquement la partie centrale de la méthode handleHTTPRequest.

package main

import "github.com/gin-gonic/gin"

func main() {
    r := gin.Default()
    r.GET("/ping", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })
    r.Run() // listen and serve on 0.0.0.0:8080
}

La méthode handleHTTPRequest fait principalement deux choses. Tout d’abord, récupérez la méthode précédemment enregistrée à partir de l’arbre de base en fonction de l’adresse de la requête. Ici, les gestionnaires seront affectés au Contexte pour ce traitement, puis appelleront la fonction Next du Context pour exécuter les méthodes dans les gestionnaires. Enfin, écrivez les données de retour de cette requête dans l'objet de type réponseWriter du Context.

Contexte

Lors du traitement d'une requête HTTP, toutes les données liées au contexte se trouvent dans la variable Context. L'auteur a également écrit dans le commentaire de la structure Context que "Le contexte est la partie la plus importante du gin", ce qui montre son importance.
En parlant de la méthode ServeHTTP du Moteur ci-dessus, on peut voir que le Contexte n'est pas directement créé, mais obtenu via la méthode Get de la variable pool du Moteur. Après avoir été retiré, son état est réinitialisé avant utilisation, et il est remis dans la piscine après utilisation.
La variable pool du moteur est de type sync.Pool. Pour l'instant, sachez simplement qu'il s'agit d'un pool d'objets fourni par le responsable de Go qui prend en charge une utilisation simultanée. Vous pouvez obtenir un objet du pool via sa méthode Get, et vous pouvez également placer un objet dans le pool à l'aide de la méthode Put. Lorsque le pool est vide et que la méthode Get est utilisée, il créera un objet via sa propre méthode New et le renverra.
Cette méthode New est définie dans la méthode New du Moteur. Jetons un autre regard sur la nouvelle méthode du moteur.

func New() *Engine {
    debugPrintWARNINGNew()
    engine := &Engine{
        RouterGroup: RouterGroup{
            //... Initialize the fields of RouterGroup
        },
        //... Initialize the remaining fields
    }
    engine.RouterGroup.engine = engine // Save the pointer of the engine in RouterGroup
    engine.pool.New = func() any {
        return engine.allocateContext()
    }
    return engine
}

Il ressort du code que la méthode de création du Context est la méthode allocateContext du Engine. Il n’y a aucun mystère dans la méthode allocateContext. Il effectue simplement une pré-allocation en deux étapes des longueurs de tranche, puis crée l'objet et le renvoie.

type RouterGroup struct {
    Handlers    HandlersChain // Processing functions of the group itself
    basePath    string        // Associated base path
    engine      *Engine       // Save the associated engine object
    root        bool          // root flag, only the one created by default in Engine is true
}

La méthode Next du Context mentionné ci-dessus exécutera toutes les méthodes des gestionnaires. Jetons un coup d'œil à sa mise en œuvre.

func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
    absolutePath := group.calculateAbsolutePath(relativePath)
    handlers = group.combineHandlers(handlers)
    group.engine.addRoute(httpMethod, absolutePath, handlers)
    return group.returnObj()
}

Bien que les gestionnaires soient une tranche, la méthode Next n'est pas simplement implémentée comme un parcours de gestionnaires, mais introduit un index d'enregistrement de progression du traitement, qui est initialisé à 0, incrémenté au début de la méthode et à nouveau incrémenté après une méthode. l'exécution est terminée.

La conception de Next a une excellente relation avec son utilisation, principalement pour coopérer avec certaines fonctions middleware. Par exemple, lorsqu'une panique est déclenchée lors de l'exécution d'un certain gestionnaire, l'erreur peut être détectée à l'aide de recovery dans le middleware, puis Next peut être rappelé pour continuer à exécuter les gestionnaires suivants sans affecter l'ensemble du tableau des gestionnaires en raison du problème. d'un gestionnaire.

Gérer la panique

Dans Gin, si la fonction de traitement d'une certaine requête déclenche une panique, l'ensemble du framework ne plantera pas directement. Au lieu de cela, un message d'erreur sera généré et le service continuera à être fourni. Cela ressemble quelque peu à la façon dont les frameworks Lua utilisent habituellement xpcall pour exécuter des fonctions de traitement de messages. Cette opération est le point de fonctionnalité "Crash-free" mentionné dans la documentation officielle.
Comme mentionné ci-dessus, lors de l'utilisation de gin.Default pour créer un moteur, la méthode Use du moteur sera exécutée pour importer deux fonctions. L'un d'eux est la valeur de retour de la fonction Recovery, qui est un wrapper d'autres fonctions. La dernière fonction appelée est CustomRecoveryWithWriter. Jetons un coup d'œil à l'implémentation de cette fonction.

package main

import "github.com/gin-gonic/gin"

func main() {
    r := gin.Default()
    r.GET("/ping", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })
    r.Run() // listen and serve on 0.0.0.0:8080
}

Nous ne nous concentrons pas ici sur les détails de la gestion des erreurs, mais regardons uniquement ce qu'elle fait. Cette fonction renvoie une fonction anonyme. Dans cette fonction anonyme, une autre fonction anonyme est enregistrée en utilisant defer. Dans cette fonction anonyme interne, la récupération est utilisée pour intercepter la panique, puis la gestion des erreurs est effectuée. Une fois le traitement terminé, la méthode Next du Context est appelée, afin que les gestionnaires du Context qui étaient initialement exécutés en séquence puissent continuer à être exécutés.

Leapcell : la plate-forme sans serveur de nouvelle génération pour l'hébergement Web, les tâches asynchrones et Redis

Enfin, permettez-moi de vous présenter la meilleure plateforme pour déployer les services Gin : Leapcell.

A Deep Dive into Gin: Golang

1. Prise en charge multilingue

  • Développez avec JavaScript, Python, Go ou Rust.

2. Déployez gratuitement un nombre illimité de projets

  • payez uniquement pour l'utilisation – pas de demande, pas de frais.

3. Rentabilité imbattable

  • Payez à l'utilisation sans frais d'inactivité.
  • Exemple : 25 $ prend en charge 6,94 millions de requêtes avec un temps de réponse moyen de 60 ms.

4. Expérience de développeur rationalisée

  • Interface utilisateur intuitive pour une configuration sans effort.
  • Pipelines CI/CD entièrement automatisés et intégration GitOps.
  • Mesures et journalisation en temps réel pour des informations exploitables.

5. Évolutivité sans effort et hautes performances

  • Mise à l'échelle automatique pour gérer facilement une concurrence élevée.
  • Zéro frais opérationnels : concentrez-vous uniquement sur la construction.

Explorez-en davantage dans la documentation !

Twitter de Leapcell : https://x.com/LeapcellHQ

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
GO Language Pack Import: Quelle est la différence entre le soulignement et sans soulignement?GO Language Pack Import: Quelle est la différence entre le soulignement et sans soulignement?Mar 03, 2025 pm 05:17 PM

Cet article explique les mécanismes d'importation des packages de Go: les importations nommées (par exemple, importation & quot; fmt & quot;) et les importations vierges (par exemple, importation _ & quot; fmt & quot;). Les importations nommées rendent le contenu du package accessible, tandis que les importations vierges ne font que l'exécuter t

Comment mettre en œuvre le transfert d'informations à court terme entre les pages du cadre Beego?Comment mettre en œuvre le transfert d'informations à court terme entre les pages du cadre Beego?Mar 03, 2025 pm 05:22 PM

Cet article explique la fonction Newflash () de Beego pour le transfert de données inter-pages dans les applications Web. Il se concentre sur l'utilisation de NewFlash () pour afficher les messages temporaires (succès, erreur, avertissement) entre les contrôleurs, en tirant parti du mécanisme de session. Limiter

Comment convertir la liste des résultats de la requête MySQL en une tranche de structure personnalisée dans le langage Go?Comment convertir la liste des résultats de la requête MySQL en une tranche de structure personnalisée dans le langage Go?Mar 03, 2025 pm 05:18 PM

Cet article détaille la conversion efficace de la requête MySQL Resulte en tranches de structure GO. Il met l'accent sur l'utilisation de la méthode de numérisation de la base de données / SQL pour des performances optimales, en évitant l'analyse manuelle. Meilleures pratiques pour la cartographie des champs struct à l'aide de balises DB et de robus

Comment écrire des objets et des talons simulés pour les tests en Go?Comment écrire des objets et des talons simulés pour les tests en Go?Mar 10, 2025 pm 05:38 PM

Cet article montre la création de simulations et de talons dans GO pour les tests unitaires. Il met l'accent sur l'utilisation des interfaces, fournit des exemples d'implémentations simulées et discute des meilleures pratiques telles que la tenue de simulations concentrées et l'utilisation de bibliothèques d'assertion. L'articl

Comment puis-je définir des contraintes de type personnalisé pour les génériques en Go?Comment puis-je définir des contraintes de type personnalisé pour les génériques en Go?Mar 10, 2025 pm 03:20 PM

Cet article explore les contraintes de type personnalisé de Go pour les génériques. Il détaille comment les interfaces définissent les exigences de type minimum pour les fonctions génériques, améliorant la sécurité du type et la réutilisabilité du code. L'article discute également des limitations et des meilleures pratiques

Comment écrire des fichiers dans GO Language de manière pratique?Comment écrire des fichiers dans GO Language de manière pratique?Mar 03, 2025 pm 05:15 PM

Cet article détaille la rédaction de fichiers efficace dans GO, en comparant OS.WriteFile (adapté aux petits fichiers) avec OS.OpenFile et Buffered Writes (optimal pour les fichiers volumineux). Il met l'accent sur la gestion robuste des erreurs, l'utilisation de différer et la vérification des erreurs spécifiques.

Comment rédigez-vous des tests unitaires en Go?Comment rédigez-vous des tests unitaires en Go?Mar 21, 2025 pm 06:34 PM

L'article traite des tests d'unité d'écriture dans GO, couvrant les meilleures pratiques, des techniques de moquerie et des outils pour une gestion efficace des tests.

Comment puis-je utiliser des outils de traçage pour comprendre le flux d'exécution de mes applications GO?Comment puis-je utiliser des outils de traçage pour comprendre le flux d'exécution de mes applications GO?Mar 10, 2025 pm 05:36 PM

Cet article explore l'utilisation d'outils de traçage pour analyser le flux d'exécution des applications GO. Il traite des techniques d'instrumentation manuelles et automatiques, de comparaison d'outils comme Jaeger, Zipkin et OpenTelelemetry, et mettant en évidence une visualisation efficace des données

See all articles

Outils d'IA chauds

Undresser.AI Undress

Undresser.AI Undress

Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover

AI Clothes Remover

Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool

Undress AI Tool

Images de déshabillage gratuites

Clothoff.io

Clothoff.io

Dissolvant de vêtements AI

AI Hentai Generator

AI Hentai Generator

Générez AI Hentai gratuitement.

Article chaud

R.E.P.O. Crystals d'énergie expliqués et ce qu'ils font (cristal jaune)
2 Il y a quelques semainesBy尊渡假赌尊渡假赌尊渡假赌
Repo: Comment relancer ses coéquipiers
4 Il y a quelques semainesBy尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island Adventure: Comment obtenir des graines géantes
4 Il y a quelques semainesBy尊渡假赌尊渡假赌尊渡假赌

Outils chauds

Dreamweaver CS6

Dreamweaver CS6

Outils de développement Web visuel

Listes Sec

Listes Sec

SecLists est le compagnon ultime du testeur de sécurité. Il s'agit d'une collection de différents types de listes fréquemment utilisées lors des évaluations de sécurité, le tout en un seul endroit. SecLists contribue à rendre les tests de sécurité plus efficaces et productifs en fournissant facilement toutes les listes dont un testeur de sécurité pourrait avoir besoin. Les types de listes incluent les noms d'utilisateur, les mots de passe, les URL, les charges utiles floues, les modèles de données sensibles, les shells Web, etc. Le testeur peut simplement extraire ce référentiel sur une nouvelle machine de test et il aura accès à tous les types de listes dont il a besoin.

Navigateur d'examen sécurisé

Navigateur d'examen sécurisé

Safe Exam Browser est un environnement de navigation sécurisé permettant de passer des examens en ligne en toute sécurité. Ce logiciel transforme n'importe quel ordinateur en poste de travail sécurisé. Il contrôle l'accès à n'importe quel utilitaire et empêche les étudiants d'utiliser des ressources non autorisées.

Version crackée d'EditPlus en chinois

Version crackée d'EditPlus en chinois

Petite taille, coloration syntaxique, ne prend pas en charge la fonction d'invite de code

mPDF

mPDF

mPDF est une bibliothèque PHP qui peut générer des fichiers PDF à partir de HTML encodé en UTF-8. L'auteur original, Ian Back, a écrit mPDF pour générer des fichiers PDF « à la volée » depuis son site Web et gérer différentes langues. Il est plus lent et produit des fichiers plus volumineux lors de l'utilisation de polices Unicode que les scripts originaux comme HTML2FPDF, mais prend en charge les styles CSS, etc. et présente de nombreuses améliorations. Prend en charge presque toutes les langues, y compris RTL (arabe et hébreu) ​​et CJK (chinois, japonais et coréen). Prend en charge les éléments imbriqués au niveau du bloc (tels que P, DIV),