Exemple de code API servlet : mur de confession de la version du serveur

1. Mur de confession de la version serveur

Le mur de confession mis en œuvre enregistre les données du message ici via le serveur , permettant ainsi un stockage « persistant » ~~

1. Déterminez l'interface interactive

Lors de la mise en œuvre de ce programme, vous devez d'abord réfléchir à la manière d'interagir entre le client et le serveur~

Convenez de l'interface pour l'interaction front-end et back-end~~

(Protocole de couche application personnalisé)
Puisqu'il s'agit d'un serveur, le serveur doit fournir certains services. Quels types de services sont fournis spécifiquement ? Et comment ces services doivent-ils être déclenchés ? Tous doivent être examinés attentivement ~~

Pour les murs de confession, il existe deux interfaces principales :

1) Indiquez au serveur quel type de données contient le message actuel ~~ (Lorsque l'utilisateur clique sur le bouton Soumettre , une requête HTTP sera envoyée au serveur pour lui permettre d'enregistrer le message)

Il est convenu que pour obtenir cet effet, le client doit envoyer quel type de requête HTTP et le serveur doit renvoyer quel type de réponse HTTP ~

demander:

POST /message
    
{
    
    
    from: "黑猫",
    to: "白猫",
    message: "喵"
}
POST /messageWall
    
from=黑猫&to=白猫&message=
GET /message?from=黑猫&to=白猫&message=

D'après les idées ci-dessus, il existe d'innombrables façons de se mettre d'accord sur le format de la requête !!!La méthode peut être modifiée, le chemin peut être modifié et le nom du paramètre peut également être modifié...

Il y a tellement de façons de se mettre d'accord, laquelle adopter ?
N'importe laquelle est bonne !!! Tant que vous pouvez en identifier une et l'implémenter strictement lors de l'écriture de code front-end/back-end , c'est ok !!

C'est exactement le sens de notre accord sur les interfaces front-end et back-end !!!

Protocole personnalisé !! (Cela restreint exactement la liberté des programmeurs et ne peut pas écrire de code aléatoire)

Dans notre futur travail de développement actuel, en particulier lorsque deux programmeurs, le front-end et le back-end, travaillent ensemble pour implémenter une fonction, ce comportement consistant à s'entendre sur les interfaces interactives front-end et back-end est crucial. fait avant que l'action de développement ne soit entreprise. Quelque chose à faire ~~

Nous acceptons d'utiliser :

demander:

POST /message
    
{
    
    
    from: "黑猫",
    to: "白猫",
    message: "喵"
}

réponse:

HTTP/1.1 200 OK
    
{
    
    
    ok: true
}

2) Obtenez du serveur quelles données de messages sont actuellement disponibles ~~ (Lorsque la page est chargée, vous devez obtenir le contenu de ces messages qui ont été stockés sur le serveur)

demander:

GET /message

Réponse : tableau au format JSON, chaque élément est un objet JSON

HTTP/1.1 200 OK
Content-Type: application/json
    
{
    
    
    {
    
    
        from: "黑猫",
        to: "白猫",
        message: "喵"
    },
    {
    
    
        from: "黑猫",
        to: "白猫",
        message: "喵"
    },
    {
    
    
        from: "黑猫",
        to: "白猫",
        message: "喵"
    }
}

2. Code back-end

  • Après avoir déterminé l'interface, vous pouvez écrire le code
  • Vous devez écrire du code back-end et front-end
  • (Dans le travail réel, deux personnes sont souvent responsables du back-end et du front-end, et elles développent en parallèle) - Débogage conjoint

Développement back-end :

Faites d’abord les préparatifs

  1. Créer un projet (maven)
  2. Introduire des dépendances (Servlet, Jackson)
	<dependencies>
        <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.12.6.1</version>
        </dependency>
    </dependencies>
  1. Créer un répertoire

  2. Écrire du code

