Oj版本一开发过程

https://github.com/strangersinsist/onlineJudge
半成品,后续填坑(一定)

1 测评

  • 代码沙箱(自主实现/部署开源项目)
  • 判题 api judge0 api 选用,最初使用 python 测试运行
  • 用 ai 做代码沙箱,理论上可以但是对于复杂的题目来说可能准确性不高,失去作为 OJ 的意义
  • 移花接木,可以通过操作模拟浏览器的方式,用别人的 OJ 帮助判题,在别人的项目中提交代码并获取结果。比如 vjudge

2 问题

遇到的报错 :

1
{code: '111', input: '222'} Analysis_A.vue:88 POST http://localhost:8080/api/runCode 404 (Not Found) request.js:55 errAxiosError: Request failed with status code 404

终端

1
Received code: 111 Received input: 222 

但是前端

1
Payload to be sent: {code: '111', input: '222'}code: "111"input: "222"[[Prototype]]: Object Analysis_A.vue: 90 Response from backend: undefined

处理 :返回一个 JSON 格式的响应

遇到了问题,前端的 response.data 仍然是 undefined,尽管后端已经正确返回了数据。问题很可能是出现在 Axios 自动解析响应体的过程中,也可能是后端的返回内容格式化不正确。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@CrossOrigin(origins = "http://localhost:8080")  
@RestController
public class CodeExecutionController {

@PostMapping("/api/runCode")
public ResponseEntity<CodeExecutionResponse> runCode(@RequestBody CodeExecutionRequest request) {
String code = request.getCode();
String input = request.getInput();

String output = "Code:\n" + code + "\n\nInput:\n" + input + "\n\nRepeated:\n" + code + "\n\n" + input;


return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(new CodeExecutionResponse(output));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
runCode() {  
console.log("Running code with input:", this.testCase);
const payload = {
code: this.codeInput,
input: this.testCase
};

console.log("Payload to be sent:", payload);

request.post("http://localhost:9091/api/runCode", payload, { responseType: 'json' })
.then(response => {
console.log("Full response from backend:", response); // 打印完整的响应
console.log("Response data:", response.data); // 打印response.data

if (response.data && response.data.output) {
this.testOutput = response.data.output;
console.log("Output:", this.testOutput);
} else {
console.error("Output is missing in the response", response.data);
}
})
.catch(error => {
console.error("Error running code:", error);
});


},

解决:
检查完整的 response 对象结构
从前面的日志看响应中存在数据,进一步打印 response 的完整结构,看看 response.data 是否被放在了其他位置。
修改代码,检查 response 对象的所有属性,而不仅仅是 response.data

3 调用 api

在 pom. xml 中增加AsyncHttpClientJSON 解析库依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
<dependencies>
<dependency>
<groupId>org.asynchttpclient</groupId>
<artifactId>async-http-client</artifactId>
<version>2.12.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.3</version>
</dependency>
</dependencies>

CodeExecutionController:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package com.example.demo.controller;  

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.asynchttpclient.*;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.util.Base64;
import java.util.concurrent.CompletableFuture;

class CodeExecutionRequest {
private String code;
private String input;


public String getCode() {
return code;
}

public void setCode(String code) {
this.code = code;
}

public String getInput() {
return input;
}

public void setInput(String input) {
this.input = input;
}
}

class CodeExecutionResponse {
private String output;

public CodeExecutionResponse(String output) {
this.output = output;
}

public String getOutput() {
return output;
}

public void setOutput(String output) {
this.output = output;
}
}

@CrossOrigin(origins = "http://localhost:8080")
@RestController
public class CodeExecutionController {

private static final String API_HOST = "judge0-ce.p.rapidapi.com";
private static final String API_KEY = "a3adbed817msh3baa8ad5c76fa09p137a87jsn636cf96b2a42";

@PostMapping("/api/runCode")
public CompletableFuture<ResponseEntity<CodeExecutionResponse>> runCode(@RequestBody CodeExecutionRequest request) {
String code = request.getCode();
String input = request.getInput();

// 将代码和输入进行 Base64 编码
String encodedCode = Base64.getEncoder().encodeToString(code.getBytes());
String encodedInput = Base64.getEncoder().encodeToString(input.getBytes());

String payload = String.format(
"{\"language_id\": 52, \"source_code\": \"%s\", \"stdin\": \"%s\"}",
encodedCode, encodedInput
);

// 创建 AsyncHttpClient 实例
AsyncHttpClient client = Dsl.asyncHttpClient();

Request apiRequest = Dsl.post("https://" + API_HOST + "/submissions?base64_encoded=true&wait=true&fields=*")
.setHeader("x-rapidapi-key", API_KEY)
.setHeader("x-rapidapi-host", API_HOST)
.setHeader("Content-Type", "application/json")
.setBody(payload)
.build();

// 处理 API 的响应
return client.executeRequest(apiRequest).toCompletableFuture().thenApply(response -> {
try {
// 解析 JSON 响应
ObjectMapper mapper = new ObjectMapper();
JsonNode jsonNode = mapper.readTree(response.getResponseBody());

// 解码 stdout 的 Base64 编码
String stdout = "";
if (jsonNode.has("stdout") && !jsonNode.get("stdout").isNull()) {
String stdoutBase64 = jsonNode.get("stdout").asText();
stdout = new String(Base64.getDecoder().decode(stdoutBase64));
} else {
stdout = "No output received.";
}

// 关闭客户端
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}

// 返回响应给前端
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(new CodeExecutionResponse(stdout));
} catch (Exception e) {
e.printStackTrace();
try {
client.close();
} catch (IOException ioException) {
ioException.printStackTrace();
}
return ResponseEntity.status(500).body(new CodeExecutionResponse("Error processing the request."));
}
});
}

}

4 提交如何测评?

手动给出一些输入样例+自动生成输入样例(用程序(上传题目时出题人要给出数据生成代码)或者 AI)
判题:将输入样例同时传入标准答案和用户答案,判断两者是否相同。
huanghongxun/Judger: Judge system for Sicily Online Judge, VMatrix Course/OJ, GDOI (github.com)

5 问题

在做上传题目的表格时,让 id 自动递增,刚开始没有保存这个表格

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

@TableName("ojquestion")
@Data
public class OJ {
@TableId(type = IdType.AUTO) // 设置为 AUTO 类型,自增主键
private Integer id; // 使用 Integer 避免默认值问题
private String question;
private String answer;
private String title;
}

application.properties:

1
mybatis-plus.global-config.db-config.id-type=AUTO

6 两者答案对比

对提交增加一个方法,点击提交后将输入的code及输入数据返回后端,然后在后端调用api,分别将(输入的code及输入数据)和(后端数据库中的ojquestion表格对应的正确代码answer及前端的输入数据)提交给api运行,如果两者运行结果一致则在前端左侧的”提交“栏显示”答案正确“否则显示”答案错误“

用户操作
用户在页面上输入代码和测试数据。
点击“提交”按钮。
前端逻辑
发送请求到 /api/submitCode/{id} 接口。
后端逻辑
获取用户代码和输入数据。
查询数据库中的正确答案代码。
分别运行两段代码,比较运行结果。
返回“答案正确”或“答案错误”给前端。
前端显示结果
使用弹窗显示结果信息。

CodeExecution.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
package com.example.demo.controller;  

import com.example.demo.entity.OJ;
import com.example.demo.mapper.OjMapper;
import jakarta.annotation.Resource;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.asynchttpclient.*;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

class CodeExecutionRequest {
private String code;
private String input;

public String getCode() {
return code;
}

public void setCode(String code) {
this.code = code;
}

public String getInput() {
return input;
}

public void setInput(String input) {
this.input = input;
}
}

class CodeExecutionResponse {
private String output;

public CodeExecutionResponse(String output) {
this.output = output;
}

public String getOutput() {
return output;
}

public void setOutput(String output) {
this.output = output;
}
}

@CrossOrigin(origins = "http://localhost:8080")
@RestController
public class CodeExecutionController {

private static final String API_HOST = "judge0-ce.p.rapidapi.com";
private static final String API_KEY = "ecb28c3467mshf8bd712d1eaeae2p100845jsnb2a33783ad93";

@PostMapping("/api/runCode")
public CompletableFuture<ResponseEntity<CodeExecutionResponse>> runCode(@RequestBody CodeExecutionRequest request) {
String code = request.getCode();
String input = request.getInput();


String encodedCode = Base64.getEncoder().encodeToString(code.getBytes());
String encodedInput = Base64.getEncoder().encodeToString(input.getBytes());

String payload = String.format(
"{\"language_id\": 52, \"source_code\": \"%s\", \"stdin\": \"%s\"}",
encodedCode, encodedInput
);


AsyncHttpClient client = Dsl.asyncHttpClient();

Request apiRequest = Dsl.post("https://" + API_HOST + "/submissions?base64_encoded=true&wait=true&fields=*")
.setHeader("x-rapidapi-key", API_KEY)
.setHeader("x-rapidapi-host", API_HOST)
.setHeader("Content-Type", "application/json")
.setBody(payload)
.build();

// 处理 API 的响应
return client.executeRequest(apiRequest).toCompletableFuture().thenApply(response -> {
try {
// 解析 JSON 响应
ObjectMapper mapper = new ObjectMapper();
JsonNode jsonNode = mapper.readTree(response.getResponseBody());

// 解码 stdout 的 Base64 编码
String stdout = "";
if (jsonNode.has("stdout") && !jsonNode.get("stdout").isNull()) {
String stdoutBase64 = jsonNode.get("stdout").asText();
stdout = new String(Base64.getDecoder().decode(stdoutBase64));
} else {
stdout = "No output received.";
}

// 关闭客户端
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}

// 返回响应给前端
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(new CodeExecutionResponse(stdout));
} catch (Exception e) {
e.printStackTrace();
try {
client.close();
} catch (IOException ioException) {
ioException.printStackTrace();
}
return ResponseEntity.status(500).body(new CodeExecutionResponse("Error processing the request."));
}
});
}
// 注入 Mapper
@Resource
private OjMapper ojMapper;
@PostMapping("/api/submitCode/{id}")
public CompletableFuture<ResponseEntity<Map<String, String>>> submitCode(
@PathVariable Integer id,
@RequestBody CodeExecutionRequest request) {

// 获取输入的代码和输入数据
String userCode = request.getCode();
String inputData = request.getInput();

// 查询数据库中的正确答案代码
OJ ojQuestion = ojMapper.selectById(id);
String correctAnswer = ojQuestion.getAnswer();

// 异步运行用户代码
CompletableFuture<String> userCodeExecution = executeCode(userCode, inputData);
// 异步运行数据库中正确答案的代码
CompletableFuture<String> correctAnswerExecution = executeCode(correctAnswer, inputData);

return userCodeExecution.thenCombine(correctAnswerExecution, (userOutput, correctOutput) -> {
Map<String, String> result = new HashMap<>();
if (userOutput.equals(correctOutput)) {
result.put("status", "correct");
result.put("message", "答案正确");
} else {
result.put("status", "incorrect");
result.put("message", "答案错误");
}
return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(result);
}).exceptionally(ex -> {
ex.printStackTrace();
Map<String, String> errorResponse = new HashMap<>();
errorResponse.put("message", "代码运行出错");
return ResponseEntity.status(500).body(errorResponse);
});
}

// 执行逻辑封装成方法
private CompletableFuture<String> executeCode(String code, String input) {
String encodedCode = Base64.getEncoder().encodeToString(code.getBytes());
String encodedInput = Base64.getEncoder().encodeToString(input.getBytes());

String payload = String.format(
"{\"language_id\": 52, \"source_code\": \"%s\", \"stdin\": \"%s\"}",
encodedCode, encodedInput
);

AsyncHttpClient client = Dsl.asyncHttpClient();
Request apiRequest = Dsl.post("https://" + API_HOST + "/submissions?base64_encoded=true&wait=true&fields=*")
.setHeader("x-rapidapi-key", API_KEY)
.setHeader("x-rapidapi-host", API_HOST)
.setHeader("Content-Type", "application/json")
.setBody(payload)
.build();

return client.executeRequest(apiRequest).toCompletableFuture().thenApply(response -> {
try {
JsonNode jsonNode = new ObjectMapper().readTree(response.getResponseBody());
String stdoutBase64 = jsonNode.has("stdout") && !jsonNode.get("stdout").isNull()
? jsonNode.get("stdout").asText()
: "";
return new String(Base64.getDecoder().decode(stdoutBase64));
} catch (IOException e) {
e.printStackTrace();
return "运行失败";
} finally {
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
});
}


}

Analysis_A.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
<template>  
<div class="main-container">

<div class="left-section">
<el-tabs> <el-tab-pane label="描述">

<div v-html="renderMarkdown(ojData.question)" class="markdown-content"></div>
</el-tab-pane> <el-tab-pane label="提交">提交内容</el-tab-pane>
<el-tab-pane label="排名">排名内容</el-tab-pane>
</el-tabs> </div>

<div class="right-section">

<div class="code-area">
<el-input type="textarea" v-model="codeInput" rows="10" placeholder="Enter your code here"></el-input>
</div>

<div class="test-output-section">

<div class="test-case-input">
<el-input type="textarea" v-model="testCase" rows="5" placeholder="在此处填写输入数据,也可留空
可以按 Ctrl+A 选中全部来一键删除"></el-input>
</div>
<div class="test-output">
<el-input type="textarea" v-model="testOutput" rows="5" placeholder="等待测试运行" disabled></el-input>
</div> </div>

<div class="button-section">
<el-button class="run-button" @click="runCode">测试运行</el-button>
<el-button class="submit-button" @click="submitCode">提交</el-button>
</div> </div> </div></template>

<script>
import { marked } from "marked";
import request from "@/utils/request";

export default {
name: "Analysis_A",
data() {
return {
ojData: {
id: '',
title: '',
question: ''
},
codeInput: '',
testCase: '',
testOutput: ''
};
},
created() {
const id = this.$route.query.id;
if (id) {
this.fetchOjData(id);
}
},
methods: {
fetchOjData(id) {
request.get(`/api/oj/${id}`).then(res => {
this.ojData = res.data;
}).catch(error => {
console.error("Error fetching OJ data:", error);
});
},
renderMarkdown(content) {
return marked(content);
},
runCode() {
console.log("Running code with input:", this.testCase);
const payload = {
code: this.codeInput,
input: this.testCase
};

console.log("Payload to be sent:", payload); // 打印 payload
request.post("http://localhost:9091/api/runCode", payload, { responseType: 'json' })
.then(response => {

console.log("Full response object from backend:", response);

if (response.data) {
console.log("Response data:", response.data);

if (response.data.output) {
this.testOutput = response.data.output;
console.log("Output:", this.testOutput);
} else {
console.error("Output is missing in the response:", response.data);
}
} else if (response.output) {
// 如果数据直接在 response 中,而不是在 response.data 中
this.testOutput = response.output;
console.log("Output directly in response:", this.testOutput);
} else {
console.error("No valid data in the response");
}
})
.catch(error => {
console.error("Error running code:", error);
});

},
submitCode() {
const id = this.$route.query.id;
const payload = {
code: this.codeInput,
input: this.testCase
};

request.post(`http://localhost:9091/api/submitCode/${id}`, payload, { timeout: 15000 })
.then(response => {
console.log('Full response:', response);
if (response.message && response.status) {
console.log('Direct response message:', response.message);
console.log('Direct response status:', response.status);
this.showSubmitResult(response.message);
} else if (response.data) {
// 打印 response.data 检查其是否存在
console.log('Result data:', response.data);
const result = response.data;
if (result.status === "correct") {
this.showSubmitResult(result.message);
} else {
this.showSubmitResult(result.message);
}
} else {
this.showSubmitResult("无法解析服务器响应");
}
})
.catch(error => {
console.error("提交代码时出错:", error);
this.showSubmitResult("提交失败,请重试");
});
},
showSubmitResult(message) {
this.$alert(message, "提交结果", {
confirmButtonText: "确定"
});
}
}
}
</script>

<style scoped>
.main-container {
display: flex;
height: 800px;
}

.left-section {
flex: 1;
padding: 10px;
width: 1500px;
}

.right-section {
flex: 2;
display: flex;
flex-direction: column;
padding: 10px;
width: 30%;
}

.code-area {
height: 2000px;
margin-bottom: 10px;
}

.test-output-section {
display: flex;
justify-content: space-between;
flex: 1;
}

.test-case-input, .test-output {
width: 48%;
height: 200px;
}

.button-section {
display: flex;
justify-content: flex-end;
margin-top: 10px;
}

.markdown-content {
padding: 10px;
background-color: #f9f9f9;
border-radius: 5px;
}

.run-button {
background-color: white;
color: black;
border: 1px solid #dcdfe6;
}

.run-button:hover {
background-color: #f2f2f2;
}

.submit-button {
background-color: #009999;
color: white;
border: none;
}

.submit-button:hover {
background-color: #007777;
}
</style>

主要改动:后端增加了/api/submitCode,接口来处理用户的提交逻辑

后端应该返回一个有效的 JSON 响应

