0 关注者 · 3 帖子

React 是一个用于构建用户界面的 JavaScript 库。它由 Facebook、Instagram 和一个由个人开发者和公司组成的社区负责维护。

官方网站

文章 Michael Lei · 八月 7, 2024 6m read

表的图形显示

在这里,我们将说明如何以图形方式显示数据收集的结果。 项目的输出将如下所示:

image

我使用的是本地计算机。 如果你在服务器上,注意使用正确的 IP 地址。

首先,导入需要的三个类(注意,我们将晚些时候编辑它们):

你可以使用 xml 并将其导入系统。

规范将创建调度类和实现模板。 如果你想详细了解此过程,可以读一读我同事 Eduard Lebedyuk 写的文章

设置 API

注意,在此演示中我们将使用 Basic Authorization。 我们还假设 Sample_DBExpansion_Data.DBAnalysisInfo 和 Sample_DBExpansion_Data.GlobalAnalysisInfo 表中已经有数据。 如果没有,返回数据收集获取数据。

  1. 首先,创建一个可以让我们访问数据的端点: image

填写相同的名称,除非你打算为 react 应用自行定制代码。

  1. 点击 Save,然后测试我们的 API。 打开 Postman,发送以下请求(确保使用正确的授权): image

输出应该类似于:

{
    "data": [
        {
            "Name": "c:\\intersystems\\irishealth\\mgr\\training\\",
            "Date": "2023-04-30 15:23:58",
            "DBUsedSize": 2010,
            "DBAllocSize": 2060
        },
        {
            "Name": "c:\\intersystems\\irishealth\\mgr\\training\\",
            "Date": "2023-05-01 09:01:42",
            "DBUsedSize": 2010,
            "DBAllocSize": 2060
        },
        {
            "Name": "c:\\intersystems\\irishealth\\mgr\\training\\",
            "Date": "2023-05-03 13:57:40",
            "DBUsedSize": 150,
            "DBAllocSize": 2060
        }
    ]
}

Next let's send a GET request to http://localhost:52776/Sample/dbAnalysis/globals/all. 查看响应是否给出了global列表,其信息如下: (注意,如果global有类名,则名称将默认为类名)

        {
            "Name": "someName.cls",
            "UsedMB": 4.2,
            "AllocatedMB": 5.7
        }

现在,测试一个特定的global,例如 Errors。 Send a GET request http://localhost:52776/Sample/dbAnalysis/global/Errors. 检查输出是否类似于:

        {
            "Name": "ERRORS",
            "UsedMB": 0.4,
            "Date": "2023-04-30 15:23:58",
            "AllocatedMB": 0.45
        },
        {
            "Name": "ERRORS",
            "UsedMB": 0.43,
            "Date": "2023-05-01 09:01:42",
            "AllocatedMB": 0.49
        },
        {
            "Name": "ERRORS",
            "UsedMB": 0.1,
            "Date": "2023-05-03 13:57:40",
            "AllocatedMB": 0.13
        }

And finally, let's send a GET request to http://localhost:52776/Sample/dbAnalysis/globals/table/1000 这将给我们带来global的增长,我们将把它的输出导入到 react-app 的 'Tabled Data' 部分。注意,1000 只是指我们应该回溯多少天。 具体完全由你自己决定。 请随意在 src/components/TableInputBar.js 文件中自定义。 注意 <Table timeBack={1000} numGlobals={searchInput}/>. 在这里输入你希望在 react 应用上查看的天数。

响应应该是一个类似这样的对象列表:

       {
            "Name": "nameOfGlobal",
            "ClassName": "AriTesting.DemoTableElad.cls",
            "OldMB": 0.14,
            "OldDate": "2023-04-30 15:23:58",
            "NewMB": 0.14,
            "NewDate": "2023-05-03 13:57:40",
            "Change": "0"
        }

