」工欲善其事,必先利其器。「—孔子《論語.錄靈公》
首頁 > 程式設計 > 開始使用 SPRING BATCH 進行編程

開始使用 SPRING BATCH 進行編程

發佈於2024-11-02
瀏覽:285

Introduction

Dans vos projets personnels ou professionnels, Il vous arrive de faire des traitements sur de gros volumes de données. Le traitement par lots de données est un moyen efficace de traiter de gros volumes de données où celles-ci sont collectées, traitées, puis les résultats par lots sont produits. Le traitement par lots peut être appliqué dans de nombreux cas d'utilisation. Un cas d'utilisation courant du traitement par lots consiste à transformer un grand ensemble de fichiers CSV ou JSON en un format structuré prêt pour un traitement ultérieur.

Dans ce tutoriel, nous allons essayer de voir comment mettre cette architecture en place avec Spring Boot qui est un framework qui facilite le développement d'applications fondées sur Spring.

Qu'est-ce que Spring-Batch?

Spring Batch est un framework open source pour le traitement par lots. Il s'agit d'une solution légère et complète conçue pour permettre le développement d'applications par lots robustes, que l'on trouve souvent dans les systèmes d'entreprise modernes. Son développement est issu d'une collaboration entre SpringSource et Accenture.

Il permet de pallier à des problème récurrents lors de développement de batchs:

  • productivité
  • gestion de gros volumes de données
  • fiabilité
  • réinvention de la roue.

N.B: En informatique, un batch est un programme fonctionnant en StandAlone, réalisant un ensemble de traitements sur un volume de données.

Architecture de base de Spring Batch

Commencer la programmation avec SPRING BATCH

Pour gérer les données d'un batch, on utilise principalement les trois outils suivants:

JobLauncher : il s'agit du composant chargé de lancer/démarrer le programme de traitement par lot (batch). Il peut être configuré pour s'auto déclencher ou pour être déclenché par un évènement extérieur (lancement manuel). Dans le workflow Spring Batch, le JobLauncher est chargé d'exécuter un Job (tâche).

Job : il s'agit du composant qui représente la tâche à qui on délègue la responsabilité du besoin métier traité dans le programme. Il est chargé de lancer de façon séquentielle une ou plusieurs Step.

Step : c'est le composant qui enveloppe le cœur même du besoin métier à traiter. Il est chargé de définir trois sous-composants structurés comme suit :

ItemReader : c'est le composant chargé de lire les données d'entrées à traiter. Elles peuvent provenir de diverses sources (bases de données, fichiers plats (csv, xml, xls, etc.), queue) ;

ItemProcessor : c'est le composant responsable de la transformation des données lues. C'est en son sein que toutes les règles de gestion sont implémentées .

ItemWriter : ce composant sauvegarde les données transformées par le processor dans un ou plusieurs conteneurs désirés (bases de données, fichiers plats (csv, xml, xls, etc.), cloud).

JobRepository : c'est le composant chargé d'enregistrer les statistiques issues du monitoring sur le JobLauncher, le Job et la (ou les) Step à chaque exécution. Il offre deux techniques possibles pour stocker ces statistiques : le passage par une base de données ou le passage par une Map. Lorsque le stockage des statistiques est fait dans une base de données, et donc persisté de façon durable, cela permet le suivi continuel du Batch dans le temps à l'effet d'analyser les éventuels problèmes en cas d'échec. A contrario lorsque c'est dans une Map, les statistiques persistées seront perdues à la terminaison de chaque instance d'exécution du Batch. Dans tous les cas, il faut configurer l'un ou l'autre obligatoirement.

Pour plus d'informations, je vous conseille de consulter le site de Spring.

Après cette explication bref de l'architecture de spring batch,essayons dès à présent de montrer comment mettre en place un job spring batch qui va lire des données à partir d'un fichier CSV que nous allons par la suite insérer dans une base de données."Let's get into coding".

Commencer la programmation avec SPRING BATCH

Configuration du projet

Le moyen le plus simple de générer un projet Spring Boot consiste à utiliser l'outil de démarrage Spring avec les étapes ci-dessous:

  • Se rendre sur le site Spring Initializr
  • Sélectionner Maven Project et langage Java
  • Ajouter Spring Batch, JPA, Lombok, H2 Database
  • Entrer le nom du groupe comme "com.example" et l'artefact comme "SpringBatch"
  • Cliquez le bouton generate

Commencer la programmation avec SPRING BATCH

