【问题标题】:React - 404 not found反应 - 404 未找到
【发布时间】:2020-03-27 17:21:33
【问题描述】:

我在从 React 向我的端点 /send-email 发出请求时遇到问题。我已将 flask-mail 配置为进行异步调用。

这是前端发出请求的方式:

 emailClient(event, index){
  let {clientCount} = this.state;
  var headers = {
      'Content-Type': 'application/json',
      'Access-Control-Allow-Origin': true,
      Authorization: `Bearer ${window.localStorage.authToken}`
    }
  const {userId} = this.props
  const client = this.state.clients[index];
  const data = {
    client: client
  };
  const url = `${process.env.REACT_APP_WEB_SERVICE_URL}/send-email/${userId}`;
  axios.post(url, data, {headers: headers})
    .then((res) => {
      this.setState({
        clientCount: clientCount +1
      });
      console.log(data);
    })
    .catch((err) => {
    });
  };

渲染:

  render() {
    const orders = this.state.clients;
    const { clients, youtube_urls, loadedVideosCount, currentPlayingIndex } = this.state;

    return (
      <div>
        <h1 className="title is-1">Jukebox</h1>
          {this.state.isLoading}
          {
            clients.map((client, index) => {
               /* 
              Obtain preview from state.previews for current artist index 
              */
              const audio = youtube_urls[index]
              /* 
              Render current client data, and corresponding audio url
              */
              return(
                <div key={index}>
                <ul>
                  <li><font color="#C86428">Client: </font><strong><font color="#6f4e37"> { client.name } </font></strong></li>
                  <li><font color="#C86428">Phone: </font><strong><font color="#6f4e37"> { client.phone } </font></strong></li>
                  <li><font color="#C86428">Email: </font><strong><font color="#6f4e37"> { client.mail } </font></strong></li>
                  <li><font color="#C86428">Artist: </font><strong><font color="#6f4e37"> { client.tracks[0].artist } </font></strong></li>
                  <li><font color="#C86428">Track: </font><strong><font color="#6f4e37"> { client.tracks[0].title } </font></strong></li>
                  <ReactPlayer 
                    url={ audio }
                    controls
                    width='50'
                    height='150'
                    onLoaded={() =>
                    this.setState(currentState => ({
                        loadedVideosCount: loadedVideosCount + 1,
                        currentPlayingIndex:
                          loadedVideosCount + 1 === youtube_urls.length ? 0 : -1,
                      }))
                    }
                    onStart={() => this.emailClient(index)} //<--------
                    onEnded={() =>
                    this.setState(currentState => ({
                        currentPlayingIndex: currentPlayingIndex + 1,
                      }))
                    }
                    playing={index === currentPlayingIndex}
                  />
                </ul></div>
              )
            })
          }
      </div>
    )
  };
};

export default Jukebox;

我收到以下错误:

POST http://localhost/send-email/1 404 (Not Found)

端点:

@task_bp.route('/send-email/<user_id>', methods=['GET','POST'])
def send_email(user_id):
   try:
     #business logic
     send_async_email.delay()

     return jsonify(response_object), 200    
   except (exc.IntegrityError, ValueError):
     db.session.rollback()
     return jsonify(response_object), 400

nginx反向代理配置:

  location /send-email {
    proxy_pass        http://web:5000;
    proxy_redirect    default;
    proxy_set_header  Upgrade $http_upgrade;
    proxy_set_header  Connection "upgrade";
    proxy_set_header  Host $host;
    proxy_set_header  X-Real-IP $remote_addr;
    proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header  X-Forwarded-Host $server_name;
  }

邮件服务所依赖的“网络”和“客户端”服务是使用docker-compose 构建的:

services:

  web:
    build:
      context: ./services/web
      dockerfile: Dockerfile-dev
    volumes:
      - './services/web:/usr/src/app'

    ports:
      - 5001:5000
    environment:
      - FLASK_ENV=development
      - APP_SETTINGS=project.config.DevelopmentConfig
      - DATABASE_URL=postgres://postgres:postgres@web-db:5432/web_dev 
      - DATABASE_TEST_URL=postgres://postgres:postgres@web-db:5432/web_test
      - SECRET_KEY=my_precious
      - GOOGLE_APPLICATION_CREDENTIALS=/usr/src/app/project/api/resources/youtube/urls/z.json  
    depends_on:  
      - web-db
      - redis

  client:
    build:
      context: ./services/client
      dockerfile: Dockerfile-dev
    volumes:
      - './services/client:/usr/src/app'
      - '/usr/src/app/node_modules'
    ports:
      - 3000:3000
    environment:
      - NODE_ENV=development
      - REACT_APP_WEB_SERVICE_URL=${REACT_APP_WEB_SERVICE_URL}
    depends_on:
      - web

每次我构建它们时,我都会这样做:

 $export REACT_APP_WEB_SERVICE_URL=http://localhost 

有什么问题吗?


注意:

如果我使用 POSTMAN 直接向端点发布到http://localhost:5001/send-email/1,它可以工作。