所有请求都已就绪,可以创建 Web 应用了。注意,如果没有得到预期响应,那么你应该返回并解决问题,然后再继续创建依赖于这些响应的应用。

创建 Web 应用的步骤

  1. 首先,创建通用 react 应用。注意,你需要在本地开发计算机上安装节点(至少版本 14),但在服务器上不需要。 如果尚未安装,请前往这里。 如果你不确定是否已经安装,可以从终端运行此命令:
node --version
  1. 现在,安装通用 react 应用,我们将根据需要更改。 这就像运行一样简单:
npx create-react-app data-collection-graphs
  1. 如果这是你第一次执行此操作,则可能需要几分钟。 完成后,我们将得到如下文件夹: image

  2. 你的通用(后续我们将定制)react 应用现在可以运行了。 查看一下:

npm start

你应该自动重定向到显示以下内容的标签页(如果没有,请转到 http://localhost:3000/): image

  1. 接下来,我们根据需求进行定制。 使用 ^C 从终端停止应用。 下载此仓库中的 src 文件夹,替换之前的命令自动创建的目录中的文件夹。 在 data-collection-graphs 目录中,安装 chart-js 和 react-chartjs-2,如下所示:
npm install --save chart.js
npm install --save react-chartjs-2

src/components 文件夹中有调用 API 端点来获取图表数据的 JavaScript 代码。 如果你的服务器没有在 localhost:80 上运行,那么你应该更改 BarChart.jsDBChart.jsSingleGlobalHistoryChart.jsTableData.js 中的 baseUrl(以及 base64 编码的基本授权,如果这是你选择使用的授权方法)。

  1. 使用 npm start 加载页面,你应该会获得带有数据库分析的页面。

注意:你可能会看到一个空白页,打开 Web 开发者工具后会出现错误:Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:52775/Sample/dbAnalysis/globals/all. (Reason: CORS preflight response did not succeed). Status code: 404.

如果是这种情况,则将以下类方法添加到生成的 Sample.DBExpansion.Util.REST.disp.cls 中:

ClassMethod OnHandleCorsRequest(pUrl As %String) As %Status
{
     //s ^CORS("OnHandleCorsRequest")="Handled"
     #; Get the origin
     Set tOrigin=$Get(%request.CgiEnvs("HTTP_ORIGIN"))
     #; Allow requested origin
     Do ..SetResponseHeaderIfEmpty("Access-Control-Allow-Origin",tOrigin)
     #; Set allow credentials to be true
     Do ..SetResponseHeaderIfEmpty("Access-Control-Allow-Credentials","true")
     #; Allow requested headers
     Set tHeaders=$Get(%request.CgiEnvs("HTTP_ACCESS_CONTROL_REQUEST_HEADERS"))
     Do ..SetResponseHeaderIfEmpty("Access-Control-Allow-Headers",tHeaders)
     #; Allow requested method
     Set tMethod=$Get(%request.CgiEnvs("HTTP_ACCESS_CONTROL_REQUEST_METHOD"))
     Do ..SetResponseHeaderIfEmpty("Access-Control-Allow-Method",tMethod)
     Return $$$OK
}

由于我们在这里没有使用委派验证,请求将由 CSPSystem 用户执行。 这意味着我们必须为 CSPSystem 用户提供适合我们所做查询的角色。 在这里了解更多相关信息(或者,为 CSPSystem 用户提供从命名空间/数据库读取数据所需的角色即可。)

配置跨域资源共享 (CORS) 后,刷新页面,你应该可以看到图表开始填充,如此页顶部所示。

请随意使用代码,并根据你的组织进行改进或定制!

如果你有改进的建议,也请告诉我。

数据分析仓库位于这里

0
0 91
文章 Nicky Zhu · 六月 29, 2024 8m read

通过 REST API 将前端 React 应用程序与 IRIS 数据库等后端服务集成,是构建健壮网络应用程序的强大方法。但是,开发人员经常遇到的一个障碍是跨源资源共享(CORS)问题,由于网络浏览器强制执行的安全限制,该问题可能会阻止前端访问后端的资源。在本文中,我们将探讨在将 React Web 应用程序与 IRIS 后端服务集成时如何解决 CORS 问题。

创建Schema

我们首先定义一个名为 Patients 的简单Schema:

Class Prototype.DB.Patients Extends %Persistent [ DdlAllowed ]
{

Property Name As %String;

Property Title As %String;

Property Gender As %String;

Property DOB As %String;

Property Ethnicity As %String;
}

您可以在表中插入一些虚假数据进行测试。我个人认为 Mockaroo 在创建假数据时非常方便。它可以让你把虚拟数据下载为 .csv 文件,直接导入管理门户。

定义 REST 服务

然后,我们定义几个 REST 服务

Class Prototype.DB.RESTServices Extends %CSP.REST
{

Parameter CONTENTTYPE = "application/json";
    
XData UrlMap [ XMLNamespace = "http://www/intersystems.com/urlmap" ]
{
<Routes>
    <Route Url = "/patients" Method="Get" Call="GetPatients"/>
    <Route Url = "/patient/:id" Method="Post" Call="UpdatePatientName"/>
</Routes>
}

ClassMethod GetPatients() As %Status
{
    #Dim tStatus As %Status = $$$OK

    #Dim tSQL As %String = "SELECT * FROM Prototype_DB.Patients ORDER BY Name"

    #Dim tStatement As %SQL.Statement = ##class(%SQL.Statement).%New()
    
    Set tStatus = tStatement.%Prepare(tSQL)

    If ($$$ISERR(tStatus)) Return ..ReportHttpStatusCode(..#HTTP400BADREQUEST, tStatus)

    #Dim tResultSet As %SQL.StatementResult

    Set tResultSet = tStatement.%Execute()

    #Dim tPatients As %DynamicArray = []

    While (tResultSet.%Next()) {
        #Dim tPatient As %DynamicObject = {}
        Set tPatient.ID = tResultSet.ID
        Set tPatient.Name = tResultSet.Name
        Set tPatient.Title = tResultSet.Title
        Set tPatient.Gender = tResultSet.Gender
        Set tPatient.DOB = tResultSet.DOB
        Set tPatient.OrderedBy = tResultSet.OrderedBy
        Set tPatient.DateOfOrder = tResultSet.DateOfOrder
        Set tPatient.DateOfReport = tResultSet.DateOfReport
        Set tPatient.Ethnicity = tResultSet.Ethnicity
        Set tPatient.HN = tResultSet.HN
        Do tPatients.%Push(tPatient)
    }
    Do ##class(%JSON.Formatter).%New().Format(tPatients)
    Quit $$$OK
}

ClassMethod UpdatePatientName(pID As %Integer)
{
    #Dim tStatus As %Status = $$$OK
    #Dim tPatient As Prototype.DB.Patients = ##class(Prototype.DB.Patients).%OpenId(pID,, .tStatus)
    If ($$$ISERR(tStatus)) Return ..ReportHttpStatusCode(..#HTTP404NOTFOUND, tStatus)
    #Dim tJSONIn As %DynamicObject = ##class(%DynamicObject).%FromJSON(%request.Content)
    Set tPatient.Name = tJSONIn.Name
    Set tStatus = tPatient.%Save()
    If ($$$ISERR(tStatus)) Return ..ReportHttpStatusCode(..#HTTP400BADREQUEST, tStatus)
    #Dim tJSONOut As %DynamicObject = {}
    Set tJSONOut.message = "patient name updated successfully"
    Set tJSONOut.patient = ##class(%DynamicObject).%New()
    Set tJSONOut.patient.ID = $NUMBER(tPatient.%Id())
    Set tJSONOut.patient.Name = tPatient.Name
    Do ##class(%JSON.Formatter).%New().Format(tJSONOut)
    Quit $$$OK
}

}

然后,我们继续在管理门户上注册网络应用程序

  1. 在管理门户上导航至 系统管理 -> 安全 -> 应用程序 -> Web 应用程序 -> 创建 Web 应用程序"。
  2. 填写下表 image
  3. Prototype/DB/RESTServices.cls 中定义的 API 将在 http://localhost:52773/api/prototype/* 中提供。
  4. 现在,我们可以使用 Postman 请求端点,以验证 API 是否可用 image

创建前端

我使用 Next.js 创建了一个简单的前端,Next.js 是一个流行的 React 框架,它能让开发人员轻松创建服务器端渲染(SSR)的 React 应用程序。

我的前端是一个简单的表格,用于显示存储在 IRIS 中的患者数据,并提供更新患者姓名的功能。

 const getPatientData = async () => {
    const username = '_system'
    const password = 'sys'
    try {
        const response: IPatient[] = await (await fetch("http://localhost:52773/api/prototype/patients", {
        method: "GET",
        headers: {
          "Authorization": 'Basic ' + base64.encode(username + ":" + password),
          "Content-Type": "application/json"
        },
      })).json()
      setPatientList(response);
    } catch (error) {
      console.log(error)
    }
  }

看似一切准备就绪,但如果直接运行 "npm run dev",就会出现 CORS :(

解决 CORS 问题

当网络应用程序尝试向不同域上的资源发出请求,而服务器的 CORS 策略限制了客户端的访问,导致请求被浏览器阻止时,就会发生 CORS 错误。我们可以在前台或后台解决 CORS 问题。

设置响应标头(后台方法)

首先,我们在定义 API 端点的同一个 Prototype/DB/RESTServices.cls 调度器类中添加 HandleCorsRequest 参数。

Parameter HandleCorsRequest = 1;

然后,我们在派发器类中定义 OnPreDispatch 方法来设置响应头。

   ClassMethod OnPreDispatch() As %Status
    {
        Do %response.SetHeader("Access-Control-Allow-Credentials","true")
        Do %response.SetHeader("Access-Control-Allow-Methods","GET, PUT, POST, DELETE, OPTIONS")
        Do %response.SetHeader("Access-Control-Max-Age","10000")
        Do %response.SetHeader("Access-Control-Allow-Headers","Content-Type, Authorization, Accept-Language, X-Requested-With")
        quit $$$OK
    } 

使用 Next.js 代理(前端方法)

next.config.mjs文件中添加重写函数:

 /** @type {import('next').NextConfig} */
        const nextConfig = {
            async rewrites() {
                return [
                    {
                        source: '/prototype/:path',
                        destination: 'http://localhost:52773/api/prototype/:path'
                    }
                ]
            }
        };

        export default nextConfig;

并将所有提取 url 从 http://127.0.0.1:52773/api/prototype/:path 更新为 `/prototype/:path

最终产物

在这里,我放置了前台页面的代码:

'use client'
import { NextPage } from "next"
import { useEffect, useState } from "react"
import { Table, Input, Button, Modal } from "antd";
import { EditOutlined } from "@ant-design/icons";
import type { ColumnsType } from "antd/es/table";
import base64 from 'base-64';
import fetch from 'isomorphic-fetch'
const HomePage: NextPage = () => {
  const [patientList, setPatientList] = useState<IPatient[]>([]);
  const [isUpdateName, setIsUpdateName] = useState<boolean>(false);
  const [patientToUpdate, setPatientToUpdate] = useState<IPatient>()
  const [newName, setNewName] = useState<string>('')
  const getPatientData = async () => {
    const username = '_system'
    const password = 'sys'
    try {
        const response: IPatient[] = await (await fetch("http://localhost:52773/api/prototype/patients", {
        method: "GET",
        headers: {
          "Authorization": 'Basic ' + base64.encode(username + ":" + password),
          "Content-Type": "application/json"
        },
      })).json()
      setPatientList(response);
    } catch (error) {
      console.log(error)
    }
  }

  const updatePatientName = async () => {
     let headers = new Headers()
    const username = '_system'
    const password = 'sys'
    const ID = patientToUpdate?.ID
    try {
      headers.set("Authorization", "Basic " + base64.encode(username + ":" + password))
      const response: { message: string, patient: { ID: number, Name: string } } =
        await (await fetch(`http://127.0.0.1:52773/api/prototype/patient/${ID}`, {
        method: "POST",
          headers: headers,
          body: JSON.stringify({Name: newName})
      })).json()
      let patientIndex = patientList.findIndex((patient) => patient.ID == response.patient.ID)
      const newPatientList = patientList.slice()
      newPatientList[patientIndex] = {...patientList[patientIndex], Name: response.patient.Name}
      setPatientList(newPatientList);
      setPatientToUpdate(undefined);
      setNewName('')
      setIsUpdateName(false)
    } catch (error) {
      console.log(error)
    }
  }
  const columns: ColumnsType = [
    {
      title: 'ID',
      dataIndex: 'ID',
    },
    {
      title: "Title",
      dataIndex: "Title"
    },
    {
       title: 'Name',
      dataIndex: 'Name',
      render: (value, record, index) => {
        return (
          <div className="flex gap-3">
            <span>{value}</span>
            <span className="cursor-pointer" onClick={() => {
              setIsUpdateName(true)
              setPatientToUpdate(record)
            }}><EditOutlined /></span>
          </div>
        )
      }
    },
    {
      title: "Gender",
      dataIndex: 'Gender'
    },
    {
      title: "DOB",
      dataIndex: "DOB"
    },
    {
      title: "Ethnicity",
      dataIndex: "Ethnicity"
    },
    {
      title: 'HN',
      dataIndex: "HN"
    }
  ]

  useEffect(() => {
    getPatientData();
  }, [])
  return (
    <>
      <div className="min-h-screen">
        <Modal open={isUpdateName} footer={null} onCancel={() => {
          setIsUpdateName(false);
          setPatientToUpdate(undefined);
          setNewName('')
        }}>
          <div className="flex flex-col gap-5 pb-5">
            <div>
              <div className="text-2xl font-bold">Update name for patient {patientToUpdate?.ID} </div>
            </div>
            <div className="text-xl">Original Name: { patientToUpdate?.Name}</div>
            <div className="flex flex-row gap-2">
              <Input className="w-60" value={newName} onChange={(event) => setNewName(event.target.value)} />
              <Button type="primary" onClick={updatePatientName}>OK</Button>
              <Button onClick={() => {
                setIsUpdateName(false)
                setPatientToUpdate(undefined);
                setNewName('')
              }}>Cancel</Button>
            </div>
          </div>
        </Modal>
      <div className="flex justify-center py-10">
        <div className="h-full w-4/5">
          {patientList.length > 0 && <Table dataSource={patientList} columns={columns}/>}
        </div>
        </div>
      </div>
    </>
  )
}

export default HomePage

现在访问 http://localhost:3000, 可见如下效果: image

项目的Github存储库: https://github.com/xili44/iris-react-integration

感谢 Bryan (@Bryan Hoon), Julian(@Julian Petrescu) 和 Martyn (@Martyn Lee),感谢新加坡办事处提供的支持和专业知识。

0
0 197
文章 Hao Ma · 一月 15, 2021 6m read

假设您想编写一些真正的web应用程序,例如medium.com网站的简单克隆。这类应用程序可以在后端使用任何不同的语言编写,也可以使用前端的任何框架编写。编写这样一个应用程序有很多方法,你也可以看看这个项目。它为完全相同的应用程序提供了一堆前端和后端实现。您可以轻松组合它们,任何所选前端应该与任何后端搭配。

我来介绍一下这个使用后端InterSystems IRIS来实现后端的相同的应用程序。  

0
0 259