Une fois le projet généré, il faut le dézipper  ensuite l'importer dans votre IDE.

Technologies utilisées:

  • JDK 1.8
  • Maven
  • IntelliJ
  • Lombok
  • Spring data JPA
  • H2 Database

Dépendances du projet

Toutes les dépendances du projet sont dans le fichier pom.xml. Les trois lettres POM sont l'acronyme de Project Object Model. Sa représentation XML est traduite par Maven en une structure de données qui représente le modèle du projet.

4.0.0org.springframework.bootspring-boot-starter-parent2.3.3.RELEASEcom.pathusSpringBatchExample0.0.1-SNAPSHOTSpringBatchExampleDemo of spring batch project 1.8org.springframework.bootspring-boot-starter-batchorg.springframework.bootspring-boot-starter-data-jpacom.h2databaseh2runtimeorg.projectlomboklomboktrueorg.springframework.bootspring-boot-starter-testtestorg.junit.vintagejunit-vintage-engineorg.springframework.batchspring-batch-testtestorg.springframework.bootspring-boot-maven-plugin

Structure du Projet

La structure du projet se présente comme suit:

Commencer la programmation avec SPRING BATCH

Configuration du Job

Pour activer le traitement par lots, nous devons annoter la classe de configuration avec @EnableBatchProcessing. Nous devons par la suite créer un reader pour lire notre fichier CSV, créer un processor pour traiter les données d'entrée avant d'écrire, créer un writer pour écrire dans la base de données.

import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemReader;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.LineMapper;
import org.springframework.batch.item.file.mapping.DefaultLineMapper;
import org.springframework.batch.item.file.transform.DelimitedLineTokenizer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;

import com.pathus90.springbatchexample.batch.StudentProcessor;
import com.pathus90.springbatchexample.batch.StudentWriter;
import com.pathus90.springbatchexample.model.Student;
import com.pathus90.springbatchexample.model.StudentFieldSetMapper;

@Configuration
@EnableBatchProcessing
@EnableScheduling
public class BatchConfig {

    private static final String FILE_NAME = "results.csv";
    private static final String JOB_NAME = "listStudentsJob";
    private static final String STEP_NAME = "processingStep";
    private static final String READER_NAME = "studentItemReader";

    @Value("${header.names}")
    private String names;

    @Value("${line.delimiter}")
    private String delimiter;

    @Autowired
    private JobBuilderFactory jobBuilderFactory;

    @Autowired
    private StepBuilderFactory stepBuilderFactory;

    @Bean
    public Step studentStep() {
        return stepBuilderFactory.get(STEP_NAME)
                .chunk(5)
                .reader(studentItemReader())
                .processor(studentItemProcessor())
                .writer(studentItemWriter())
                .build();
    }

    @Bean
    public Job listStudentsJob(Step step1) {
        return jobBuilderFactory.get(JOB_NAME)
                .start(step1)
                .build();
    }

    @Bean
    public ItemReader studentItemReader() {
        FlatFileItemReader reader = new FlatFileItemReader();
        reader.setResource(new ClassPathResource(FILE_NAME));
        reader.setName(READER_NAME);
        reader.setLinesToSkip(1);
        reader.setLineMapper(lineMapper());
        return reader;

    }

    @Bean
    public LineMapper lineMapper() {

        final DefaultLineMapper defaultLineMapper = new DefaultLineMapper();
        final DelimitedLineTokenizer lineTokenizer = new DelimitedLineTokenizer();
        lineTokenizer.setDelimiter(delimiter);
        lineTokenizer.setStrict(false);
        lineTokenizer.setNames(names.split(delimiter));

        final StudentFieldSetMapper fieldSetMapper = new StudentFieldSetMapper();
        defaultLineMapper.setLineTokenizer(lineTokenizer);
        defaultLineMapper.setFieldSetMapper(fieldSetMapper);

        return defaultLineMapper;
    }

    @Bean
    public ItemProcessor studentItemProcessor() {
        return new StudentProcessor();
    }

    @Bean
    public ItemWriter studentItemWriter() {
        return new StudentWriter();
    }
}

Configuration du job et de la Step

La première méthode définit le job et la seconde définit une seule step. Les jobs sont créés à partir des steps, où chaque step peut impliquer un reader, un processor et un writer. Dans la définition de la step, on définit la quantité de données à écrire à la fois et dans notre cas, il écrit jusqu'à 5 enregistrements à la fois. Ensuite, on  configure le reader, le processor  et le writer à l'aide des beans injectés précédemment. Quand  la définition de notre job, il pourra définir différentes step au sein de notre exécution à travers un ordre précis. la step studentStep sera exécutée par le job  listStudentsJob.

