从 Next JS 向 Microsoft Graph API 节点发送 Python Flask api 请求

问题描述 投票:0回答:1

我有一个使用 Next.js 构建的应用程序,它与 Python Flask API 交互以生成 DOCX 文件并通过 Microsoft Graph API 将其上传到 OneDrive。以下是相关代码的概述:

1.Next.js 代码(TypeScript):

import toPascalCase from "@/libs/toPascalCase";
import axios from "axios";

const DownloadTemplate = async (suratData: any, accessToken: String) => {
  try {
    const locResponse = await fetch("http://ip-api.com/json/");
    const locData = await locResponse.json();
    const city = locData.city || "Jakarta"; // Default to Jakarta if city is not found

    const requestBody = {
      template: "Memo",
      kepada:
        suratData?.surat?.kepadaId.map((k: any) => k.name).join(", ") || "",
      dari: suratData?.surat?.dariId.name || "",
      perihal: suratData?.konten?.perihal.name || "",
      sifat: suratData?.konten?.sifat.name || "",
      lampiran: suratData?.konten?.lampiran?.name || "-",
      tgl: suratData?.surat?.tanggal
        ? new Date(suratData.surat.tanggal).toLocaleDateString("id-ID", {
            day: "2-digit",
            month: "long",
            year: "numeric",
          })
        : "",
      Tempat: city,
      jabatan: toPascalCase(suratData?.surat?.dariId?.jabatan || ""),
      name: toPascalCase(suratData?.surat?.dariId?.name || ""),
      stringforqr: "testing dari front end node",
    };

    const formData = new FormData();
    formData.append("data", JSON.stringify(requestBody));

    console.log("Request Body:", requestBody);

    const response = await axios.post('http://127.0.0.1:5000/getTemplate', requestBody, {
      headers: {
        "Authorization": `Bearer ${accessToken}`,
        'Content-Type': 'application/json',
      },
    });

    console.log(response.data);
    return response.data;
  } catch (error) {
    console.error("Error downloading template:", error);
  }
};

export default DownloadTemplate;

2。 Python Flask 代码:

from flask import Flask, request, send_file
from flask_cors import CORS
from docx import Document
from docx.shared import Inches
import database
from docx2pdf import convert
import os
import tempfile
import pythoncom
import logging
import pythoncom
import qrcode
import requests
import tempfile
import json
from PIL import Image
from flask import request

app = Flask(__name__)
CORS(app)
# app.config['CORS_HEADERS'] = 'Content-Type'


def fill_invitation(template_path, output_path, data, qr_code_path=None):
    doc = Document(template_path)
    # doc = template_path
    # data['tgl'] = datetime.today().strftime("%d %B %Y")

    for paragraph in doc.paragraphs:
        for key, value in data.items():
            if key in paragraph.text:
                for index, run in enumerate(paragraph.runs):
                    newkey = '{{' + key + '}}'
                    if paragraph.runs[index-1].text == '{{' and run.text == key:
                        run.text = run.text.replace(key, value)
                        paragraph.runs[index-1].text = ''
                        paragraph.runs[index+1].text = ''
                    elif newkey == run.text:
                        if newkey == "qr":
                            run.text
                        run.text = run.text.replace(newkey, value)
                        
    for table in doc.tables:
        for row in table.rows:
            for cell in row.cells:
                for paragraph in cell.paragraphs:
                    for key, value in data.items():
                        if key in paragraph.text:
                            for index, run in enumerate(paragraph.runs):
                                newkey = '{{' + key + '}}'
                                if paragraph.runs[index-1].text == '{{' and paragraph.runs[index+1].text == '}}' and run.text == key:
                                    run.text = run.text.replace(key, value)
                                    paragraph.runs[index-1].text = ''
                                    paragraph.runs[index+1].text = ''
                                elif newkey == run.text:
                                    run.text = run.text.replace(newkey, value)
                                    
    if qr_code_path:
            inserted = False
            for paragraph in doc.paragraphs:
                if "{{qr}}" in paragraph.text:
                    logging.debug("QR code placeholder '{{qr}}' found.")
                    paragraph.text = paragraph.text.replace("{{qr}}", "")
                    run = paragraph.add_run()
                    run.add_picture(qr_code_path, width=Inches(1.3))
                    inserted = True
                    logging.debug("QR code inserted into paragraph.")

            if not inserted:
                logging.debug("QR code placeholder '{{qr}}' not found in the document.")
                
    doc.save(output_path)

@app.route('/getTemplate', methods=['POST'])