// 和前面约定的 前后端交互接口 相匹配
@WebServlet("/message")
public class MessageServlet extends HttpServlet {
    
    
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        // 处理提交信息请求
        resp.getWriter().write("hello post");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        // 获取到消息列表
        resp.getWriter().write("hello get");
    }
}
    1. Packaging et déploiement (à l'aide de Smart Tomcat)

Insérer la description de l'image ici

  1. vérifier

Insérer la description de l'image ici

Code complet :

class Message {
    
    
    public String from;
    public String to;
    public String message;
}

// 和前面约定的 前后端交互接口 相匹配
@WebServlet("/message")
public class MessageServlet extends HttpServlet {
    
    
    private ObjectMapper objectMapper = new ObjectMapper();

    private List<Message> messages = new ArrayList<>();

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        // 处理提交信息请求
        Message message = objectMapper.readValue(req.getInputStream(), Message.class);
        // 最简单的保存放法就是保存到内存中
        messages.add(message);
        // 通过 ContentType 告知页面,返回的数据是 json 格式
        // 有了这样的声明,此时 jquery ajax 就会自动帮我们把字符串转成 js 对象
        // 如果没有,jquery ajax 只会当成字符串来处理
        resp.setContentType("application/json; charset=utf8");
        resp.getWriter().write("{ \"ok\": true }");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        // 获取到消息列表 将消息列表的内容整个的都返回给客户端即可
        // 此处需要使用 ObjectMapper 把 Java 对象,转成 JSON 格式字符串
        String jsonString = objectMapper.writeValueAsString(messages);
        System.out.println("jsonString: " + jsonString);
        resp.setContentType("application/json; charset=utf8");
        resp.getWriter().write(jsonString);
    }
}

Démarrez le serveur et utilisez Postman pour construire la requête

OBTENIR:
Insérer la description de l'image ici

POSTE:

Insérer la description de l'image ici

Construisez PSOT trois fois, puis revenez à GET :

Insérer la description de l'image ici


3. Code frontal

Maintenant que la logique back-end a été écrite, écrivons le code front-end~~
Vous pouvez copier les pages que vous avez écrites plus tôt.

Sur la base du code précédent, ajoutez l'opération ajax ~

Insérer la description de l'image ici

let body = {
    
    
	"from": from,
	to: to,
	message: msg
};
{
    
    
    "from": "黑猫",
    "to": "白猫",
    "message": "喵"
},

En JSON , la clé doit être entre guillemets , mais en JS , la clé dans l'objet peut être entre guillemets ou non.

Avec ou sans, tout est de type chaîne ~~
(Normalement, c'est obligatoire, mais js vous permet d'omettre les guillemets afin de faciliter l'écriture pour tout le monde)

S'il y a des symboles spéciaux dans la clé , tels que des espaces, ce type de ~~ doit être cité à ce moment-là.

Conversion entre objets et chaînes JSON :

  • Java:
    • objectMapper.readValue convertit la chaîne json en objet
    • objectMapper.writeValueAsString convertit les objets en chaînes json
  • JS:
    • JSON.parse convertit la chaîne json en objet
    • JSON.stringify convertit les objets en chaînes json

Code complet :

<style>
    * {
      
      
        margin: 0;
        padding: 0;
        box-sizing: border-box;
    }

    .container {
      
      
        width: 100%;
    }

    h3 {
      
      
        text-align: center;
        padding: 30px 0; /* 上下内边距 20,左右为 0 */
        font-size: 24px;
    }

    p {
      
      
        text-align: center;
        color: #999;
        padding: 10px 0;
    }

    .row {
      
      
        width: 400px;
        height: 50px;
        margin: 0 auto;

        display: flex;
        justify-content: center;
        align-items: center;
    }

    .row span {
      
      
        width: 60px;
        font-size: 20px;
    }

    .row input {
      
      
        width: 300px;
        height: 40px;
        line-height: 40px;
        font-size: 20px;
        text-indent: 0.5em;
        /* 去掉输入框的轮廓线 */
        outline: none;
    }

    .row #submit {
      
      
        width: 200px;
        height: 40px;
        font-size: 20px;
        line-height: 40px;
        margin: 0 auto;
        color: white;
        background-color: orange;
        /* 去掉边框 */
        border: none;
        border-radius: 10px;
    }

    /* 按下的效果 */
    .row #submit:active {
      
      
        background-color: grey;
    }
</style>

<div class="container">
    <h3>表白墙</h3>
    <p>输入后点击提示,会将信息显示在表格中</p>
    <div class="row">
        <span>谁:</span>
        <input type="text">
    </div>
    <div class="row">
        <span>对谁:</span>
        <input type="text">
    </div>
    <div class="row">
        <span>说:</span>
        <input type="text">
    </div>
    <div class="row">
        <button id="submit">提交</button>        
    </div>
</div>

<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>