@Bean
    public Step studentStep() {
        return stepBuilderFactory.get(STEP_NAME)
                .chunk(5)
                .reader(studentItemReader())
                .processor(studentItemProcessor())
                .writer(studentItemWriter())
                .build();
    }

    @Bean
    public Job listStudentsJob(Step step1) {
        return jobBuilderFactory.get(JOB_NAME)
                .start(step1)
                .build();
    }

Définition du Reader

Dans la configuration de notre batch, le Reader lit une source de données et est appelé successivement au sein d'une étape et retourne des objets pour lequel il est défini (Student dans notre cas).

 @Bean
    public ItemReader studentItemReader() {
        FlatFileItemReader reader = new FlatFileItemReader();
        reader.setResource(new ClassPathResource(FILE_NAME));
        reader.setName(READER_NAME);
        reader.setLinesToSkip(1);
        reader.setLineMapper(lineMapper());
        return reader;
    }

La classe FlatFileItemReader utilise la classe DefaultLineMapper qui utilise à son tour la classe DelimitedLineTokenizer. Le rôle de DelimitedLineTokenizer est de décomposer chaque ligne dans un objet FieldSet  et la propriété names donne le format de l'entête du fichier et permet d'identifier les données de chaque ligne. Cette propriété names est utilisée par la classe d'implantation de transformation de données en objet métier  à travers l'objet FieldSet. Il s'agit de la classe indiquée par la propriété fieldSetMapper ( StudentFieldSetMapper).

import org.springframework.batch.item.file.mapping.FieldSetMapper;
import org.springframework.batch.item.file.transform.FieldSet;

public class StudentFieldSetMapper implements FieldSetMapper {
    @Override
    public Student mapFieldSet(FieldSet fieldSet) {
        return Student.builder()
                .rank(fieldSet.readString(0))
                .firstName(fieldSet.readString(1))
                .lastName(fieldSet.readString(2))
                .center(fieldSet.readString(3))
                .pv(fieldSet.readString(4))
                .origin(fieldSet.readString(5))
                .mention(fieldSet.readString(6))
                .build();
    }
}

L'interface LineMapper quand à elle est utilisée pour mapper des lignes (chaînes) vers des objets généralement utilisée pour mapper des lignes lues à partir d'un fichier

  @Bean
    public LineMapper lineMapper() {

        final DefaultLineMapper defaultLineMapper = new DefaultLineMapper();
        final DelimitedLineTokenizer lineTokenizer = new DelimitedLineTokenizer();
        lineTokenizer.setDelimiter(delimiter);
        lineTokenizer.setStrict(false);
        lineTokenizer.setNames(names.split(delimiter));

        final StudentFieldSetMapper fieldSetMapper = new StudentFieldSetMapper();
        defaultLineMapper.setLineTokenizer(lineTokenizer);
        defaultLineMapper.setFieldSetMapper(fieldSetMapper);

        return defaultLineMapper;
    }

Definition du Processor

Contrairement à un Reader, les implémentations d'un Processor sont plutôt pour des besoins fonctionnels. Il n'est pas obligatoire et on peut s'en passer si aucun besoin fonctionnel n'est prévu dans notre traitement Dans notre exemple, on a écrit un simple processor qui ne fait que convertir quelques attributs de notre objet student en majuscules et on peut aller au delà de cet exemple avec des cas fonctionnels plus concrets.

import org.springframework.batch.item.ItemProcessor;

import com.pathus90.springbatchexample.model.Student;

public class StudentProcessor implements ItemProcessor {

    @Override
    public Student process(Student student) {
        student.setFirstName(student.getFirstName().toUpperCase());
        student.setLastName(student.getLastName().toUpperCase());
        student.setCenter(student.getCenter().toUpperCase());
        student.setOrigin(student.getOrigin().toUpperCase());
        student.setMention(student.getMention().toUpperCase());
        return student;
    }
}

Definition du Writer

Le Writer écrit les données provenant du processor (ou directement lues par le Reader). Dans notre cas, Il reçoit du processor les objets transformés et chaque objet sera par la suite persisté dans notre base de donnés et transaction sera validée.

import java.util.List;

import org.springframework.batch.item.ItemWriter;
import org.springframework.beans.factory.annotation.Autowired;

