在Gatsby / @ReachRouter中处理预提取的动态客户端路由

时间:2019-10-15 23:35:04

标签: javascript reactjs gatsby reach-router

我有这种情况。

  • 用户在地址栏中输入/company/<company-id>
  • 由于该应用与后端完全分开,因此需要预取公司。
  • 正常用户流量为/login-> /company/。我很好地处理了这种情况,只需导航到/company/<whatever id is first in the prefetch>即可。
  • 但是如果您加载 WITH 这个ID怎么办?我有解决方案,但我认为我对路线选择有些误解。

您可以假设我的预取工作正常,并且下面的代码段仅在companyState.success为true时触发。如我所说,它正在工作。 我是通过

手动处理的
// pretty sure i can handle this with regex better to capture other cases
// but that's beside the point for the scope of this question
const urlId = +location.pathname.replace("/company/", "")
const checkCompany = !!companyState.data.find(d => d.id === urlId)
if(checkCompany){
  company.set(urlId)
}
else{
  navigate("/404")
}

如果company.set(<company:id>)确实进行了更新,我将在其中使用钩子,它将预取视图所需的所有其他信息。而且,company是一个自定义上下文挂钩,因此它在我的应用程序中无处不在。

是否有更好的方法来处理?手动检查路径名似乎很麻烦。 您可以假设我的gatsby_node.js具有正确的定义以允许客户端路由。

这是我的路由定义:(这是我放在pages文件夹中的内容)

const DashboardPage = () => (
  <ProtectedRoute>
    <Router>
      <Company path="/company/*" />
    </Router>
  </ProtectedRoute>
)

最后在components文件夹中,

const Company = ({location}) => (
    <Router>
      <Main path="/:companyId">
        <Summary path="/" />
        .... other dashboard routes
      </Main>
    </Router>
)

2 个答案:

答案 0 :(得分:0)

您必须假定客户端代码始终可以由恶意行为者更改。最终,您必须在后端确保用户只能请求他应该看到的资源。

您对客户端的解决方案对我来说似乎不错。除了手动检查URL路径然后重定向之外,我没有其他方法。

通过登录,需要为您的用户分配一个加密安全的cookie或令牌(例如JSON Web令牌),以便您始终可以确定其身份。每当公司ID路由到您的前端时,您的前端都需要将用户身份发送到您的后端。只有在那里,您才能免受代码操作的伤害。您的后端需要检查用户是否可以查看此页面。

  • 如果用户可以:您的后端发送页面数据
  • 如果用户不能这样做:您的后端发送“未授权”消息,并且您的前端重定向

这样,即使有人操纵您的客户端代码并取消了重定向,黑客也会盯着一个无用的空白页。

摘要:

您在客户端的方法很好。发送公司数据之前,请确保后端检查用户的身份。

答案 1 :(得分:0)

因此,我不用手动处理路由,而是仅使用ReachRouter的导航来模拟history.push(),以避免重新呈现整个页面。 (类似地,状态更改也具有跟踪历史的好处)。后端受到身份验证令牌的完全保护,因此无需担心。

我下面的策略将处理这些情况:

  • 用户输入或应用导航至/company(应用将预取>默认情况下获取第一家公司>导航至/company/{id}
  • 用户键入或应用导航至/company/{id}(应用将预提取>导航至/company/{id}>由该路由触发的组件将检查id的有效性,否则导航至404)

我创建的策略是

  • 创建一个默认情况下将加载加载屏幕的组件,并预取所述公司。
  • 默认调用pages文件夹中的所说组件,在我的情况下,/company应该是pages > company.jspages > company > index.js
  • 如果预取成功,请导航到/company/{id},它是所述组件的子级,纯粹是客户端路由。
  • 确保gatsby-node.js具有必要的createPages定义,以允许对所有/company/*进行客户端路由
  • 手动检查当前位置是否为/company,以便当用户键入/company/{id}时拒绝重定向以捕获第二种情况

如果我显示代码,最好不要使用自定义的内置挂钩。

useRequest仅给出一个axios类,其中true参数表明它是已验证的必需请求。 useApi仅提供了一个方便的(异步等待)功能,该功能包括dispatch的redux状态并调用api本身。我正在使用thunk,所以successerror是标准的。

const CompanyDefault = ({location}) => {

  const [loading, toggle] = useState(true)

  const request = useRequest(true)
  const companyApi = useApi(request, getCompanies)
  const companyState = useSelector(({company}) => company)
  const redirectToCompany = async () => await navigate(clientRoutes.COMPANY(companyState.data[0].id))

  const fetchCompanies = async () => {
    await companyApi()
    toggle(false)
  }

  useEffect(() => {
    if(companyState.data.length === 0){
      fetchCompanies()
    } 
  }, [])

  if(loading){
    return <Loading />
  }
  else if(companyState.error || companyState.data.length === 0){
    return <Error callApi={companyApi} />
  }
  else if(companyState.success && (location.pathname === "/company" || location.pathname === "/company/")){
    redirectToCompany()
  }

  return (
    <Router>
      <Company path="company/:companyId/*" />
    </Router>
  )
}

export default CompanyDefault

因此,公司模块将只是

const Company = ({companyId}) => (
    // you now have companyId! 
    // do magic here, and check if that company id is valid.
    <Router>
      <Main path="/">
        <Summary path="/" />
        .... other dashboard routes
      </Main>
    </Router>
)

希望这更干净。如果有更好的方法,请告诉我! :D