<script>
    // 加入 ajax 的代码,此处要加入的逻辑有两个部分
    // 点击按钮提交的时候,ajax 要构造数据发送给服务器
    // 页面加载的时候,从服务器获取消息列表,并在界面上直接显示

    function getMessages() {
      
      
        $.ajax({
      
      
            type: 'get',
            url: "message",
            success: function(body) {
      
      
                // 当前 body 已是一个 js 对象数组了,ajax 会根据响应的 content type 来自动进行解析
                // 如果服务器返回的 content-type 已经是 application/json 了,ajax 就会把 body 自动转成 js 对象
                // 如果客户端没有自动转,也可以通过 JSON.parse() 这个函数手动转换
                let container = document.querySelector('.container');
                for (let message of body) {
      
      
                    let div = document.createElement('div');
                    div.innerHTML = message.from + ' 对 ' + message.to + ' 说 ' + message.message;
                    div.className = 'row'; // 应用 row 的样式
                    container.appendChild(div);
                }
            }
        });
    }

    // 加上函数调用
    getMessages();


    // 当用户点击 submit,就会获取 input 中的内容,把内容构造成一个 div,插入页面末尾
    let submitBtn = document.querySelector('#submit');
    submitBtn.onclick = function() {
      
      
        // 1、获取 2 个 input
        let inputs = document.querySelectorAll('input');
        let from = inputs[0].value;
        let to = inputs[1].value;
        let msg = inputs[2].value;
        if (from == '' || to == '' || msg == '') {
      
       // 用户还未填写完毕
            return;
        }
        // 2、生成一个新的 div,内容就是 input 中的内容,新的 div 加到页面中
        let div = document.createElement('div');
        div.innerHTML = from + ' 对 ' + to + ' 说 ' + msg;
        div.className = 'row'; // 应用 row 的样式
        let container = document.querySelector('.container');
        container.appendChild(div);
        // 3、清空之前输入框的内容
        for (let i = 0; i < inputs.length; i++) {
      
      
            inputs[i].value = '';
        }

        // 4、把当前获取到的输入框的内容,构造成一个 HTTP POST 请求,通过 ajax 发给服务器
        let body = {
      
      
            "from": from, // 在 JSON 中,key 要带引号,但是在 JS 中,对象这里的 key 可以带引号,也可以不带
            to: to,
            message: msg
        };
        $.ajax({
      
      
            type: "post",
            url: "message",
            contentType: "application/json;charset=utf8",
            data: JSON.stringify(body),
            success: function(body) {
      
      
                alert("消息提交成功!");
            },
            error: function() {
      
      
                alert("消息提交失败!");
            }
        });
    }
</script>

Visite : 127.0.0.1:8080/message_wall/messageWall.html

Capturez le paquet pour voir la réponse :

HTTP/1.1 200
Content-Type: application/json;charset=utf8
Content-Length: 2
Date: Sun, 22 May 2022 12:00:17 GMT
Keep-Alive: timeout=20
Connection: keep-alive

[]

Lorsque la page est chargée, vous pouvez voir qu'il y a une requête ajax. Cette requête consiste à obtenir la liste des messages du serveur ~~Vous pouvez voir que
la réponse ici est vide !!!
Ne venez-vous pas de soumettre trois messages à le serveur ?Enregistrer ??

Insérer la description de l'image ici

Le code vient d'enregistrer le message dans cette liste (la liste est la mémoire ). Une fois le programme redémarré , les données dans la mémoire seront perdues ~~

Par conséquent, la logique ci-dessus ne peut garantir que les données ne seront pas perdues après l'actualisation de la page , mais elle ne peut pas garantir que les données ne seront pas perdues après le redémarrage du serveur ~~
La probabilité d'actualisation de la page doit être plus grande !!