import com.pathus90.springbatchexample.model.Student;
import com.pathus90.springbatchexample.service.IStudentService;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class StudentWriter implements ItemWriter {

    @Autowired
    private IStudentService studentService;

    @Override
    public void write(List extends Student> students) {
        students.stream().forEach(student -> {
            log.info("Enregistrement en base de l'objet {}", student);
            studentService.insertStudent(student);
        });
    }
}

Fichier de configuration du batch (application.properties)

# Enabling H2 Console
spring.h2.console.enabled=true

# To See H2 Console in Browser:
spring.h2.console.path=/h2-console
spring.h2.console.settings.web-allow-others=true

# ===============================
# DB
# ===============================

spring.datasource.url=jdbc:h2:file:~/test1
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.driver-class-name=org.h2.Driver

spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
# ===============================
# JPA / HIBERNATE
# ===============================

spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
spring.datasource.continue-on-error=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect

header.names= Rang,Prenoms,Noms,Centre,PV,Origine,Mention
line.delimiter=,

logging.level.org.springframework.web=ERROR
logging.level.com.pathus90=DEBUG

Fichier CSV à écrire en base de données

Rang,Prenoms,Noms,Centre,PV,Origine,Mention
1,Mamadou Sanou ,diallo,TOMBO 2,1686,MES FLEURS,BIEN
2,SAFIATOU,DIALLO,A.S. TOURE,191,JACQUELINE,BIEN
3,BoubA ,Camara, vh,1686,koumba diawara ,BIEN
4,SAFIATOU,CondE,kk3,1951,kipe,BIEN

Lancement de l'application

Une fois que que avions fini de mettre en place la configuration du batch, regardons dès à present voir si tout ce qui a été dit plus haut fonctionne 

Pour exécuter l'application, il faut chercher le fichier qui contient l'annotation @SpringBootApplication qui est le main de notre application.

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
public class SpringBatchExampleApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringBatchExampleApplication.class, args);
    }
}

Le lancement du main ci-dessus va démarrer notre job et le batch launcher se présente comme suit :

package com.pathus90.springbatchexample;

import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.JobParametersInvalidException;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
import org.springframework.batch.core.repository.JobRestartException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class BatchLauncher {

    @Autowired
    private JobLauncher jobLauncher;

    @Autowired
    private Job job;

    public BatchStatus run() throws JobParametersInvalidException, JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException {
        JobParameters parameters = new JobParametersBuilder().addLong("time", System.currentTimeMillis()).toJobParameters();
        JobExecution jobExecution = jobLauncher.run(job, parameters);
        return jobExecution.getStatus();
    }
}

Un scheduler (planificateur) a été mis en place pour permettre au batch de s'auto déclencher. Dans cet exemple, le batch une fois lancé s'exécuter toutes les 8 secondes. Vous pouvez jouer avec en changeant la valeur fixedDelayen millisecondes.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;

@Component
@Slf4j
public class Scheduler {

    @Autowired
    private BatchLauncher batchLauncher;

    @Scheduled(fixedDelay = 8000)
    public void perform() throws Exception {
        log.info("Batch programmé pour tourner toutes les 8 secondes");
        batchLauncher.run();
    }
}

En plus  d'exécuter le fichier main ci-dessus pour démarrer le batch, on peut également lancé la commande mvn spring-boot:run  tout en utilisant une invite de commande.

On peut aussi lancer l'application avec le fichier d'archive JAR et dans ce cas il faut :

  1. Se mettre dans le dossier parent du projet en utilisant une invite de commande et exécuter la commande mvn clean package qui va packager notre projet.

  2. Dans le dossier target, un fichier jar sera crée.

  3. Pour exécuter l'application, utiliser la commande java -jar target/nom_du_fichier_généré-0.0.1-SNAPSHOT.jar

S'assurer aussi que la console H2console a déjà démarré lors du lancement notre application spring batch et la base de données est automatiquement générée ainsi que la creation de la table Student.

Commencer la programmation avec SPRING BATCH

On observe bien que notre fichier a été bien intégré dans notre base de données.

N.B:Si on souhaite aussi démarrer le batch manuellement sans passer un schedulerqui va se déclencher en fonction de notre paramétrage , j'ai e xposé une API à l'aide du contrôleur pour appeler le Job Spring Batch.

package com.pathus90.springbatchexample.controller;

import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.JobParametersInvalidException;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
import org.springframework.batch.core.repository.JobRestartException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.pathus90.springbatchexample.BatchLauncher;

import lombok.extern.slf4j.Slf4j;

