【问题标题】:How to get POST body in ESP8266 micropython server如何在 ESP8266 micropython 服务器中获取 POST 正文
【发布时间】:2022-11-10 02:12:40
【问题描述】:

我正在尝试创建一个简单的 Micropython 项目,当微控制器第一次打开时,如果连接到 wifi 不成功,它将开始托管一个接入点。当连接到接入点时,设备会提供一个简单的网页,允许用户输入他们的 SSID 和密码,然后将其存储到设备中以供将来使用。

但是,在提交表单后检索用户在网页中键入的值时,我遇到了问题。这是我的代码:

import ujson as json
import usocket as socket
import network
import time

max_wifi_retry = 30

class ConnectWifi:
    # Constructor retrieves stored credentials and saves them to class variables
    def __init__(self, credentialsFile='config.json'):
        with open(credentialsFile) as fp:
            config = json.load(fp)
            self.ssid = config["ssid"]
            self.password = config["password"]
            self.access_point_ssid = config["access_point_ssid"]
       
    # This method will attempt to connect device to wifi
    def connectWifi(self):
        self.wifi = network.WLAN(network.STA_IF)
    
        #Restarting WiFi
        self.wifi.active(False)
        time.sleep(0.5)
        self.wifi.active(True)
        
        self.wifi.connect(self.ssid, self.password)
        
        if not self.wifi.isconnected():
            print('connecting')
            wifi_retry_attempts = 0
            while not self.wifi.isconnected() and wifi_retry_attempts < max_wifi_retry:
                print(max_wifi_retry - wifi_retry_attempts)
                wifi_retry_attempts += 1
                time.sleep_ms(1000)
                
        if not self.wifi.isconnected():
            self.wifi.active(False)
                
        return self.wifi.isconnected()
    
    # This is where I am having trouble knowing what to do
    def enableAccessPoint(self):
        print('Unable to connect to wifi, enabling wireless access point config')
        ap = network.WLAN(network.AP_IF)
        ap.active(True)
        ap.config(essid=self.access_point_ssid, authmode=network.AUTH_OPEN)
        
        print(ap.ifconfig())
        
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.bind(('', 80))
        s.listen(5)
        while True:
            try:
                conn, addr = s.accept()
                print('Got a connection from %s' % str(addr))
                request = conn.recv(1024)
                print('Content = %s' % str(request))
                response = self.getWebPage()
                conn.send(response)
                conn.close()
            except KeyboardInterrupt:
                print("break")
                break
            
    def getWebPage(self):
        html = """
                <html>
                    <head>
                        <meta name="viewport" content="width=device-width, initial-scale=1">
                    </head>
                    <body>
                        <h1>Configure Wifi</h1>
                        <form action="/submit" method="post">
                          <label for="ssid">SSID:</label><br>
                          <input name="SSID" type="text" id="ssid" value=""><br><br>
                          <label for="password">Password:</label><br>
                          <input name="PASSWORD" type="text" id="password" value=""><br><br>
                          <input type="submit" value="Submit">
                        </form>
                    </body>
                </html>
                """
        return html
        


简而言之,为了快速解释这个问题,我创建了这个套接字并监听请求:

        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.bind(('', 80))
        s.listen(5)
        while True:
            try:
                conn, addr = s.accept()
                print('Got a connection from %s' % str(addr))
                request = conn.recv(1024)
                print('Content = %s' % str(request))
                response = self.getWebPage()
                conn.send(response)
                conn.close()
            except KeyboardInterrupt:
                print("break")
                break

我试图弄清楚如何修改上面的代码以识别用户请求的页面,并在用户提交表单时读取任何表单参数,这可能吗?

这是供参考的 HTML 表单。

<html>
                    <head>
                        <meta name="viewport" content="width=device-width, initial-scale=1">
                    </head>
                    <body>
                        <h1>Configure Wifi</h1>
                        <form action="/submit" method="post">
                          <label for="ssid">SSID:</label><br>
                          <input name="SSID" type="text" id="ssid" value=""><br><br>
                          <label for="password">Password:</label><br>
                          <input name="PASSWORD" type="text" id="password" value=""><br><br>
                          <input type="submit" value="Submit">
                        </form>
                    </body>
                </html>

