JavaWeb 项目 --- 在线 OJ 平台 (三)

x33g5p2x  于2022-05-27 转载在 Java  
字(4.9k)|赞(0)|评价(0)|浏览(407)

1. 设计网页页面

1.1 列表页

只是大概模板

1.2 详情页

只是大概模板

2. 设计网页的前后端交互接口

约定交互1: 获取题目的列表

约定交互2: 获取指定题目的详情信息

约定交互3: 向服务器提交编写的代码

3. 服务器的API

由于这些前后交互需要用到 json 格式去传入数据.(可以不用,但是我这里约定了使用这样的一个格式)

3.1 导入 JackSon 库

3.2 创建 ProblemServlet 类

创建 api 包, 在包下创建 这个类
这个类对应的是交互1

package api;

import com.fasterxml.jackson.databind.ObjectMapper;
import dao.Problem;
import dao.ProblemDao;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

@WebServlet("/problem")
public class ProblemServlet extends HttpServlet {
    // 这个 ObjectMapper 是 Jackson 中的一个重要的类
    private final ObjectMapper objectMapper = new ObjectMapper();
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setStatus(200);
        resp.setContentType("application/json;charset=utf8");

        // 获取全部的题目列表
        ProblemDao problemDao = new ProblemDao();
        List<Problem> problems = problemDao.selectAll();

        // 转化成json格式字符串
        String respString = objectMapper.writeValueAsString(problems);
        resp.getWriter().write(respString);
    }
}

3.3 测试 ProblemServlet 类

3.4 创建 DescServlet 类

同样在api包下, 创建 DescServlet 类
这个类对应交互2

package api;

import com.fasterxml.jackson.databind.ObjectMapper;
import dao.Problem;
import dao.ProblemDao;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/desc")
public class DescServlet extends HttpServlet {
    private final ObjectMapper objectMapper = new ObjectMapper();
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setStatus(200);
        resp.setContentType("application/json;charset=utf8");

        // 首先获取到id
        String idString = req.getParameter("id");
        if(idString == null || "".equals(idString)){
            resp.setContentType("text/html;charset=utf-8");
            resp.getWriter().write("<h3>id丢失</h3>");
            return;
        }
        int id = Integer.parseInt(idString);
        // 获取对应id题目的详情页
        ProblemDao problemDao = new ProblemDao();
        Problem problem = problemDao.selectOne(id);
        // 将problem 转成json格式字符串 写回响应
        String respString = objectMapper.writeValueAsString(problem);
        resp.getWriter().write(respString);
    }
}

3.5 测试 DescServlet 类

3.6 创建 ResultServlet 类

同样放到 api 包里.
这个类对应的是交互3

由于这里请求也需要带着这个json格式.所以使用两个静态内部类来表示请求body和响应body的格式.

静态内部类 ResultRequest

/**
     * 结果的请求body格式
     */
    static class ResultRequest{
        public int id;
        public String code;
    }

静态内部类 ResultResponse

/**
     * 结果的响应body格式
     */
    static class ResultResponse{
        // error 0:运行成功 1: 编译出错 2: 运行出错 3:其他错误
        public int error;
        public String reason;
        public String stdout;
    }

实现 doPost

private final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setStatus(200);
        resp.setContentType("application/json;charset=utf8");
        // 1. 读取请求正文, 按照json格式读取
        String body = readBody(req);
        ResultRequest resultRequest = objectMapper.readValue(body, ResultRequest.class);
        // 2. 根据 id 从数据库中查找到题目的测试用例
        ProblemDao problemDao = new ProblemDao();
        Problem problem = problemDao.selectOne(resultRequest.id);
        // 测试用例代码
        String testCode = problem.getTestCode();
        // 提交的代码
        String requestCode = resultRequest.code;
        // 3. 把用户提交的代码和测试用例的代码,给瓶装成一个完整的代码
        String finalCode = mergeCode(requestCode,testCode);
        // 4. 创建一个 Task 实例, 调用里面的 compileAndRun 来进行编译运行
        Task task = new Task();
        Question question = new Question();
        question.setCode(finalCode);
        Answer answer = task.compileAndRun(question);
        // 5. 根据 Task 运行的结果, 包装成一个 Http 响应
        ResultResponse resultResponse = new ResultResponse();
        resultResponse.error = answer.getError();
        resultResponse.reason = answer.getReason();
        resultResponse.stdout = answer.getStdout();

        String respString = objectMapper.writeValueAsString(resultResponse);
        resp.getWriter().write(respString);
    }

实现 readBody 方法

/**
     * 读取请求的正文
     * @param req 请求
     * @return 以字符串形式返回
     * @throws UnsupportedEncodingException 字符编码不支持的异常
     */
    private String readBody(HttpServletRequest req) throws UnsupportedEncodingException {
        // 1. 根据 Content-Length 获取 body 中的长度(单位是字节)
        int contentLength = req.getContentLength();
        // 2. 按照找个长度准备一个 byte[]
        byte[] buffer = new byte[contentLength];
        // 3. 通过 req 里面的 getInputStream 方法, 获取body的流对象(body比较长)
        try (InputStream inputStream = req.getInputStream()){
            // 4. 基于这个流对象,读取内容,将读取到内容放到byte[]数组中
            inputStream.read(buffer);
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 5. 把这个 byte[] 的内容构造成一个 String
        return new String(buffer,"utf8");
    }

实现 mergeCode 方法

/**
     * 测试用例代码和提交的代码合并
     * @param requestCode 提交的代码
     * @param testCode 测试用例代码
     * @return 返回合并后的代码
     */
    private String mergeCode(String requestCode, String testCode) {
        // 找到最后一个"}"的位置
        int index =requestCode.lastIndexOf("}");
        if (index == -1){
            // 这里是不存在的情况
            return null;
        }
        // 截取前面的代码
        String curCode = requestCode.substring(0,index);
        // 拼接代码
        return curCode + testCode + "\n}";
    }

3.7 测试 ResultServlet 类

打开postman应用
如果不会使用postman 可以看 博文 Postman的使用

3.8 这里发现 如果 id不存在 或者 code为空的时候出现服务器错误.

更改代码

并在 comon包中 创建这两个类继承 Exception

当catch到异常的时候, 设置状态码为3

3.9 再次对 ResultServlet 进行测试

相关文章