@RestController
@RequestMapping("/load")
@Slf4j
public class StudentController {

    @Autowired
    private BatchLauncher batchLauncher;

    @GetMapping
    public BatchStatus load() throws JobParametersInvalidException, JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException {
        log.info("Batch demarré à la demande");
        return batchLauncher.run();
    }
}

il suffit juste de lancer L'URL : http://localhost:8080/load et le batch se lancera 

Commencer la programmation avec SPRING BATCH

On arrive en fin de notre premier apprentissage sur la programmation des batchs grâce au framework Spring. Laissez des commentaires ou des questions si vous en aviez !

Bonne apprentissage à tous et j'espère que ce premier tutoriel vous sera bénéfique.

Vous trouverez le code source disponible ici

Références

  1. https://spring.io/guides/gs/batch-processing/
  2. https://jeremy-jeanne.developpez.com/tutoriels/spring/spring-batch/#LIII-B-3
  3. https://www.baeldung.com/introduction-to-spring-batch
版本聲明 本文轉載於:https://dev.to/pathus90/commencer-la-programmation-avec-spring-batch-5ec7?1如有侵犯,請聯絡[email protected]刪除
最新教學 更多>
  • FCS API 與 Insight Ease:比特幣 API 服務的簡單比較
    FCS API 與 Insight Ease:比特幣 API 服務的簡單比較
    如果您熱衷於比特幣 API,那麼選擇正確的 API 非常重要。特別是如果您是開發人員、金融分析師或經營金融科技公司。您會聽到的兩個流行名稱是 FCS API 和 Insight Ease。但哪一個比較好呢?讓我們仔細觀察它們的比較,特別是當涉及加密貨幣即時匯率 API、加密貨幣 API 交易,甚至...
    程式設計 發佈於2024-11-02
  • 如何在不修改HTML的情況下用JavaScript監聽表單提交事件?
    如何在不修改HTML的情況下用JavaScript監聽表單提交事件?
    在JavaScript 中監聽表單提交事件而不修改HTML在本文中,我們解決了在不修改HTML 的情況下監聽表單提交事件的常見挑戰必須修改HTML 程式碼。我們不依賴 HTML 中的 onClick 或 onSubmit 屬性,而是提供純 JavaScript 解決方案。 為了實現這一點,我們利用 ...
    程式設計 發佈於2024-11-02
  • Document.getElementById 與 jQuery $():主要差異是什麼?
    Document.getElementById 與 jQuery $():主要差異是什麼?
    Document.getElementById vs jQuery $():比較分析深入研究Web 開發領域時,了解一般版本之間的細微差別JavaScript 和jQuery 可能至關重要。本文研究了兩個看似相同的程式碼片段之間的細微差別:var contents = document.getEle...
    程式設計 發佈於2024-11-02
  • 在 Java 中使用方法和變數句柄進行運行時物件存取和操作
    在 Java 中使用方法和變數句柄進行運行時物件存取和操作
    反射和方法/var 句柄是 Java 中的兩個強大功能,允許開發人員在運行時存取和操作物件。然而,它們在存取和處理物件的方式上有所不同。 讓我們來看一個如何使用反射來存取類別中方法的範例。我們將從一個名為「MyClass」的簡單類別開始,它有一個私有字串變數和該變數的 getter 方法。為了創建...
    程式設計 發佈於2024-11-02
  • 如何在 Python 中使用內建函數驗證 IP 位址?
    如何在 Python 中使用內建函數驗證 IP 位址?
    Python 中的 IP 位址驗證驗證 IP 位址的有效性是程式設計中的常見任務。從使用者接收字串形式的 IP 位址時,必須對其進行驗證,以確保它們符合正確的格式和結構。 要在 Python 中有效驗證 IP 位址,請考慮以下方法:無需手動解析 IP 位址,而是利用套接字模組中的內建 inet_at...
    程式設計 發佈於2024-11-02
  • 我需要學習程式設計的幫助
    我需要學習程式設計的幫助
    您好,我是系統工程專業的學生,我覺得我在課程中學到的程式設計知識不多。我想自學,因為我對這個主題非常感興趣。這就是我在這個網站上向了解程式設計的人尋求幫助的原因。如果有人知道學習程式設計的最佳課程,從基礎開始並進步到更專業的水平,那將會有很大的幫助。 我感興趣的語言: Java JavaScrip...
    程式設計 發佈於2024-11-02
  • 如何將 gorm.Model 整合到具有日期時間支援的 Protocol Buffer 定義中?
    如何將 gorm.Model 整合到具有日期時間支援的 Protocol Buffer 定義中?
    將gorm.Model 整合到Protocol Buffer 定義中將gorm.Model 整合到Protocol Buffer 定義中將gorm 的gorm.Model 欄位整合到protobuf 定義時,由於proto3 中缺乏日期時間支持,出現了挑戰。本文探討了此問題的解決方案。 ProtoB...
    程式設計 發佈於2024-11-02
  • 修補您的 Discord 活動的網路請求,以實現順利的 CSP 合規性
    修補您的 Discord 活動的網路請求,以實現順利的 CSP 合規性
    透過Discord運行Discord活動時,您可能會遇到內容安全策略(CSP)問題。您可以透過確保網路請求遵循 Discord 代理 規則來修復這些問題。 這可以手動完成...或者你可以讓@robojs/patch處理它。 什麼是CSP? 內容安全策略 (CSP) 是一種安全標準...
    程式設計 發佈於2024-11-02
  • 推薦項目:刪除課程表查看數據
    推薦項目:刪除課程表查看數據
    LabEx 的這個專案釋放了資料庫管理的力量,提供了在資料庫中建立和操作視圖的全面學習體驗。無論您是嶄露頭角的資料庫管理員還是經驗豐富的開發人員,該專案都提供了寶貴的機會來增強您的技能並獲得對資料管理世界的實際見解。 深入了解基礎知識 在這個專案中,您將踏上了解資料庫中視圖的核心概...
    程式設計 發佈於2024-11-02
  • 模擬網路請求變得容易:整合 Jest 和 MSW
    模擬網路請求變得容易:整合 Jest 和 MSW
    Writing unit tests that involve mocking or stubbing API calls can feel overwhelming—I’ve been there myself. In this article, I’ll guide you through a ...
    程式設計 發佈於2024-11-02
  • 使用 Javascript 的哈希映射
    使用 Javascript 的哈希映射
    介紹 哈希映射(Hash Map),也稱為哈希表(Hash Table),是實現關聯數組抽象資料類型的資料結構,是可以將鍵映射到值的結構。 它使用雜湊函數來計算儲存桶或槽數組的索引,從中可以找到所需的值。 哈希映射的主要優點是它的效率。插入新的鍵值對、刪除鍵值對以及查找給定鍵...
    程式設計 發佈於2024-11-02
  • HTPX 簡介:適用於 JavaScript 和 Node.js 的輕量級多功能 HTTP 用戶端
    HTPX 簡介:適用於 JavaScript 和 Node.js 的輕量級多功能 HTTP 用戶端
    作為開發人員,我們的Web 應用程式通常需要一個可靠且高效的HTTP 用戶端,無論我們是在瀏覽器中使用JavaScript 還是在伺服器端使用Node.js 進行構建。這就是我創建 HTPX 的原因——一個強大的、輕量級的解決方案,旨在簡化 HTTP 請求,同時為現代開發提供一系列功能。 在本文中...
    程式設計 發佈於2024-11-02
  • 使用自然語言透過法學碩士產生簡單的 Python GUI .... 在不到幾分鐘的時間內
    使用自然語言透過法學碩士產生簡單的 Python GUI .... 在不到幾分鐘的時間內
    Thought that building Python GUIs took hours of tedious coding? Welcome to an exciting new era! Not only can tools like Github Copilot help with code ...
    程式設計 發佈於2024-11-02
  • Dev、Oops 和 WEBAPP 故事
    Dev、Oops 和 WEBAPP 故事
    作為 DevOps 專業人員開發桌面 Web 應用程式感覺就像在廣闊而複雜的海洋中航行。隨著技術融合,Web、桌面和基於雲端的應用程式之間的界限變得模糊,迫使 DevOps 深入傳統上由前端佔據的領域 終端開發商。選擇正確的框架變得至關重要,但挑戰往往在於篩選當今可用的眾多選項。例如,Vite、Re...
    程式設計 發佈於2024-11-02
  • 釋放您的 Django 潛力:適合 4 人的專案創意和資源
    釋放您的 Django 潛力:適合 4 人的專案創意和資源
    Django 電子報 - 2024 年 10 月 Django 簡介與專案想法 如果您希望開始使用 Django 或提高自己的技能,請考慮以下一些寶貴的資源和專案想法: Django 專案想法 對於想要嘗試或建立自己的作品集的人來說,Django 提...
    程式設計 發佈於2024-11-02

免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。

Copyright© 2022 湘ICP备2022001581号-3