【问题讨论】:

  • 你读过错误吗?该错误表示未找到您的端点,这似乎与 react 或 react-player 无关。您的 URL 是否缺少端口? (比如localhost:1234/send-mail/1)?您是否有一个端点处理程序在其末尾接收用户 ID?身份验证失败了吗?
  • 我使用代理声明端口location /send-email { proxy_pass http://web:5000;
  • 错误提示您的 URL 是 http://localhost,但您向我们展示的端点是 http://localhost:5001
  • 您的路线是send-email,而您的网址正在向send-mail @@发送请求
  • 浏览器发送OPTIONS请求来检查后端服务器是否允许跨源请求。例如:如果您向其发出异步请求的 url 不在同一个域中,或者例如 localhostlocalhost:5001 是不同的域,浏览器将首先发送一个 OPTIONS 请求,然后发布服务器确认它浏览器会发送实际的发布请求吗?需要检查的几件事:1)如果代理传递将请求传递给正确的上游服务器 2)您正在使用来自前端的正确端点

标签: reactjs flask-mail react-player


【解决方案1】:

在客户端的localhost: 之后添加端口5001 为我修复了它:

   const url = `${process.env.REACT_APP_WEB_SERVICE_URL}:5001/send-email/${userId}`;

控制台:

XHR finished loading: OPTIONS "http://localhost:5001/send-email/1".

我仍然不明白为什么,因为所有其他数十个端点都分配了5001,但它是固定的。任何澄清原因的答案都将获得赏金。

【讨论】:

    【解决方案2】:

    在您的 Nginx 前端网络服务器配置中,您设置了一个反向代理以将请求传递给 /send-email 到主机名为 web 的网络应用程序服务器侦听端口 5000

    location /send-email {
        proxy_pass        http://web:5000;
    

    这意味着您可以直接向/send-email/1 请求。

    您在问题中指出,OPTIONS 请求在您的浏览器中执行并获得 404 响应,而 POST 是预期的。

    原因是使用 axios 客户端执行的请求是预检的。

    对于preflight requests,浏览器首先向路径发出OPTIONS请求,以确定该请求是否可以安全发送。

    许多特征决定了预检请求,其中之一是Content-Type header 的值与application/x-www-form-urlencoded | multipart/form-data | text/plain 不同

    OPTIONS 请求的响应以确认POST 请求是安全的,必须有Access-Control-Allow-* 标头这样说明。例如,POST 请求中的请求标头是:

    • Content-Type
    • Authorization

    为了从服务器确认POST 请求是安全的,OPTIONS 请求的响应标头必须是

    Access-Control-Allow-Origin: http://localhost
    Access-Control-Allow-Methods: HEAD, OPTIONS, GET, POST
    Access-Control-Allow-Headers: Authorization, Content-Type, Access-Control-Allow-Origin
    Access-Control-Max-Age: 86400
    

    您可以在 Nginx 配置中处理此预检响应。例如

        location /send-email {
            # ...
            if ($request_method = OPTIONS) {
                add_header 'Access-Control-Allow-Origin' 'http://localhost';
                add_header 'Access-Control-Allow-Methods' 'HEAD, OPTIONS, GET, POST';
                add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, Access-Control-Allow-Origin';
                add_header 'Access-Control-Allow-Credentials' 'true';
                add_header 'Access-Control-Max-Age' 86400;
                return 204;
            }
        }
    

    或者,您可以在视图函数中处理 OPTIONS 请求

    from functools import wraps
    
    def cors(f):
        '''Handle preflight OPTIONS request'''
        @wraps
        def wrapper(*args, **kwargs):
            if request.method != 'OPTIONS':
                return make_response(f(*args, **kwargs))
    
            response = current_app.make_default_options_response()
            access_control_headers = {
                'Access-Control-Allow-Origin': 'http://localhost',
                'Access-Control-Allow-Methods': 'HEAD, OPTIONS, GET, POST',
                'Access-Control-Max-Age': '86400',
                'Access-Control-Allow-Credentials': 'true',
                'Access-Control-Allow-Headers': 'Authorization, Content-Type, Access-Control-Allow-Origin',
            }
            response.headers.extend(access_control_headers)
            return response
        return wrapper
    
    @cors
    @task_bp.route('/send-email/<user_id>', methods=['GET','POST'])
    def send_email(user_id):
        # ...
    

    【讨论】:

    • 感谢您的回答。我忘记在函数中添加'Access-Control-Allow-Origin': true,它一直存在,甚至在我添加端口之前。请参考我在emailClient() 函数上的编辑。我希望它使您的答案有效。
    • POST 请求中设置的所有标头都需要在Access-Control-Allow-Headers 中列出,以用于OPTIONS 响应。
    猜你喜欢
    • 1970-01-01
    • 2018-05-03
    • 2018-11-27
    • 1970-01-01
    • 2019-07-31
    • 2019-02-15
    • 2012-05-18
    • 2019-02-05
    • 1970-01-01
    相关资源
    最近更新 更多