API调用过多,导致错误“发送后无法设置标头”

时间:2019-07-23 12:19:40

标签: node.js reactjs express redux

我必须检索多个下拉列表的数据,这些下拉列表在组件安装时一个接一个地出现,而且我已经在具有所有导航栏并保存其余组件的主组件中设置了通知/状态调用。问题在于这些通知/状态调用会干扰进行调用的组件,并导致错误在发送后无法设置标头。如果我关闭通知/状态调用,则可以检索下拉列表的数据,但是现在我的应用程序可以执行这两个操作之一,也可以不执行两者。我不知道这是否是设计缺陷,还是我需要集成Promises,现在我使用fetch进行所有调用。我的后端是Express JS,并在PHP中调用另一个后端。我还使用了代理服务器,因为create-react-app和express不能在同一端口上。

我已经研究了问题,并尝试在服务器中使用Promise.resolve,但这不能解决问题。

CreateEndpoint.js

componentDidMount(){
     this.getData()
  }

  async getData() {

    let data = [
            "contact_phone","contact_point","contract_id",
            "customer","display_name","ep_id","equip_model",
            "equip_type","location","maintenance_type",
            "manufacturer","region","tech_support_group"
            ]

    let attributesObj = {};
    for(let i = 0; i < data.length; i++){
      let newData = await fetch(`/api/endpointAttributeValues/${data[i]}`).then(res => res.json()).catch(err => this.setState({redirect: true}))
      let attributeName = data[i]
      attributesObj[attributeName] = newData.data;
    }

    this.setState({attributeValues: attributesObj})
}

AsyncApp.js(所有组件都包含在此主要组件中)

class AsyncApp extends Component {

  constructor(props){
    super(props)

    this.startTimer = this.startTimer.bind(this)
    this.handleEvent = this.handleEvent.bind(this)
    this.handleClose = this.handleClose.bind(this)

    this.state = {
      redirect: false,
      maxSessionInactivity: null,
      showAlert: false,
      sinceLastCheck: ''
    }
  }

  async componentDidMount() {
    this._isMounted = true;
    this.show = null
    let self = this
    let messages;
    const { dispatch } = this.props
    await document.body.addEventListener("keypress", this.handleEvent);
    await document.body.addEventListener("click", this.handleEvent);

    if(this._isMounted){

      await fetch('/api/getStatus').then(res => res.json()).then(function(res){
        if(!res.data.is_active){
          self.setState({redirect: true})
        }
        console.log("IN GET STATUS ", res)
      })
      .catch(err => self.setState({redirect: true}))

      await fetch('/api/getFirstNotification')
      .then(res => res.json())
      .then(function(res){
        // if(res.status.errorOccured){
        //   self.setState({redirect: true})
        // }
        messages = res.data.messages
        dispatch(updateMessages(res.data.messages))
        self.setState({sinceLastCheck: res.data.since_last_check})
      })
      .catch(err => self.setState({redirect: true}))

    }
    await fetch('/api/getStatus').then(res => res.json()).then(res => this.setState({maxSessionInactivity: res.data.session_inactivity_minutes - 1 * 1000}));
     await this.startTimer()
     await console.log("STATE J", this.state)
     await this.interval(messages)
     await this.notifications()
  }

  startTimer() {
     this.firstTimer = setTimeout(function() {
        this.setState({showAlert: true})
     }.bind(this), 100000);
     this.lastTimer = setTimeout(function() {
        this.setState({redirect: true})
     }.bind(this), 600000)
  }

  handleEvent(e){
    console.log("event", e)
    clearTimeout(this.firstTimer)
    clearTimeout(this.lastTimer)
    this.startTimer()
  }

  async interval(messages){
    this.intervalStatus = await setInterval(async () => {
      await this.notify(messages)
    }, 60000)
  };

  async notifications(){

    const { dispatch } = this.props

    this.newNotifications = await setInterval( async () => {

      let data = { since_last_checked : this.state.sinceLastCheck }
      let res1 = await fetch('/api/getNotifications', {
        method:'POST',
        headers: {
          'Content-type': 'application/json',
          'accept': 'application/json'
        },
        body:JSON.stringify(data)
      })
      .then(res => res.json())
      .catch(err => console.log(err))

      console.log("NOTIFICATIONS NEXTTT", res1)

      if(res1 === undefined || res1.data === undefined || res1.data === null){
        this.setState({redirect: true})
      }

      if(res1 != undefined && res1.data != null) dispatch(updateMessages(res1.data.messages))

      let res2 = await fetch('/api/getStatus')
      .then(res => res.json())
      .catch(err => console.log(err))

      console.log("STATUSS", res2)

      if(res2 === undefined || res2.data === undefined || res2.data === null || res2.data.is_active === 'N' || res2.data.status === 'closed'){
        this.setState({redirect: true})
      }

    }, 5000)

  }

  handleClose(event){
   this.setState({showAlert: false})
  }

  componentWillUnmount(){
    console.log("componentWillUnmount!!!!")
    this._isMounted = false
    clearInterval(this.newNotifications)
    clearInterval(this.intervalStatus)
    clearTimeout(this.firstTimer)
    clearTimeout(this.lastTimer)
    document.body.removeEventListener("keypress", this.handleEvent);
    document.body.removeEventListener("click", this.handleEvent);
  }