Bien sûr, si vous souhaitez résoudre ce problème de manière plus approfondie, vous devez enregistrer les données sur le disque dur (il peut s'agir d'un fichier ou d'une base de données).

Entrez la soumission :

Insérer la description de l'image ici

Il peut en effet être prouvé que même si la page est fermée et rouverte, les messages précédemment soumis ne seront pas perdus ~~ (Lorsque la page est chargée, la liste des messages est obtenue du serveur et affichée dans la page du navigateur)


4. Enregistrez les données dans un fichier

En réponse au problème ci-dessus, si les données sont enregistrées dans un fichier, les données ne seront pas perdues si le serveur est redémarré.

Modifiez le code MessageServlet :

  • Supprimez le membre des messages précédents.
  • Créez un nouveau membre String filePath , qui représente le chemin du fichier à stocker.
  • Ajout d'une nouvelle méthode de chargement pour lire le contenu du fichier. (load sera appelé lorsque la page sera chargée)
  • Une nouvelle méthode de sauvegarde est ajoutée pour écrire le contenu dans le fichier. (la sauvegarde sera appelée lors de la soumission d'un message)
  • Le format de fichier est stocké sous forme de ligne de texte. Chaque enregistrement occupe une ligne et les champs (de, à, message) de chaque enregistrement sont séparés par \t.

Le format du fichier est le suivant :

Insérer la description de l'image ici

@WebServlet("/message")
public class MessageServlet extends HttpServlet {
    
    
	// 用于保存所有的留言
    // private List<Message> messages = new ArrayList<Message>();
    // 用于转换 JSON 字符串
    private ObjectMapper objectMapper = new ObjectMapper();
    // 数据文件的路径
    private String filePath = "d:/messages.txt";
    
    public List<Message> load() {
    
    
        List<Message> messages = new ArrayList<>();
        System.out.println("从文件读取数据");
        try (BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath))) {
    
    
            while (true) {
    
    
                String line = bufferedReader.readLine();
                    if (line == null) {
    
    
                    break;
                }
                String[] tokens = line.split("\t");
                Message message = new Message();
                message.from = tokens[0];
                message.to = tokens[1];
                message.message = tokens[2];
                messages.add(message);
            }
		} catch (IOException e) {
    
    
            // 首次运行的时候文件不存在, 可能会在这里触发异常.
            e.printStackTrace();
        }
        System.out.println("共读取数据 " + messages.size() + " 条!");
        return messages;
    }
    
    public void save(Message message) {
    
    
        System.out.println("向文件写入数据");
        // 使用追加写的方式打开文件
        try (FileWriter fileWriter = new FileWriter(filePath, true)) {
    
    
            fileWriter.write(message.from + "\t" + message.to + "\t" +
            message.message + "\n");
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }
    
    // 获取所有留言
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        List<Message> messages = load();
        resp.setContentType("application/json;charset=utf-8");
        String respString = objectMapper.writeValueAsString(messages);
        resp.getWriter().write(respString);
    }
    
    // 新增留言
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        resp.setContentType("application/json;charset=utf-8");
        Message message = objectMapper.readValue(req.getInputStream(), Message.class);
        save(message);
        resp.getWriter().write("{ \"ok\": 1 }");
    }
}

5. Stocker les données dans la base de données

Il est possible d'utiliser des fichiers pour stocker des messages, mais ce n'est pas élégant.
On peut également utiliser une base de données pour compléter le travail de stockage.

5.1.Introduire la dépendance mysql

    <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>

5.2. Réécrire le code MessageServlet

class Message {
    
    
    public String from;
    public String to;
    public String message;
}

// 和前面约定的 前后端交互接口 相匹配
@WebServlet("/message")
public class MessageServlet extends HttpServlet {
    
    
    private ObjectMapper objectMapper = new ObjectMapper();

//    private List<Message> messages = new ArrayList<>();
//    改成数据库,不需要整个变量

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        // 处理提交信息请求
        Message message = objectMapper.readValue(req.getInputStream(), Message.class);
//        // 最简单的保存放法就是保存到内存中
//        messages.add(message);
        // 通过 ContentType 告知页面,返回的数据是 json 格式
        // 有了这样的声明,此时 jquery ajax 就会自动帮我们把字符串转成 js 对象
        // 如果没有,jquery ajax 只会当成字符串来处理
        save(message);
        resp.setContentType("application/json; charset=utf8");
        resp.getWriter().write("{ \"ok\": true }");
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
    
        // 获取到消息列表 将消息列表的内容整个的都返回给客户端即可
        // 此处需要使用 ObjectMapper 把 Java 对象,转成 JSON 格式字符串
        List<Message> messages = load();
        String jsonString = objectMapper.writeValueAsString(messages);
        System.out.println("jsonString: " + jsonString);
        resp.setContentType("application/json; charset=utf8");
        resp.getWriter().write(jsonString);
    }

    private void save(Message message) {
    
    
        // 把一条数据保存到数据库中
    }

    private List<Message> load() {
    
    
        // 从数据库中获取所有数据
    }
}

5.3. Créer une classe DBUtil

Processus de base d'utilisation de JDBC :

  1. Créer une source de données
  2. Établir une connexion avec la base de données
  3. Construire une instruction SQL
  4. Exécuter l'instruction SQL
  5. S'il s'agit d'une instruction de requête, il est nécessaire de parcourir le jeu de résultats. S'il s'agit d'une insertion/suppression/modification, ce n'est pas obligatoire.
  6. Fermez la connexion et libérez les ressources

Créer une base de données :

create table message(`from` varchar(1024), `to` varchar(1024), message varchar(4096));

