使用纯JavaScript发送POST数据而无需表单

303

有没有一种方法可以使用纯JavaScript(而非jQuery $.post())发送POST方法的数据,而不需要表单且不刷新页面?也许是通过 httprequest 或其他方式实现的(只是目前找不到)?


4
XMLHttpRequest是答案...$.post在幕后使用相同的技术。 - Chandu
这个问题可能会对你有所帮助:[https://dev59.com/Arbna4cB1Zd3GeqPfLFE#58218057][1] [1]: https://dev59.com/Arbna4cB1Zd3GeqPfLFE#58218057 - Jorge del Campo Andrade
13个回答

298
你可以发送它并将数据插入到正文中:

你可以发送它并将数据插入到正文中:

var xhr = new XMLHttpRequest();
xhr.open("POST", yourUrl, true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify({
    value: value
}));

顺便提一句,对于GET请求:

var xhr = new XMLHttpRequest();
// we defined the xhr

xhr.onreadystatechange = function () {
    if (this.readyState != 4) return;

    if (this.status == 200) {
        var data = JSON.parse(this.responseText);

        // we get the returned data
    }

    // end of state change: it can be after some time (async)
};

xhr.open('GET', yourUrl, true);
xhr.send();

4
xhr.open中的true布尔变量是用来指定请求是异步还是同步的。 - Hylle
6
@Hylle async: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/open@Hylle async: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/open - BlackICE
3
这是旧的请求方式。我强烈建议不要使用这种方法,而要使用fetch函数代替。 - Michael Artman
这是关于此主题的 MDN 页面上的教程: https://developer.mozilla.org/en-US/docs/Learn/Forms/Sending_forms_through_JavaScript - nuiun
@MichaelArtman 即使在你的评论一年之后,这个功能至今仍然没有被所有浏览器支持。 - IgorR
显示剩余5条评论

273

Fetch API 旨在简化 GET 请求,但也可进行 POST 请求。

let data = {element: "barium"};

fetch("/post/data/here", {
  method: "POST",
  headers: {'Content-Type': 'application/json'}, 
  body: JSON.stringify(data)
}).then(res => {
  console.log("Request complete! response:", res);
});
如果你和我一样懒(或者只是喜欢捷径/助手):
window.post = function(url, data) {
  return fetch(url, {method: "POST", headers: {'Content-Type': 'application/json'}, body: JSON.stringify(data)});
}

// ...

post("post/data/here", {element: "osmium"});

4
由于这个答案太简单了,我提前点赞了它,但现在无法取消我的投票。只有添加标头,发送数据才有效(标头:{'Accept': 'application/json','Content-Type': 'application/json'})。此外,如果您不调用响应的json()方法,接收数据也不起作用,像这样:res.json(),这恰好会返回另一个要解包的Promise。最好使用async/await并使用await解包所有这些Promise。 - OCDev
1
fetch API 是当前的最佳选择。 - Hannes Schneidermayer
谢谢,运行得很顺利。 - Frijey Labs
获取fetch函数参数文档的直接链接:https://developer.mozilla.org/en-US/docs/Web/API/fetch#parameters - Nico Bako
我使用了这种技术。它在某种程度上解决了我的问题,但当 Chrome 开发者工具停止显示响应主体时,我变得很疯狂。我以为这是我的 API 的问题。经过数小时的调试,我知道这是一个 fetch API 的问题。一旦你返回 res.json(),开发者工具就开始显示响应。我希望我能在周五晚上做些更有成效的事情 :( - Manish Bansal

69

您可以按照以下方式使用XMLHttpRequest对象:

xhr.open("POST", url, true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
xhr.send(someStuff);

那段代码将someStuff发送到url。只需确保创建XMLHttpRequest对象时,它是跨浏览器兼容的。如何实现跨浏览器兼容性有无数的例子。


3
你能否给 someStuff 写一个例子? - FluorescentGreen5
7
someStuff = 'param1=val1&param2=val2&param3=val3' 的翻译是:一些内容 = '参数1=值1&参数2=值2&参数3=值3' - Camel
1
那是一个很好的回答,someStuff 可以是你想要的任何东西,甚至是一个简单的字符串。你可以使用在线服务来检查请求,比如我个人最喜欢的:(https://requestb.in) - JamesC
application/x-www-form-urlencoded MIME 类型没有 charset 参数:https://www.iana.org/assignments/media-types/application/x-www-form-urlencoded - jbg
someStuff变量通常需要设置为“variable=value”。然后,您可以使用$_POST ['variable']检索数据,这将等于'value'。 - Rhys Broughton

44
此外,RESTful 使您能够从 POST 请求中获取数据。
JS(放在 static/hello.html 中通过 Python 提供服务):
<html><head><meta charset="utf-8"/></head><body>
Hello.

<script>

var xhr = new XMLHttpRequest();
xhr.open("POST", "/postman", true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify({
    value: 'value'
}));
xhr.onload = function() {
  console.log("HELLO")
  console.log(this.responseText);
  var data = JSON.parse(this.responseText);
  console.log(data);
}

</script></body></html>

Python服务器(用于测试):

import time, threading, socket, SocketServer, BaseHTTPServer
import os, traceback, sys, json


log_lock           = threading.Lock()
log_next_thread_id = 0

# Local log functiondef


def Log(module, msg):
    with log_lock:
        thread = threading.current_thread().__name__
        msg    = "%s %s: %s" % (module, thread, msg)
        sys.stderr.write(msg + '\n')

def Log_Traceback():
    t   = traceback.format_exc().strip('\n').split('\n')
    if ', in ' in t[-3]:
        t[-3] = t[-3].replace(', in','\n***\n***  In') + '(...):'
        t[-2] += '\n***'
    err = '\n***  '.join(t[-3:]).replace('"','').replace(' File ', '')
    err = err.replace(', line',':')
    Log("Traceback", '\n'.join(t[:-3]) + '\n\n\n***\n*** ' + err + '\n***\n\n')

    os._exit(4)

def Set_Thread_Label(s):
    global log_next_thread_id
    with log_lock:
        threading.current_thread().__name__ = "%d%s" \
            % (log_next_thread_id, s)
        log_next_thread_id += 1


class Handler(BaseHTTPServer.BaseHTTPRequestHandler):

    def do_GET(self):
        Set_Thread_Label(self.path + "[get]")
        try:
            Log("HTTP", "PATH='%s'" % self.path)
            with open('static' + self.path) as f:
                data = f.read()
            Log("Static", "DATA='%s'" % data)
            self.send_response(200)
            self.send_header("Content-type", "text/html")
            self.end_headers()
            self.wfile.write(data)
        except:
            Log_Traceback()

    def do_POST(self):
        Set_Thread_Label(self.path + "[post]")
        try:
            length = int(self.headers.getheader('content-length'))
            req   = self.rfile.read(length)
            Log("HTTP", "PATH='%s'" % self.path)
            Log("URL", "request data = %s" % req)
            req = json.loads(req)
            response = {'req': req}
            response = json.dumps(response)
            Log("URL", "response data = %s" % response)
            self.send_response(200)
            self.send_header("Content-type", "application/json")
            self.send_header("content-length", str(len(response)))
            self.end_headers()
            self.wfile.write(response)
        except:
            Log_Traceback()


# Create ONE socket.
addr = ('', 8000)
sock = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(addr)
sock.listen(5)

# Launch 100 listener threads.
class Thread(threading.Thread):
    def __init__(self, i):
        threading.Thread.__init__(self)
        self.i = i
        self.daemon = True
        self.start()
    def run(self):
        httpd = BaseHTTPServer.HTTPServer(addr, Handler, False)

        # Prevent the HTTP server from re-binding every handler.
        # https://dev59.com/nlYO5IYBdhLWcg3wL-vY
        httpd.socket = sock
        httpd.server_bind = self.server_close = lambda self: None

        httpd.serve_forever()
[Thread(i) for i in range(10)]
time.sleep(9e9)

控制台日志(chrome):

HELLO
hello.html:14 {"req": {"value": "value"}}
hello.html:16 
{req: {}}
req
:
{value: "value"}
__proto__
:
Object

控制台日志(火狐浏览器):

GET 
http://XXXXX:8000/hello.html [HTTP/1.0 200 OK 0ms]
POST 
XHR 
http://XXXXX:8000/postman [HTTP/1.0 200 OK 0ms]
HELLO hello.html:13:3
{"req": {"value": "value"}} hello.html:14:3
Object { req: Object }

控制台日志(Edge):

HTML1300: Navigation occurred.
hello.html
HTML1527: DOCTYPE expected. Consider adding a valid HTML5 doctype: "<!DOCTYPE html>".
hello.html (1,1)
Current window: XXXXX/hello.html
HELLO
hello.html (13,3)
{"req": {"value": "value"}}
hello.html (14,3)
[object Object]
hello.html (16,3)
   {
      [functions]: ,
      __proto__: { },
      req: {
         [functions]: ,
         __proto__: { },
         value: "value"
      }
   }

Python 日志:

HTTP 8/postman[post]: PATH='/postman'
URL 8/postman[post]: request data = {"value":"value"}
URL 8/postman[post]: response data = {"req": {"value": "value"}}

36

您可以使用XMLHttpRequest,fetch API等...

如果您想使用XMLHttpRequest,可以按照以下步骤进行

var xhr = new XMLHttpRequest();
xhr.open("POST", url, true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify({
    name: "Deska",
    email: "deska@gmail.com",
    phone: "342234553"
 }));
xhr.onload = function() {
    var data = JSON.parse(this.responseText);
    console.log(data);
};

或者如果你想使用fetch API

fetch(url, {
    method:"POST",
    body: JSON.stringify({
        name: "Deska",
        email: "deska@gmail.com",
        phone: "342234553"
        })
    }).then(result => {
        // do something with the result
        console.log("Completed with result:", result);
    }).catch(err => {
        // if any error occured, then catch it here
        console.error(err);
    });

13

使用FormData对象,您可以轻松地将数据打包并像发送HTML表单一样将其发送到服务器,使用POST方法。

data = new FormData()
data.set('Foo',1)
data.set('Bar','boo')

let request = new XMLHttpRequest();
request.open("POST", 'some_url/', true);
request.send(data)

现在您可以像处理常规HTML表单一样在服务器端处理数据。

附加信息

建议在发送FormData时不要设置Content-Type头,因为浏览器会自动处理。


1
❗️ FormData 会创建一个多部分表单请求,而不是一个 application/x-www-form-urlencoded 请求。 - ccpizza
@ccpizza - 感谢您的澄清。由于OP没有提到要POST哪种类型的数据,我认为FormData是回答的最合适方式。 - Amin Hemati Nik

12

你知道JavaScript有内置的方法和库可以创建表单并提交吗?

我在这里看到很多回复都要求使用第三方库,我认为这是过度设计。

我会用纯JavaScript做以下操作:

<script>
function launchMyForm()
{
   var myForm = document.createElement("FORM");
   myForm.setAttribute("id","TestForm");
   document.body.appendChild(myForm);

// this will create a new FORM which is mapped to the Java Object of myForm, with an id of TestForm. Equivalent to: <form id="TestForm"></form>

   var myInput = document.createElement("INPUT");
   myInput.setAttribute("id","MyInput");
   myInput.setAttribute("type","text");
   myInput.setAttribute("value","Heider");
   document.getElementById("TestForm").appendChild(myInput);
   
// To submit the form: 
   myForm.method = "POST";
   myForm.action = "whatever.aspx";  // or "response.php" 
   myForm.submit();

// This will create an INPUT equivalent to: <INPUT id="MyInput" type="text" value="Heider" /> and then assign it to be inside the TestForm tags. 
}
</script>

通过这种方式(A),您无需依赖第三方来完成工作(B),因为它已经内置于所有浏览器中(C),速度更快(D),而且它有效,欢迎尝试。

希望这能帮到您。

H


我喜欢这个回复。现在你已经创建了一个表单,但是你如何发送它呢? - étale-cohomology
你好,我看了一下我的回复,你说得完全正确,我刚刚修改了它并添加了如何提交的说明,希望一切顺利。如果你需要更多帮助,请告诉我,谢谢,Heider。 - Heider Sati

8

navigator.sendBeacon()

如果你只需要POST数据而不需要来自服务器的响应,最简单的解决方案是使用navigator.sendBeacon()
const data = JSON.stringify({
  example_1: 123,
  example_2: 'Hello, world!',
});

navigator.sendBeacon('example.php', data);

2
执行“sendBeacon”时失败:“Navigator”:仅支持通过HTTP(S)发送信标。 - Ali80
1
在我看来,navigator.sendBeacon 不应该用于这个目的。 - jolivier

6

这里最受欢迎的答案没有展示如何从POST中获取数据。同时,流行的“fetch”解决方案在将数据发送到最新版本的NodeJS时,在最新版本的Chrome中不起作用,除非您传递headers并取消返回json() Promise的包装。此外,这些流行答案也没有使用async/await。

这是我能想出的最干净和最完整的解决方案,它可以正常工作。

async function postJsonData(jsonObject) {
    const response = await fetch("/echo", {
        method: "POST",
        headers: {'Content-Type': 'application/json'},
        body: JSON.stringify(jsonObject)
    });
    
    const actualResponse = await response.json();
}

不错。如果有人想在node.js中使用该解决方案,请阅读此链接:https://dev59.com/U1YM5IYBdhLWcg3wvCH- - Gernot

4
const data = { username: 'example' };

fetch('https://example.com/profile', {
  method: 'POST', // or 'PUT'
  headers: {
 '           Content-Type': 'application/json',
           },
  body: JSON.stringify(data),
})
  .then(response => response.json())
  .then(data => {
      console.log('Success:', data);
     })
 .catch((error) => {
         console.error('Error:', error);
   });

无效名称错误 - Spacehold

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接