def docsFilling():
    data = request.get_json()
    # data = json.loads(request.form.get('data'))
    
    accessToken = request.headers.get('Authorization')
    print('data ================================== > ', data)
    print('accessToken ================================== > ', accessToken)
    templateData = data.get('template')
    listTipeSurat = database.getData(templateData)
    template = "retrieved_file.docx"

    Logo_link = 'Logo_PGASOL.jpg'
    stringqr = data['stringforqr']
    basewidth = 200

    logo = Image.open(Logo_link)
    wpercent = (basewidth / float(logo.size[0]))
    hsize = int((float(logo.size[1]) * float(wpercent)))
    logo = logo.resize((basewidth, hsize))

    QRcode = qrcode.QRCode(
        error_correction=qrcode.constants.ERROR_CORRECT_H
    )
    QRcode.add_data(stringqr)
    QRcode.make()
    QRcolor = 'Black'
    QRimg = QRcode.make_image(
        fill_color=QRcolor, back_color="white").convert('RGB')

    pos = ((QRimg.size[0] - logo.size[0]) // 2,
           (QRimg.size[1] - logo.size[1]) // 2)
    QRimg.paste(logo, pos)
    
    try:
        with tempfile.NamedTemporaryFile(delete=False, suffix='.png', dir='./temp/qr') as temp_qr:
            qr_code_path = temp_qr.name
            QRimg.save(qr_code_path)

        with open(template, "wb") as f:
            f.write(listTipeSurat.fileTemplate)

        with tempfile.NamedTemporaryFile(delete=False, suffix='.docx', dir='./temp/docs') as temp_docx:
            output_path = temp_docx.name
            print('output_path =====> ', output_path)
            fill_invitation(template, output_path, data, qr_code_path)
        
        with open(output_path, 'rb') as item:
            media_content = item.read()
        
        x = requests.put('https://graph.microsoft.com/v1.0/me/drive/special/approot:/test15.docx:/content', 
                                 headers={'Authorization': accessToken, 
                                          'Content-Type': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'}, 
                                 data=media_content)
        dataContent = x.json()
        return dataContent
        

    except Exception as e:
        return str(e), 500

    finally:
        # Ensure temporary files are cleaned up
        try:
            if os.path.exists(output_path):
                os.remove(output_path)
            if os.path.exists(qr_code_path):
                os.remove(qr_code_path)
        except PermissionError:
            pass  # Handle the permission error or log it
    

if __name__ == '__main__':
    logging.basicConfig(level=logging.ERROR)
    app.run(debug=True)

我的代码的目的是调用python中的端点来使用从下一个js发送到python的ruqest数据,并将它们转换为已自动填充的docx,然后通过microsoft graph api上传到onedrive。

问题:

当我从 Next.js 应用程序调用 Python API 端点时,我从 Microsoft Graph API 收到以下错误:

{
    "error": {
        "code": "itemNotFound",
        "innerError": {
            "client-request-id": "4a9e1e4f-1cc0-4725-944e-a95ec0d5e409",
            "date": "2024-08-08T13:22:57",
            "request-id": "4a9e1e4f-1cc0-4725-944e-a95ec0d5e409"
        },
        "message": "Item not found"
    }
}

但是当我通过 POSTMAN 应用程序测试它时,它运行没有任何问题,文档已创建并上传到我的一个驱动器。

问题:

  1. 这个问题是否与 CORS 问题有关,或者是代码级别的问题?
  2. 我可以采取哪些步骤来排除和解决此问题?

谢谢您的帮助!

python flask next.js microsoft-graph-api
1个回答
0
投票

首先,您向

/me/drive/{some-id}/content
提出的请求不存在 对于某个项目的内容,您应该调用其中之一

GET /drives/{drive-id}/items/{item-id}/content
GET /groups/{group-id}/drive/items/{item-id}/content
GET /me/drive/root:/{item-path}:/content
GET /me/drive/items/{item-id}/content
GET /shares/{shareIdOrEncodedSharingUrl}/driveItem/content
GET /sites/{siteId}/drive/items/{item-id}/content
GET /users/{userId}/drive/items/{item-id}/content

也就是说,您提出的请求也只有声明

scp=User.Read profile openid email
,而
Files.Read
是所需的最低许可。

我的建议是首先检查正在调用的 URL,并确保它是 https://learn.microsoft.com/en-us/graph/api/driveitem-get-content?view=graph-rest 中列出的 URL 之一-1.0&tabs=http

然后检查应用程序中的 Files.Read 权限。应将其添加为 Entra Portal 上的委托权限。

© www.soinside.com 2019 - 2024. All rights reserved.