Ejemplo de código API de servlet: muro de confesión de la versión del servidor

1. Muro de confesión de la versión del servidor

El muro de confesión implementado guarda los datos del mensaje aquí a través del servidor , logrando así un almacenamiento "persistente" ~~

1. Determinar la interfaz interactiva.

Al implementar este programa, primero debe considerar cómo interactuar entre el cliente y el servidor ~

Acuerde la interfaz para la interacción front-end y back-end ~~

(Protocolo de capa de aplicación personalizado)
Dado que es un servidor, el servidor debe proporcionar algunos servicios. ¿Qué tipo de servicios se proporcionan específicamente? ¿Y cómo se deben activar estos servicios? Todo debe considerarse cuidadosamente ~~

Para los muros de confesión, hay dos interfaces principales:

1) Dígale al servidor qué tipo de datos es el mensaje actual ~~ (Cuando el usuario hace clic en el botón enviar , se enviará una solicitud HTTP al servidor para permitirle guardar el mensaje)

Se acuerda que para lograr este efecto, el cliente debe enviar qué tipo de solicitud HTTP y el servidor debe devolver qué tipo de respuesta HTTP ~

preguntar:

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

De acuerdo con las ideas anteriores, ¡hay innumerables formas de acordar el formato de solicitud! Se puede cambiar el método, se puede cambiar la ruta y también se puede cambiar el nombre del parámetro...

Hay tantas formas de ponerse de acuerdo, ¿cuál adoptar? ¡
Cualquiera está bien! ¡Siempre que puedas identificar una de ellas e implementarla estrictamente al escribir el código front-end/back-end , está bien!

¡¡¡Éste es exactamente el significado de nuestro acuerdo sobre las interfaces front-end y back-end!!!

¡Protocolo personalizado! (Esto restringe exactamente la libertad de los programadores y no puede escribir código aleatorio)

En nuestro futuro trabajo de desarrollo real, especialmente cuando dos programadores, el front-end y el back-end, trabajan juntos para implementar una función, este comportamiento de acordar las interfaces interactivas del front-end y el back-end es crucial. debe hacerse antes de que se tomen las medidas de desarrollo. Algo por hacer~~

Aceptamos utilizar:

preguntar:

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

respuesta:

HTTP/1.1 200 OK
    
{
    
    
    ok: true
}

2) Obtenga del servidor qué datos de mensajes están disponibles actualmente ~~ (Cuando se carga la página, debe obtener el contenido de estos mensajes que se han almacenado en el servidor)

preguntar:

GET /message

Respuesta: matriz en formato JSON, cada elemento es un objeto JSON

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

2. Código de fondo

  • Después de determinar la interfaz, puede escribir el código.
  • Necesita escribir código tanto de back-end como de front-end
  • (En el trabajo real, dos personas suelen ser responsables del back-end y del front-end y se desarrollan en paralelo) -Depuración conjunta

Desarrollo de back-end:

Primero haz los preparativos

  1. Crear proyecto (maven)
  2. Introducir dependencias (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. Crear un directorio

  2. escribir codigo

// 和前面约定的 前后端交互接口 相匹配
@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. Empaquetado e implementación (usando Smart Tomcat)

Insertar descripción de la imagen aquí

  1. verificar

Insertar descripción de la imagen aquí

Código completo:

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

Inicie el servidor y use Postman para construir la solicitud.

CONSEGUIR:
Insertar descripción de la imagen aquí

CORREO:

Insertar descripción de la imagen aquí

Construya PSOT tres veces y luego regrese a GET:

Insertar descripción de la imagen aquí


3. Código de interfaz

Ahora que se ha escrito la lógica del back-end, escribamos el código del front-end ~~
Puede copiar las páginas que escribió anteriormente.

Según el código anterior, agregue la operación ajax ~

Insertar descripción de la imagen aquí

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

En JSON , la clave debe estar entre comillas , pero en JS , la clave del objeto puede estar entre comillas o no.

Con o sin él, todo es tipo cadena~~
(Normalmente, es obligatorio, pero js le permite omitir las comillas para que sea más fácil de escribir para todos)

Si hay algunos símbolos especiales en la clave , como espacios, este tipo de ~~ debe citarse en este momento.

Conversión entre objetos y cadenas JSON:

  • Java:
    • objectMapper.readValue convierte una cadena json en un objeto
    • objectMapper.writeValueAsString convierte objetos en cadenas json
  • JS:
    • JSON.parse convierte una cadena json en un objeto
    • JSON.stringify convierte objetos en cadenas json

Código completo:

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

Visita: 127.0.0.1:8080/message_wall/messageWall.html

Capture el paquete para ver la respuesta:

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

[]

Cuando se carga la página, puedes ver que hay una solicitud ajax. Esta solicitud es para obtener la lista de mensajes del servidor ~~ ¡¡¡Puedes ver que
la respuesta aquí está vacía!!!
¿No acabas de enviar tres mensajes a el servidor?grabar??

Insertar descripción de la imagen aquí

El código acaba de guardar el mensaje en esta Lista (La Lista es memoria ). Una vez que se reinicia el programa , los datos en la memoria se perderán ~~

Por lo tanto, la lógica anterior solo puede garantizar que los datos no se perderán después de que se actualice la página , pero no puede garantizar que los datos no se perderán después de que se reinicie el servidor ~~ ¡
La probabilidad de que la página se actualice debe ser mayor!

Por supuesto, si desea solucionar este problema más a fondo, debe guardar los datos en el disco duro (puede ser un archivo o una base de datos)

Ingrese el envío:

Insertar descripción de la imagen aquí

De hecho, se puede demostrar que incluso si la página se cierra y se vuelve a abrir, los mensajes enviados anteriormente no se perderán ~~ (Cuando se carga la página, la lista de mensajes se obtiene del servidor y se muestra en la página del navegador)


4. Guardar datos en un archivo

En respuesta al problema anterior, si los datos se guardan en un archivo, no se perderán si se reinicia el servidor.

Modifique el código de MessageServlet:

  • Eliminar el miembro de mensajes anteriores.
  • Cree un nuevo miembro String filePath , que representa la ruta del archivo que se almacenará.
  • Se agregó un nuevo método de carga para leer el contenido del archivo (se llamará a la carga cuando se cargue la página).
  • Se agrega un nuevo método de guardar para escribir contenido en el archivo (se llamará a guardar al enviar un mensaje).
  • El formato del archivo se almacena como texto de línea. Cada registro ocupa una línea y los campos (desde, hasta, mensaje) de cada registro están separados por \t

El formato del archivo es el siguiente:

Insertar descripción de la imagen aquí

@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. Almacenar datos en la base de datos.

Es posible utilizar archivos para almacenar mensajes, pero no es elegante,
también podemos utilizar una base de datos para completar el trabajo de almacenamiento.

5.1 Introducir la dependencia de 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 Reescribir el código del 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 Crear clase DBUtil

Proceso básico de uso de JDBC:

  1. Crear fuente de datos
  2. Establecer una conexión con la base de datos.
  3. Construir declaración sql
  4. Ejecutar sentencia sql
  5. Si es una declaración de consulta, es necesario recorrer el conjunto de resultados, si es inserción/eliminación/modificación, no es necesario.
  6. Cerrar la conexión y liberar recursos.

Crear base de datos:

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

from es una palabra clave en SQL. Cuando la palabra clave se usa como nombre de tabla/nombre de columna, debe agregar una tilde invertida ` (la tecla en la esquina superior izquierda del teclado)

¿Se ejecutará el getConnection actual en un entorno multiproceso?

  • getConnection se llamará en doGet / doPost~~

  • ¿ Los múltiples métodos doXXX se activan mediante múltiples solicitudes llamadas en un entorno de subprocesos múltiples?

Insertar descripción de la imagen aquí

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étodo 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étodo de carga

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

Comience, visite: 127.0.0.1:8080/message_wall/messageWall.html

Insertar descripción de la imagen aquí

MySQL:

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

5.6 Resumen de MVC

Pasos básicos para desarrollar un muro de confesiones (un sitio web sencillo):

  1. Acuerde la interfaz para la interacción front-end y back-end ~ (cuál es la solicitud, cuál es la respuesta)

  2. Desarrollar código de servidor

    • Primero escriba un servlet que pueda manejar solicitudes desde el front-end.
    • Escribir código de base de datos para almacenar/recuperar datos clave
  3. Desarrollar código de cliente

    • Capaz de construir solicitudes y analizar respuestas basadas en ajax.
    • Capaz de responder a las operaciones del usuario (después de hacer clic en el botón, activa el comportamiento de enviar una solicitud al servidor)

La mayoría de estos programas de "sitios web" se encuentran en una situación similar ~~

MVC:

  • Controlador (controlador, lógica clave después de procesar la solicitud)
  • Modelo (lógica para el acceso a datos operativos)
  • Ver (interfaz mostrada al usuario)

Vista —— Controlador —— Modelo


Supongo que te gusta

Origin blog.csdn.net/qq_56884023/article/details/125785439
Recomendado
Clasificación