【问题讨论】:

  • 当你打印出request = conn.recv(1024) 返回的值时,你看到了什么?您确定该请求将适合 1024 字节吗?你了解HTTP request 的样子吗?
  • This 可能感兴趣。
  • @larsks 返回的值如下所示: b'GET /submit?SSID=Djxjdk&PASSWORD=Fjdjd HTTP/1.1\r\nHost: 192.168.4.1\r\nUpgrade-Insecure-Requests: 1\r\nAccept: text/html,应用程序/xhtml+xml,应用程序/xml;q=0.9,/;q=0.8\r\nUser-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1\r\n接受-Language: en-US,en;q=0.9\r\nAccept-Encoding: gzip, deflate\r\nConnection: keep-alive\r\n\r\n' 我确实将其更改为 GET 方法而不是POST,所以我可以看到请求中的值!
  • @larsks现在的问题是我正在使用正则表达式来尝试解析输入,这真的很难看。我知道在 micropython 中可能有更好的方法来解决这个问题,但我不确定该怎么做。非常感谢您的意见,非常感谢!
  • http.server 模块不是使用 socket 模块来创建套接字连接,而是定义了用于实现 HTTP 服务器的类,并使编写 Web 服务器应用程序更容易。它不适用于生产级的实施,但对您的应用来说已经足够了。

标签: python esp8266 micropython


【解决方案1】:

一旦您的缓冲区中有客户端请求,您需要对其进行解析以找到您正在寻找的各个部分。有一些模块(例如 GitHub 上的 microdot)可以使用 MicroPython 为您完成此任务。或者你可以自己滚动。

如果你选择自己做,它看起来像这样:

    def parse_query_string(query_string):
        query = {}
        query_params = query_string.split('&')
        for param in query_params:
            if (not '=' in param):  # A key with no value, like: 'red' instead of 'color=red'
                key=param
                query[key] = ''
            else:
                key, value = param.split('=')
                query[key] = value
                
        return query


    def parse_http_request(req_buffer):
        assert (req_buffer != b''), 'Empty request buffer.'

        req = {}
        req_buffer_lines = req_buffer.decode('utf8').split('
')
        req['method'], target, req['http_version'] = req_buffer_lines[0].split(' ', 2)  # Example: GET /route/path HTTP/1.1
        if (not '?' in target):
            req['path'] = target
        else:  # target can have a query component, so /route/path could be something like /route/path?state=on&timeout=30
            req['path'], query_string = target.split('?', 1)
            req['query'] = parse_query_string(query_string)

        req['headers'] = {}
        for i in range(1, len(req_buffer_lines) - 1):
            if (req_buffer_lines[i] == ''):  # Blank line signifies the end of headers.
                break
            else:
                name, value = req_buffer_lines[i].split(':', 1)
                req['headers'][name.strip()] = value.strip()
                
        req['body'] = req_buffer_lines[len(req_buffer_lines) - 1]  # Last line is the body (or blank if no body.)
        
        return req

如果您将在request 中捕获的缓冲区取出并传递给parse_http_request(),如下所示:

req = parse_http_request(request)

您将获得一本名为req 的字典,其中包含您需要的标题、正文等内容。

当您从网页发布表单数据时,您最终会在正文中得到查询字符串。您可以使用parse_query_string() 函数获取发送参数的python 字典。如果将表单方法设置为 GET,您将在 `req['query'] 中找到作为字典的参数。

Mozilla 的开发人员网络有一个 HTTP 事务期间发生的示例。 https://developer.mozilla.org/en-US/docs/Web/HTTP/Session

上面的函数是从一个名为 Thimble 的模块中提取的,我正在开发该模块以在我的 ESP 设备上运行 API。您可以在项目的 GitHub 页面上找到更多示例,这些示例可能会对您想要做的事情有所帮助。

【讨论】:

    猜你喜欢
    • 2021-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-03
    相关资源
    最近更新 更多