0 Seguidores · 2 Postagens

 React é uma biblioteca JavaScript usada para construir interfaces de usuário. É mantida pelo Facebook, Instagram e uma comunidade de desenvolvedores individuais e corporações.

Site oficial.

Pergunta Guilherme Silva · Jun. 10, 2025

Estamos desenvolvendo uma aplicação em React e essa aplicação terá seus próprios usuários/senhas. Estamos fazendo alguns testes de autenticação, e estamos procurando conhecer melhor JWT do Iris. Entretanto, segundo a documentação, precisamos passar um body com um usuário do Iris para então receber o token JWT. A nossa preocupação é que essa primeira chamada não parece ser segura, já que precisamos enviar esse body aberto para então conseguir um token. Alguma sugestão de como enviar esse body de forma oculta? utilizamos como draft um post da comunidade:
 

3
0 67
Artigo Heloisa Paiva · Jul. 4, 2024 9m read

Integrar aplicações frontend de React com serviços backend como a base de dados IRIS através de APIs REST pode ser uma forma poderosa de contruir aplicações web robustas. No entanto, um obstáculo comum que os desenvolvedores costumam encontrar é o problema de Cross-Origin Resource Sharing (CORS), que pode impedir que o frontend acesse os recursos no backend devido a restrições de segurança impostas pelos navegadores web. Nesse artigo, exploraremos como abordar os problemas de CORS ao integrar aplicações web de React com serviços backend de IRIS.

Criando o esquema

Começamos definindo um esquema simples chamado Pacientes:

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;
}

Você pode inserir alguns dados de teste na tabela. Pessoalmente, acho o Mockaroo útil quando se trata de criar dados falsos. Ele permite fazer o download dos dados de teste como um arquivo .csv que pode ser importado diretamente no Portal de Administração.

Definição de serviços REST

Logo, definimos alguns serviços 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
}

}

Logo, seguimos para registrar a aplicação web no portal de administração.

  1. No Portal de Adminsitração, navegue a Administração do Sistema -> Segurança -> Aplicação - Aplicação Web -> Criar Nova Aplicação Web.
  2. Preencha o formulário confome mostrado a seguir image
  3. As APIs definidas em Prototype/DB/RESTServices.cls estarão disponíveis em http://localhost:52773/api/prototype/*
  4. Agora podemos verificar que as APIs estão disponíveis solicitando os endpoints usando o Postman. image

Criando o frontend

Eu utilizei o Next.js para criar um frontend simples. Next.js é uma framework popular de React que permite que os desenvolvedores construam aplicações React renderizados do lado do servidor (SSR) com facilidade.

O meu frontend é uma tabela simples que mostra os dados de pacientes armazenados no IRIS e oferece a funcionalidade para atualizar os nomes dos pacientes.

 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)
    }
  }

Parece que temos tudo pronto, mas se executarmos npm run dev diretamente, obtemos um erro de CORS :(

Resolvendo CORS

Um erro de CORS ocorre quando uma aplicação web tenta fazer uma solicitação a um recurso em domínio diferente e a política de CORS do servidor restringe o acesso desde a origem do cliente, resultando em que a solicitação seja bloqueada pelo navegador. Podemos resolver o problema de CORS tanto no frontend como no backend.

Estabelecer headers de resposta (o enfoque de backend)

Primeiro, adicionamos o parâmetro HandleCorsRequest à mesma classe de dispatch Prototype/DB/RESTServices.cls onde definimos os endpoints da API.

Parameter HandleCorsRequest = 1;

Logo, definimos o método OnPreDispatch dentro de sua classe dispatcher para estabelecer os headers de resposta.

   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
    } 

Uso do proxy Next.js (o enfoque frontend)

No seu arquivo next.config.mjs, adicione a função de reescrita (rewrite) da seguinte maneira:

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

        export default nextConfig;

E atualize qualquer URL de fetch desde http://127.0.0.1:52773/api/prototype/:path a /prototype/:path.

O produto final

Para continuação deixo o código para a página frontend:

'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

Agora, quando visitar http://localhost:3000, isso é o que verá: image

Repositório Github para o projeto: https://github.com/xili44/iris-react-integration

Gostaria de agradecer a Bryan (@Bryan Hoon), Julian(@Julian Petrescu) e Martyn (@Martyn Lee), da oficina de Singapura, por seu apoio e experiência.

0
0 89