from est un mot-clé en SQL. Lorsque le mot-clé est utilisé comme nom de table/nom de colonne, vous devez ajouter un backtick ` (la touche dans le coin supérieur gauche du clavier)

Le getConnection actuel sera-t-il exécuté dans un environnement multithread ??

  • getConnection sera appelé dans doGet / doPost~~
  • Les multiples méthodes doXXX sont-elles déclenchées par plusieurs requêtes appelées dans un environnement multithread ?
    Oui !!!

Insérer la description de l'image ici

public class DBUtil {
    
    
    private static final String URL = "jdbc:mysql://127.0.0.1:3306/java102?characterEncoding=utf8&useSSL=false";
    private static final String USERNAME = "root";
    private static final String PASSWORD = "11111";

    private volatile static DataSource dataSource = null; // volatile

    private static DataSource getDataSource() {
    
    
        // 多线程安全
        if (dataSource == null) {
    
    
            synchronized (DnsUrl.class) {
    
    
                if (dataSource == null) {
    
     // 懒汉模式
                    dataSource = new MysqlDataSource();
                    ((MysqlDataSource) dataSource).setURL(URL);
                    ((MysqlDataSource) dataSource).setUser(USERNAME);
                    ((MysqlDataSource) dataSource).setPassword(PASSWORD);
                }
            }
        }
        return dataSource;
    }

    // 代码和数据库服务器建立连接 import java.sql.Connection;
    public static Connection getConnection() throws SQLException {
    
    
        return getDataSource().getConnection();
    }

    // 释放资源
    public static void close(Connection connection, PreparedStatement statement, ResultSet resultSet) {
    
    
        if (resultSet != null) {
    
    
            try {
    
    
                resultSet.close();
            } catch (SQLException e) {
    
    
                e.printStackTrace();
            }
        }
        if (statement != null) {
    
    
            try {
    
    
                statement.close();
            } catch (SQLException e) {
    
    
                e.printStackTrace();
            }
        }
        if (connection != null) {
    
    
            try {
    
    
                connection.close();
            } catch (SQLException e) {
    
    
                e.printStackTrace();
            }
        }
    }
}

5.4.Méthode Sava

    private void save(Message message) {
    
    
        // 把一条数据保存到数据库中
        Connection connection = null;
        PreparedStatement statement = null;
        try {
    
    
            // 1、和数据库建立连接
            connection = DBUtil.getConnection();
            // 2、构造 SQL
            String sql = "insert into message values(?, ?, ?)";
            statement = connection.prepareStatement(sql);
            statement.setString(1, message.from);
            statement.setString(2, message.to);
            statement.setString(3, message.message);
            // 3、指向 SQL
            statement.executeUpdate();
        } catch (SQLException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            DBUtil.close(connection, statement, null);
        }
    }

5.5. méthode de chargement

    private List<Message> load() {
    
    
        // 从数据库中获取所有数据
        List<Message> messages = new ArrayList<>();
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
    
    
            connection = DBUtil.getConnection();
            String sql = "select * from message";
            statement = connection.prepareStatement(sql);
            resultSet = statement.executeQuery();
            while (resultSet.next()) {
    
    
                Message message = new Message();
                message.from = resultSet.getString("from");
                message.to = resultSet.getString("to");
                message.message = resultSet.getString("message");
                messages.add(message);
            }
        } catch (SQLException throwables) {
    
    
            throwables.printStackTrace();
        } finally {
    
    
            DBUtil.close(connection, statement, resultSet);
        }
        return messages;
    }

Commencez, visitez : 127.0.0.1:8080/message_wall/messageWall.html

Insérer la description de l'image ici

MySQL:

mysql> select * from message;
+--------------+-----------+------------+
| from         | to        | message    |
+--------------+-----------+------------+
| 海棉宝宝     | 章鱼哥    | 早上好~    |
+--------------+-----------+------------+

5.6. Résumé de MVC

Étapes de base pour développer un mur de confession (un simple site Web) :

  1. Convenez de l'interface pour l'interaction front-end et back-end ~ (quelle est la demande, quelle est la réponse)

  2. Développer le code du serveur

    • Écrivez d’abord un servlet capable de gérer les requêtes du front-end.
    • Écrire le code de la base de données pour stocker/récupérer les données clés
  3. Développer le code client

    • Capable de construire des requêtes et d'analyser des réponses basées sur ajax
    • Capable de répondre aux opérations de l'utilisateur (après avoir cliqué sur le bouton, déclencher le comportement d'envoi d'une requête au serveur)

La plupart de ces programmes « sites Web » sont dans une situation similaire~~

MVC:

  • Contrôleur (contrôleur, clé logique après traitement de la demande)
  • Modèle (logique d'accès aux données d'exploitation)
  • Vue (interface affichée à l'utilisateur)

Vue —— Contrôleur —— Modèle


Je suppose que tu aimes

Origine blog.csdn.net/qq_56884023/article/details/125785439
conseillé
Classement