Я также боролся с пустым телом запроса в моем проекте сервера api.
Я хотел опубликовать данные из нескольких частей, которые состоят из строковых / текстовых данных и изображения, на бэкэнд-сервер успокоительного API. Я подумал, что достаточно просто передать данные с помощью объекта formData через Axios. Однако в итоге я был почти разочарован, потому что в backend restful api project я получал пустое тело запроса. К счастью, через поиск по некоторым статьям, stackoverflow и youtube. Мне удалось собрать полностью работающую программу node.js. Нет больше пустых данных тела запроса !!! ,
Здесь я хотел бы поделиться своими рабочими кодами.
Чтобы передать multipart / form-data от клиента к серверу, я обнаружил, что мы должны использовать несколько промежуточных программ и объектов node / java:
- В проекте api на стороне сервера: я использую {multer, body-parser} middlewares
- В проекте клиентского приложения: я использую 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
Код для ProductController в (бэкэнд API): /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 , чтобы отправить запрос на отправку серверному серверу 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]
}
Я надеюсь, что эти коды помогут любому, кто столкнется с проблемой пустого тела запроса.