我也在为我的 api 服务器项目中的 empty request body 苦苦挣扎。
我想将由字符串/文本数据和图像组成的多部分形式数据发布到后端 RESTful api 服务器。我认为仅通过 Axios 使用 formData 对象传递数据就足够了。然而,我最终几乎感到沮丧,因为在后端的 restful api 项目中,我一直得到空的请求正文。谢天谢地,通过搜索一些文章、stackoverflow 和 youtube。我设法组装了一个完整的 node.js 程序。不再有空的请求正文数据!!! .
在这里我想分享一下我的工作代码。
为了将 multipart/form-data 从客户端传递到服务器,我发现我们使用了几个中间件和 node/java 对象:
- 在服务器端 api 项目中:我使用 {multer, body-parser} 中间件
- 在客户端应用项目中:我使用 formData 和 axios
这是我的工作代码:(在 node.js 中编码,使用 Visual Studio 代码)
// in my (server backend api) node project : /src/routes/products/index.js
const express = require("express")
const router = express.Router()
const multer = require('multer')
const ProductsController = require('../controllers/products')
const storage = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, './uploads/')
},
filename: function(req, file, cb) {
// use this on Windows dir
cb(null, new Date().toISOString().replace(/:/g, '-') + file.originalname)
// otherwise (in Linux or IOS use this
// cb(null, new Date().toISOString() + file.originalname)
}
})
const fileFilter = (req, file, cb) => {
// reject a file if not allowed
var allowedMimes = ['image/jpeg','image/png' , 'image/gif']
if (allowedMimes.indexOf(file.mimetype) > -1) {
cb(null, true)
} else {
cb(null, false)
}
}
const upload = multer({
storage: storage,
limits: {fileSize: 1024 * 1024 * 10},
fileFilter: fileFilter
})
router.post("/", upload.single('productImage'),ProductsController.products_create_product)
module.exports = router
// end of /src/routes/products/index.js
现在在(服务器后端 api)/src/app.js 中:
const express = require("express")
const morgan = require("morgan")
const bodyParser = require("body-parser")
const mongoose = require("mongoose")
const productRoutes = require("./routes/products")
const app = express()
mongoose.connect('mongodb://localhost/shopcart', {
useMongoClient: true
})
var db = mongoose.connection
db.on('error', console.error.bind(console, 'connection error:'))
db.once('open', function() {
// we're connected!
console.log("Connected to mongoDB..."+ db.db.databaseName+ ', url: ' +'http://localhost:5000')
})
mongoose.Promise = global.Promise
app.use(morgan("dev"))
app.use('/uploads', express.static('uploads'))
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "*")
res.header(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept, Authorization"
)
if (req.method === "OPTIONS") {
res.header("Access-Control-Allow-Methods", "PUT, POST, PATCH, DELETE, GET")
return res.status(200).json({})
}
next()
})
// Routes which should handle requests
app.use("/products", productRoutes)
app.use((req, res, next) => {
const error = new Error("api Not found...")
error.status = 404
next(error)
})
app.use((error, req, res, next) => {
res.status(error.status || 500)
res.json({
error: {
message: error.message
}
})
})
module.exports = app
//end of : /src/app.js
现在在(服务器后端 api)/src/index.js 中:
const http = require('http')
const app = require('./app.js')
const ip_addr = 'localhost'
const port = process.env.PORT || 3000
const server = http.createServer(app)
server.listen(port , ip_addr)
console.log('Server started on %s port: %s',ip_addr , port )
// end of : /src/index.js
(后端 api)中 ProductController 的代码:/src/controller/Products.js:
(请注意,req.file 是由 'multer' 中间件自动添加的)
exports.products_create_product = (req, res, next) => {
// note : productImage: req.file.path
// (file) is added by 'multer' middleware
const product = new Product({
_id: new mongoose.Types.ObjectId(),
name: req.body.name,
price: req.body.price,
specification: req.body.specification,
productImage: req.file.path
})
product
.save()
.then(result => {
res.status(201).json({
message: "Product created successfully",
createdProduct: {
name: result.name,
price: result.price,
specification: result.specification,
_id: result._id,
productImage: result.productImage,
request: {
type: "POST",
url: "http://localhost:5000/products/"
}
}
})
})
.catch(err => {
res.status(500).json({
error: err
})
})
}
最后在我的客户端应用程序项目中,我使用 Axios 和 formData 将 post 请求发送到后端 api 服务器。
这是前端客户端应用程序的代码摘录:
function createProduct (payload, apiUrl, myAuthToken) {
const headers = {
'Content-Type': 'multipart/form-data',
// 'Authorization': 'Bearer ' + myAuthToken // if using JWT
}
var formData = new FormData()
formData.append( 'productImage', payload.productImage)
formData.append( 'name', payload.name)
formData.append( 'price', payload.price)
formData.append( 'specification', payload.specification)
Axios.post(apiUrl + '/products',
formData,
{'headers' : headers}
)
.then((response) => {
var imgUrl = response.data.createdProduct.productImage
payload.productImage = apiUrl+'/'+ imgUrl.replace(/\\/g, "/")
payload.id= response.data.createdProduct._id
payload.specification = response.data.createdProduct.specification
payload.price =response.data.createdProduct.price
commit('createProduct', payload)
})
.catch((error) => {
alert(error)
})
}
以下函数可用于从输入的 html 事件中提取图像数据:
function onFilePicked (event) {
const files = event.target.files
if (!files) {
return
}
let filename = files[0].name
if (filename.lastIndexOf('.') <= 0) {
return alert('Please add a valid file!')
}
const fileReader = new FileReader()
fileReader.addEventListener('load', () => {
this.productImage = fileReader.result
})
fileReader.readAsDataURL(files[0])
return files[0]
}
我希望这些代码能帮助任何遇到空请求正文问题的人。