  notify(arr){
    if(arr === undefined) return null

    if(typeof arr === 'string'){
      return toast.success(`${arr}`)
    }

    if(arr.length < 4){
      let messages = arr.map(message => toast.success(`${message.message_text}`))
      return messages
    } else {
      return toast.success(`You have ${arr.length} new Notifications!`)
    }
  };

  render() {
    const { classes } = this.props

    if (this.state.redirect) return <Redirect to="/logout" />

    return (
      <div>
        <ToastContainer />
        <Snackbar
          anchorOrigin={{
            vertical: 'bottom',
            horizontal: 'left',
          }}
          open={this.state.showAlert}
          autoHideDuration={6000}
          onClose={this.handleClose}
        >
          <MySnackbarContentWrapper
            onClose={this.handleClose}
            variant="warning"
            message="Your session will expire in one minute!"
          />
        </Snackbar>

        <ThemeProvider theme={theme}>
          <div className={classes.root}>
            <CssBaseline />
            <nav className={classes.drawer}>
              <Hidden xsDown implementation="css">
                <Navigator PaperProps={{ style: { width: drawerWidth } }} />
              </Hidden>
            </nav>
            <div className={classes.appContent}>
              <Header onDrawerToggle={this.handleDrawerToggle} />
              <main className={classes.mainContent}>
                <div>
                  <Switch>
                    <Route exact path="/EditContracts/:contractId/sections/:section" component={EditSection} />
                    <Route exact path="/EditContracts/:contractId" component={EditContract} />
                    <Route exact path="/EditUsers/:userId" component={EditUser} />
                    <Route exact path="/EditEndpoints/:epId" component={EditEndpoint} />
                    <Route exact path="/EditContracts/:contractId/addSection" component={CreateSection} />
                    <Route exact path="/Contracts/List" component={Contracts} />
                    <Route exact path="/Contracts/Create" component={CreateContract} />
                    <Route exact path="/Contracts/Import" component={ImportContract} />
                    <Route exact path="/Users/List" component={Users} />
                    <Route exact path="/Users/Create" component={CreateUser} />
                    <Route exact path="/Endpoints/Create" component={CreateEndpoint} />
                    <Route exact path="/Endpoints/List" component={Endpoints} />
                    <Route exact path="/Pug_Community" component={PugCommunity} />
                    <Redirect exact from="/Users" to="/Users/List" />
                    <Redirect exact from="/Endpoints" to="/Endpoints/List" />
                    <Redirect exact from="/Contracts" to="/Contracts/List" />
                  </Switch>
                </div>
              </main>
            </div>
          </div>
        </ThemeProvider>
      </div>
    )
  }
}

App.js(AsyncApp.js位于此组件中)

export default class App extends Component {
  render() {
    return (
      <Switch>
        <Route exact path="/signin" component={SignIn} />
        <Route exact path="/changePassword" component={ChangePassword} />
        <Route exact path="/logout" component={Logout} />
        <Route path="/" component={AsyncApp} />
      </Switch>
    )
  }
}

Root.js(App.js位于此组件中)

const store = configureStore()

export default class Root extends Component {
  render() {
    return (
      <Provider store={store}>
        <Router>
         <App />
       </Router>
      </Provider>
    )
  }
}

index.js(这是所有调用都通过WebSocket通过PHP的另一个API重新路由的地方)

app.get(`/api/endpointAttributeValues/:attr`, function(req, res) {

  var attribute = req.params.attr;

  console.log("Attribute123", attribute);

  stompClient = new StompJs.Client({
      connectHeaders: { login: "", passcode: "" },
      brokerURL: brokerURL,
      reconnectDelay: 200,
      //debug: (str) =>  { console.log("DEBUG", str) },
      onConnect: () => { sendCommandToPUG("get_values_for_attribute", "endpoints",  {"attribute_name": attribute}, stompClient, req.session.userID, req.session.pugSessionID); },
      onUnhandledMessage: (messageReply) => {

          reply = JSON.parse(messageReply.body);
          if(reply.status.errorOccured)
              console.log("PUG-SERVER RETURNED ERROR: " + reply.status.errorText);

          replyData = reply.data; //here is where you will find all the data that matches your query (eg: fields/rows)
          res.json(reply);
          //stompClient.disconnect();
      },
  });

  stompClient.activate();

});

主要错误是发送标头后无法设置标头。我不知道是否需要在所有呼叫中指定标题,甚至忘记请求。也许因为我使用的是redux,所以我不应该在组件中进行API调用,而要获取较大的列表,请在初始加载时收集我的主要组件上的所有信息,并将其存储在redux中?

2 个答案:

答案 0 :(得分:0)

错误“错误:发送标头后无法设置标头”。表示您已经处于“正文”或“完成”状态,但是某些函数试图设置标头或statusCode。当您看到此错误时,请尝试查找在某些正文已被写入之后尝试发送标头的任何内容。例如,查找意外调用两次的回调,或发送正文后发生的任何错误。

我认为onUnhandledMessage方法被多次调用并导致问题。尝试在其中添加console.log,以便进行调试。

希望这会有所帮助!

答案 1 :(得分:0)

为防止此类错误,您可以在任何res之前添加“ return”。

那样,发送数据后,您就不再需要我编辑数据了。

您的案例示例:

return res.json(reply);