我写了一个简单的Seneca插件,我最终会用它来收听http消息:
buyer.js
module.exports = function buyer (){
this.add('role:buyer, action: acceptOffer', function(msg, respond){
respond(JSON.stringify({answer: msg.id}))
})
}
当我使用以下脚本运行时:
index.js
require('seneca')()
.use(require('./designs/offer/roles/buyer.js'))
.listen()
我现在可以向localhost:10101/act
发送POST请求并使用插件:
curl -d '{"role": "buyer", "action": "acceptOffer", "id": "12"}' http://localhost:10101/act
{"answer":"12"}
这里的事情变得混乱。我现在想通过网络应用程序发出这个http请求,所以我使用axios从网页上发出这样的请求:
App.vue
<template>
<div id="app">
<button @click="sendQuery"></button>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'App',
data(){
return {
postBody: {
id: '12',
role: 'buyer',
action: 'acceptOffer'
}
}
},
methods: {
sendQuery: function(){
var body = JSON.stringify(this.postBody)
axios.post(`http://localhost:10101/act`, {
body: body
})
}
}
}
</script>
当我点击按钮发送请求时,我从浏览器控制台收到此错误消息(启用CORS后):
xhr.js?ec6c:178 POST http://localhost:10101/act 500 (Internal Server Error)
dispatchXhrRequest @ xhr.js?ec6c:178
xhrAdapter @ xhr.js?ec6c:12
dispatchRequest @ dispatchRequest.js?c4bb:59
Promise.then (async)
request @ Axios.js?5e65:51
Axios.(anonymous function) @ Axios.js?5e65:71
wrap @ bind.js?24ff:9
sendQuery @ App.vue?26cd:26
boundFn @ vue.esm.js?efeb:190
invoker @ vue.esm.js?efeb:2004
fn._withTask.fn._withTask @ vue.esm.js?efeb:1802
createError.js?16d0:16 Uncaught (in promise) Error: Request failed with status code 500
at createError (createError.js?16d0:16)
at settle (settle.js?db52:18)
at XMLHttpRequest.handleLoad (xhr.js?ec6c:77)
任何人都可以告诉我为什么这与curl不同?为什么我无法得到我的回复?
答案 0 :(得分:1)
curl不起作用,否则你只是不在查询中传递所需的标题。底线是服务器应该理解它应该以哪种格式接收数据。我在你的代码中看到你给了JSON。因此,请通过标题指定传输数据的格式。
例如,你应该提出这样的要求:
curl -H "Content-Type: application/json" localhost:10101/act
您的服务器(后端)必须使用完全相同的标头进行响应。
问题CORS - 服务器问题。如果您对CORS有疑问,在您的情况下,我认为您的前端与api相比在不同的端口上工作。在任何情况下,您都不需要在前端传输CORS的标头(虽然有人试图这样做,但通常是浪费时间)。您只需要监控传输的数据类型。
参见示例Axios get / post(没关系):
const configAxios = {
headers: {
'Content-Type': 'application/json',
},
};
axios.post('api/categories', configAxios)
.then((res) => {
this.categories = res.data;
console.log(res);
})
.catch((err) => {
console.warn('error during http call', err);
});
在您的代码中使用JSON.stringify
,请不要这样做,因为Axios已经使用此功能。
例如服务器端。我喜欢Symfony4并使用NelmioCorsBundle,请查看allow_origin: ['*']
。如果你使用Symfony,这很简单。
nelmio_cors:
defaults:
allow_credentials: false
allow_origin: ['*']
allow_headers: ['Content-Type']
allow_methods: []
expose_headers: []
max_age: 0
hosts: []
origin_regex: false
forced_allow_origin_value: ~
paths:
'^/api/':
allow_origin: ['*']
allow_headers: ['X-Custom-Auth', 'Content-Type', 'Authorization']
allow_methods: ['POST', 'PUT', 'GET', 'DELETE']
max_age: 3600
'^/':
origin_regex: true
allow_origin: ['^http://localhost:[0-9]+']
allow_headers: ['X-Custom-Auth', 'Content-Type']
allow_methods: ['POST', 'PUT', 'GET', 'DELETE']
max_age: 3600
hosts: ['^api\.']
如果您不是直接使用服务器,请与供应商核实此细微差别。
此标头也可以通过Nginx传输,这不是最好的主意。
例如,请查看:
add_header Access-Control-Allow-Origin *;
server {
listen 8080;
server_name site.local;
root /var/www/site/public;
location / {
add_header Access-Control-Allow-Origin *;
# try to serve file directly, fallback to index.php
try_files $uri /index.php$is_args$args;
}
location ~ ^/index\.php(/|$) {
fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
fastcgi_split_path_info ^(.+\.php)(/.*)$;
include fastcgi_params;
# When you are using symlinks to link the document root to the
# current version of your application, you should pass the real
# application path instead of the path to the symlink to PHP
# FPM.
# Otherwise, PHP's OPcache may not properly detect changes to
# your PHP files (see https://github.com/zendtech/ZendOptimizerPlus/issues/126
# for more information).
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
# Prevents URIs that include the front controller. This will 404:
# http://domain.tld/index.php/some-path
# Remove the internal directive to allow URIs like this
internal;
}
# return 404 for all other php files not matching the front controller
# this prevents access to other php files you don't want to be accessible.
location ~ \.php$ {
return 404;
}
error_log /var/log/nginx/project_error.log;
access_log /var/log/nginx/project_access.log;
}
如果没有数据传递则删除Content-Type值得注意。必须始终传输数据或null
。这很奇怪,这是误导。
答案 1 :(得分:1)
以下代码存在许多问题:
var body = JSON.stringify(this.postBody)
axios.post(`http://localhost:10101/act`, {
body: body
})
首先,axios.post()
返回Promise
。如果您的方法是异步的,则应await axios.post(…)
并标记方法async
或return axios.post(…)
。
其次,如果你看到axios docs for axios.post()
,第二个参数就是postData本身。您指示AXIOS要做的是将此JSON作为正文发送:
{"body":"{\\"id\\":\\"12\\",\\"role\\":\\"buyer\\",\\"action\\":\\"acceptOffer\\"}"}
即,你是1.包装你打算在另一个对象中发送的对象2.双重字符串化数据。考虑一次解析它的结果(seneca会为你做的):
> JSON.parse(s)
{ body: '{"id":"12","role":"buyer","action":"acceptOffer"}' }
以body
键为字符串的上述数据是Seneca发送的内容以及为此创建处理程序所需的内容。在你的处理程序中,你将不得不再次使用身体:
> JSON.parse(JSON.parse(s).body)
{ id: '12', role: 'buyer', action: 'acceptOffer' }
但是,我不知道为什么这会导致Seneca.js本身出错。 seneca可能会抛出错误,因为您没有为具有字符串值的body
属性的模式设置任何处理程序。也许如果你做这样的事情(我不知道如何接受字符串值作为seneca中的模式,这可能是错误的):
this.add('body:string', function(msg, respond){respond({value: msg.body})});
也许你打算写你的axios调用传递未转义的数据,以便它被axios正确地编码为JSON:
var body = this.postBody; // Note *NOT USING JSON.stringify()*
axios.post('http://localhost:10101/act', body); // Note *NOT PASSING body KEY*
并且在你的处理程序中你不应该对结果进行双重编码(可能,不确定塞内卡的工作原理):
module.exports = function buyer (){
this.add('role:buyer, action: acceptOffer', function(msg, respond){
respond({answer: msg.id}) // Note *NOT USING JSON.stringify*
})
}
答案 2 :(得分:1)
Yup,在本地测试过,你的问题确实似乎与Stringify有关,正如我的评论所述,只是直接发送数据:
axios.post('http://localhost:10101/act', this.postBody)
答案 3 :(得分:0)
您的 axios
请求标头必须包含
header {
'Content-Type' : 'application/x-www-form-urlencoded; charset=UTF-8;application/json'
}
这对我有用。