0

我写了一个简单的 Seneca 插件,我最终会用它来监听 http 消息:

买家.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()

我现在可以发送 POST 请求localhost:10101/act并使用该插件:

curl -d '{"role": "buyer", "action": "acceptOffer", "id": "12"}' http://localhost:10101/act
{"answer":"12"}

这就是事情变得混乱的地方。我现在想通过 web 应用程序发出这个 http 请求,所以我使用 axios 从网页发出这样的请求:

应用程序.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 的工作方式不同?为什么我得不到我的回应?

4

4 回答 4

3

是的,在本地测试,您的问题确实似乎与 Stringify 相关,正如我在评论中提到的,只需直接发送数据:

axios.post('http://localhost:10101/act', this.postBody)
于 2018-02-14T20:37:11.043 回答
1

否则 curl 不起作用,您只是没有在查询中传递所需的标头。底线是服务器应该了解它应该以哪种格式接收数据。我在您的代码中看到您提供了 JSON。因此,通过报头指定传输数据的格式。

例如,您应该这样要求:

curl -H "Content-Type: application/json" localhost:10101/act

您的服务器(后端)必须使用完全相同的标头进行响应。

有问题的CORS - 有问题的服务器。如果您对CORS有问题,在您的情况下,我认为您的前端与 api 相比在不同的端口上工作。在任何情况下,您都不需要在前端传输 CORS 的标头(尽管有人正在尝试这样做,通常这是在浪费时间)。您只需要监控传输的数据类型。

请参阅示例 Axios 获取/发布(没关系):

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。这很奇怪,而且具有误导性。

于 2018-02-14T17:01:58.673 回答
1

下面的代码有很多问题:

var body = JSON.stringify(this.postBody)
axios.post(`http://localhost:10101/act`, {
  body: body
})

首先,axios.post()返回一个Promise. 如果您的方法是异步的,您应该await axios.post(…)标记您的方法asyncreturn axios.post(…)

其次,如果您看到axios 文档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 正在发送的内容以及您必须为其创建处理程序的内容。在您的处理程序中,您将不得不再次对 body 进行转义:

> 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*

并且在您的处理程序中,您也不应该对结果进行双重编码(可能不确定 seneca 是如何工作的):

module.exports = function buyer (){
    this.add('role:buyer, action: acceptOffer', function(msg, respond){
        respond({answer: msg.id}) // Note *NOT USING JSON.stringify*
    })
}
于 2018-02-14T17:29:13.107 回答
0

您的axios请求标头必须包含

header {
    'Content-Type' : 'application/x-www-form-urlencoded; charset=UTF-8;application/json'
}

这对我有用。

于 2021-04-11T13:01:35.470 回答