英文:
How do I send a acknowledgement to client, perform work in the background and then send the data back to the client?
问题
尝试创建一个处理Excel数据的REST API。我想要通知客户端已经收到了文件。
app.post('/upload', upload.fields([{name: 'raw'},{name: 'report'}]), (req: any, res: any)=>{
res.send(statuscode 200)
// 执行异步Excel操作
return res.json({data: data});
})
我不想在客户端等待30秒后发送响应。
await excelOperations();
return res.json()
英文:
Trying to create a rest api that processes excel data. I want to let the client know that the files have been received.
app.post('/upload',upload.fields([{name: 'raw'},{name: 'report'}]), (req: any, res: any)=>{
res.send(statuscode 200)
// do async excel operations
return res.json({data: data});
})
I don't want to send a response after the client has been waiting for 30 seconds
await excelOperations();
return res.json()
答案1
得分: 1
使用传统的HTTP请求-响应模型,你不能这样做。你应该在数据提交后将确认作为响应发送,当上传完成时。然后客户端发起另一个请求(甚至可以是多个请求)来查询处理状态,你需要对每个请求都用进度报告响应来回应,最后客户端发起另一个请求以下载结果,你需要以已处理的资源作为响应。
但是,你可以使用服务器推送事件(server-sent events)或Websockets,只要连接保持打开,就可以发送多个内容。
英文:
With the traditional HTTP request-response model, you don't. You send the acknowledgement as the response to the data submission, when the upload has completed. Then the client makes another request (or even multiple ones) to inquire about the status of the processing, which you respond to with a progress report response each, and finally the client makes another request to download the results, to which you respond with the processed resource.
You could however use server-sent events or websockets which would allow you to send multiple things as long as the connection is open.
答案2
得分: 1
以下是您要翻译的内容:
要完成Bergi的回答,您可以这样做:
- 创建一个joblist对象
- 在访问/upload端点之前,在上传之前创建一个新的带有状态"pending"的job id,并将job id作为响应返回
- 在上传完成后(在异步代码之后),将job的状态更改为completed
- 创建一个新的端点[GET] /job/:jobId,返回存储在job对象中的job状态
- 在前端定期检查端点/job/:jobId,使用[POST] / upload返回的job id
这段代码只是一个示例,用于解释答案,请根据您的需求进行调整,处理错误等
代码示例
const NanoidAsync = require('nanoid/async');
let jobList = {}
app.post('/upload', upload.fields([{name: 'raw'},{name: 'report'}]), async (req, res) => {
let jobId = await NanoidAsync.nanoid();
jobList[jobId] = {jobId: jobId, status: "pending"}
res.send(jobList[jobId])
await excelOperations();
})
app.get('/job/:jobId', (req, res) => {
res.send(jobList[jobId])
})
如评论中所要求,之后该怎么办?
如果您的作业不需要返回,只需在检测到作业成功后,在前端显示一条消息或更新按钮文本或其他需要告知用户的内容(如果需要)。
如果您的作业必须返回数据,请按如下方式编辑代码:
app.post('/upload', upload.fields([{name: 'raw'},{name: 'report'}]), async (req, res) => {
let jobId = await NanoidAsync.nanoid();
jobList[jobId] = {jobId: jobId, status: "pending", data: null} // 新代码
res.send(jobList[jobId])
jobList[jobId].data = await excelOperations(); // 新代码
})
然后,在前端,您将在[GET] /job/:jobId的返回值中获得作业返回的数据。
将这样的代码投入生产的建议
- 管理作业数据的生命周期,经过一段时间后,删除作业列表中的键,以避免在内存中拥有一个大对象。
- 处理错误。
英文:
To complete Bergi answer you can do something like that :
- Create a joblist object
- When hitting the /upload endpoint, before uploading create a new job id with a status "pending" and return the job id as response
- When upload complete (after your async code) change the status of the job to completed
- Create a new endpoint [GET] /job/:jobId that return status of a job stored in your job object
- On the front side check in interval the endpoint /job/:jobId with the job id returned by [POST] / upload
> This code is just an example to explain the answer, adapt it to your need, handle error etc
Code example
const NanoidAsync = require('nanoid/async');
let jobList = {}
app.post('/upload',upload.fields([{name: 'raw'},{name: 'report'}]), (req: any, res: any)=>{
let jobId = await NanoidAsync.nanoid();
jobList[jobId] = {jobId:jobId,status:"pending"}
res.send(jobList[jobId])
await excelOperations();
})
app.get('/job/:jobId', (req: any, res: any)=>{
res.send(jobList[jobId])
})
As asked in comment , what to do after ?
If your job doesn't need return it's just processing something just display on the front side when you detect job success a message or update button text or whatever yo uwant to tell the user (if needed) that job is completed
If you job must return data, edit the code as follow :
app.post('/upload',upload.fields([{name: 'raw'},{name: 'report'}]), (req: any, res: any)=>{
let jobId = await NanoidAsync.nanoid();
jobList[jobId] = {jobId:jobId,status:"pending",data:null} // New code
res.send(jobList[jobId])
jobList[jobId].data = await excelOperations(); // New code
})
And on the front side you will get as return of [GET] /job/:jobId the data returned by your job
Advice to put a code like that in production
- Manage a lifecycle of your jobs data, after X time , delete the key in your job list to avoid having a big object in memory
- Manage error
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论