  • 使用了 HashMap 来创建一个可变的映射(Map<String, String>)。
  • 使用 put 方法动态添加键值对,分别为 "status""message"
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    @PostMapping("/api/submitCode/{id}")
    public CompletableFuture<ResponseEntity<Map<String, String>>> submitCode(
    @PathVariable Integer id,
    @RequestBody CodeExecutionRequest request) {

    OJ ojQuestion = ojMapper.selectById(id);
    if (ojQuestion == null) {
    Map<String, String> errorResponse = new HashMap<>();
    errorResponse.put("message", "题目不存在");
    return CompletableFuture.completedFuture(
    ResponseEntity.status(404).body(errorResponse));
    }
    String correctAnswer = ojQuestion.getAnswer();

    String userCode = request.getCode();
    String inputData = request.getInput();

    CompletableFuture<String> userCodeExecution = executeCode(userCode, inputData);
    CompletableFuture<String> correctAnswerExecution = executeCode(correctAnswer, inputData);

    return userCodeExecution.thenCombine(correctAnswerExecution, (userOutput, correctOutput) -> {
    Map<String, String> result = new HashMap<>(); // 使用 HashMap 以便动态添加数据
    if (userOutput.equals(correctOutput)) {
    result.put("status", "correct");
    result.put("message", "答案正确");
    } else {
    result.put("status", "incorrect");
    result.put("message", "答案错误");
    }
    return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(result);
    }).exceptionally(ex -> {
    ex.printStackTrace();
    Map<String, String> errorResponse = new HashMap<>();
    errorResponse.put("message", "代码运行出错");
    return ResponseEntity.status(500).body(errorResponse);
    });
    }

使用Json的形式返回前端,但是现在前端提交正确代码时{code: 200, message: “success”, data: {id: 1,…}} code : 200 data : {id: 1,…} message : “success”,前端弹窗却显示答案错误

解决方法:
可能 Axios 响应对象中并没有 response.status,通过 response.data 来获取实际返回的结果,而不是 response.status
分别打印

1
console.log('Full response:', response);
1
console.log('Result data:', result);

与问题 1 属于同一原因导致的,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
submitCode() {
const id = this.$route.query.id;
const payload = {
code: this.codeInput,
input: this.testCase
};

request.post(`http://localhost:9091/api/submitCode/${id}`, payload, { timeout: 15000 })
.then(response => {
console.log('Full response:', response); // 打印完整响应对象
// 检查 response 是否直接包含 message 和 status
if (response.message && response.status) {
console.log('Direct response message:', response.message);
console.log('Direct response status:', response.status);
this.showSubmitResult(response.message);
} else if (response.data) {
// 打印 response.data 检查其是否存在
console.log('Result data:', response.data);
const result = response.data;
if (result.status === "correct") {
this.showSubmitResult(result.message);
} else {
this.showSubmitResult(result.message);
}
} else {
this.showSubmitResult("无法解析服务器响应");
}
})
.catch(error => {
console.error("提交代码时出错:", error);
this.showSubmitResult("提交失败,请重试");
});
}

其实这里可以换一种方式实现,比如将数据库正确答案传至 api 后的结果作为正确输出,和用户输入的代码及测试用例一起再次传到 api,这样可以直接把 api 返回的 json 传至前端,也不用加判断正误的逻辑,不用手动实现传输 json。

作者

zyh

发布于

2024-10-28

更新于

2024-10-28

许可协议

评论