เป็นวิธีที่เหมาะสมในการโทร AJAX ในองค์ประกอบคืออะไร?


40

ฉันกำลังพัฒนาองค์ประกอบที่กำหนดเองสำหรับ Joomla! 3.x และต้องการโทร AJAX ภายในเพื่อเรียกข้อมูลบางอย่าง วิธีที่เหมาะสมในการทำคืออะไร?


คำแนะนำที่สำคัญคือไม่ทำลายการไหลของ Joomla เช่นองค์ประกอบไม่กี่ isten ajax ขอเกี่ยวกับเหตุการณ์ AfterAroute และทำภารกิจและฆ่าคำขอที่นี่เอง ทำให้เกิดข้อผิดพลาดที่ยากต่อการตรวจแก้จุดบกพร่อง
Shyam

คุณหมายถึง - อย่าปิดแอพ คุณสามารถอธิบายเพิ่มเติมหรือไม่
Dmitry Rekun

ใช่ถ้า joomla ปิดแอพมันจะดีที่สุด ดังนั้นส่วนขยายของคุณจะได้รับการดูแลรักษา
Shyam

ยังไม่เข้าใจอย่างถ่องแท้ สิ่งที่ฉันกำลังพูดถึงคือ $ app-> close () ในตัวควบคุม คุณหมายถึงสิ่งเดียวกันหรือไม่ :)
Dmitry Rekun

ใช่พูดถึงจุดเดียวกัน ทำไมเราควรปิดแอพในคอนโทรลเลอร์ขณะเดียวกันก็จะทำโดย joomla เอง
Shyam

คำตอบ:


47

โปรดทราบว่าคำตอบนี้มีอายุสองสามปีแล้วและไม่ได้รับการอัปเดต อย่าลังเลที่จะแก้ไข / แสดงความคิดเห็นหากคุณคิดว่าบางสิ่งไม่ถูกต้องอีกต่อไป

บทคัดย่อ

แทบจะไม่มีวิธีที่เป็นทางการในการจัดการกับสิ่งนี้มันขึ้นอยู่กับความซับซ้อนและจำนวนที่คุณต้องการใช้รูปแบบ MVC ในการทำงาน

ด้านล่างนี้เป็นวิธีแก้ปัญหาที่เป็นไปได้สิ่งที่ควรใช้กับ Joomla 2.5 และ 3.x ไม่มีการแสดงรหัสสำหรับงานคัดลอก - วาง แต่เป็นแนวคิดทั่วไป

ก่อน Joomla! 3.2 componentสิ่งเดียวที่คุณจำเป็นต้องใช้ตัวอย่างด้านล่างนี้เป็น หลังจาก Joomla 3.2 (สำหรับงานที่มีความซับซ้อนต่ำ) คุณสามารถจัดการการร้องขอจากโมดูลและปลั๊กอิน


การตอบสนอง HTML ทั่วไป (ตาม MVC ดั้งเดิม)

คุณURLสำหรับงานที่ต้องการที่จะมีลักษณะเช่นนี้

index.php?option=com_similar&task=abc&format=raw

คุณกว่าสร้างตัวควบคุมที่จะใช้มุมมองสมมติว่าAbcจะมีไฟล์ view.raw.html (เหมือนกับไฟล์มุมมองปกติ)

ด้านล่างคุณมีรหัสสำหรับสร้างการตอบกลับ HTML แบบดิบ:

/controller.php

public function abc() 
{
    // Set view

    // Joomla 2.5
    JRequest::setVar('view', 'Abc'); 

    // (use JInput in 3.x)
    $this->input->set('view', 'Abc');

    parent::display();
}

/views/abc/view.raw.php

<?php
defined('_JEXEC') or die;

jimport('joomla.application.component.view');

class SimilarViewAbc extends JViewLegacy
{
    function display($tpl = null)
    {
        parent::display($tpl);
    }
}

/views/abc/tmpl/default.php

<?php

echo "Hello World from /views/abc/tmpl/default.php";

หมายเหตุ: นี่เป็นวิธีแก้ปัญหาที่ฉันจะใช้ถ้าฉันต้องส่งคืน HTML (มันสะอาดกว่าและติดตามตรรกะของ Joomla!) สำหรับการส่งคืนข้อมูล JSON แบบง่าย ๆ ดูด้านล่างวิธีใส่ทุกอย่างในคอนโทรลเลอร์

Subcontrollers

หากคุณส่งคำขอ Ajax ไปที่ผู้ควบคุมย่อยเช่น:

index.php?option=com_similar&controller=abc&format=raw

กว่าชื่อ subcontroller คุณ (สำหรับมุมมองดิบ) abc.raw.phpจะต้องมีการ

ซึ่งหมายความว่าคุณจะ / อาจมีผู้ควบคุมบัญชีย่อย 2 คนชื่อ Abc

ถ้าคุณกลับ JSON ก็อาจทำให้ความรู้สึกที่จะใช้และformat=json abc.json.phpใน Joomla 2.5 ฉันมีปัญหาบางอย่างในการรับตัวเลือกนี้ให้ทำงาน (อย่างใดผลลัพธ์เสียหาย) ดังนั้นฉันจึงใช้ raw


การตอบสนอง JSON ที่ถูกต้อง (ติดตาม MVC ใหม่ / ดั้งเดิม)

หากคุณต้องการสร้างการตอบสนอง JSON ที่ถูกต้องให้ตรวจสอบหน้าเอกสารการสร้างเอาต์พุต JSON

// We assume that the whatver you do was a success.
$response = array("success" => true);
// You can also return something like:
$response = array("success" => false, "error"=> "Could not find ...");

// Get the document object.
$document = JFactory::getDocument();

// Set the MIME type for JSON output.
$document->setMimeEncoding('application/json');

// Change the suggested filename.
JResponse::setHeader('Content-Disposition','attachment;filename="result.json"');

echo json_encode($response);

โดยทั่วไปแล้วคุณจะใส่รหัสนี้ในตัวควบคุม (คุณจะเรียกรูปแบบที่จะส่งคืนข้อมูลที่คุณเข้ารหัส - สถานการณ์ทั่วไปมาก) หากคุณจำเป็นต้องดำเนินการเพิ่มเติมคุณยังสามารถสร้างมุมมอง JSON (view.json.php) ซึ่งคล้ายกับตัวอย่าง raw


ความปลอดภัย

ตอนนี้คำขอ Ajax ใช้งานได้อย่าเพิ่งปิดหน้านี้ อ่านด้านล่าง.

อย่าลืมตรวจสอบคำขอปลอม JSession::checkToken()เข้ามามีประโยชน์ที่นี่ อ่านเอกสารเกี่ยวกับวิธีเพิ่มการต่อต้านการปลอมแปลง CSRF ในแบบฟอร์ม


เว็บไซต์หลายภาษา

อาจเกิดขึ้นได้หากคุณไม่ส่งชื่อภาษาในคำขอ Joomla จะไม่แปลสตริงภาษาที่คุณต้องการ

ลองพิจารณาต่อท้าย lang param กับคำขอของคุณ (เช่น&lang=de)


Joomla! อินเตอร์เฟส Ajax

ใหม่ใน Joomla 3.2! - ช่วยให้คุณสามารถจัดการคำขอโดยไม่ต้องสร้างส่วนประกอบ

Joomla! Ajax Interface - Joomla ตอนนี้ให้วิธีที่มีน้ำหนักเบาในการจัดการคำขอ Ajax ในปลั๊กอินหรือโมดูล คุณอาจต้องการใช้ Joomla! Ajax Interface หากคุณยังไม่มีส่วนประกอบหรือถ้าคุณต้องการที่จะขอจากโมดูลที่มีอยู่แล้ว


9
คำตอบที่มีคุณภาพดีที่สุดที่ฉันเคยเห็นใน joomla.stackexchange.com จนถึงตอนนี้ทำได้ดีมากและมีวิธีในการยกระดับบาร์ ยอดเยี่ยมมาก!
NivF007

เห็นด้วย แต่เกี่ยวกับJRequestอะไร มันเลิกใช้แล้วหาก$this->inputฉันใช้ v3.x
Dmitry Rekun

1
JRequestผมจ่าหน้าความกังวลของคุณเกี่ยวกับ ขอบคุณ
Valentin Despa

3
คำตอบที่ดีแค่อยากจะพูดถึงว่ามีคลาส Joomla ตั้งแต่ 3.1 ที่จัดการผลผลิต JSON: API , การใช้งาน
fruppel

@ fl0r yeap, Valentin พูดถึงมันในValid JSON Responseส่วน
Dmitry Rekun

20

นี่เป็นคำตอบที่ล่าช้าสำหรับคำถามที่ได้รับคำตอบเป็นอย่างดีนี้ แต่ฉันต้องการเพิ่มโซลูชันการไล่ล่าสำหรับผู้ที่ต้องการวิธีง่าย ๆ ในการเข้าถึงข้อมูลส่วนประกอบของพวกเขาด้วยการโทร AJAX

ด้วยทุกรุ่น Joomla, ความเป็นไปได้ของบุคคลที่สามและแฮ็กที่ฉันพบในช่วงเวลาหลายวันของ googling นี่เป็นวิธีที่ง่ายที่สุดที่ฉันสามารถหาได้ - และข้อเสนอแนะได้รับการชื่นชมอย่างแน่นอน

  1. เพิ่มฟังก์ชั่นexecuteให้กับคอนโทรลเลอร์หลักที่มีอยู่ของฉัน
  2. สร้างผู้ควบคุมย่อยพร้อมฟังก์ชั่นสาธารณะสำหรับงานที่ฉันต้องการโทรหาด้วย AJAX
  3. ใช้คลาส JoomlaespesponseJson ในตัวเพื่อจัดการกับผลลัพธ์ ( มันดีจริงๆ! )

URL สำหรับโทร / สั่งงาน:

www.mysite.com/index.php?option=com_example&task=ForAjax.mytaskname

แก้ไขตัวควบคุมหลัก \ com_example \ controller.php

class ExampleController extends JControllerLegacy {
    public function display($cachable = false, $urlparams = false) {
        $app = JFactory::getApplication();
        $view = $app->input->getCmd('view', 'default');
        $app->input->set('view', $view);
        parent::display($cachable, $urlparams);
        return $this;
    }

    public function execute()
    {
        // Not technically needed, but a DAMN good idea.  See http://docs.joomla.org/How_to_add_CSRF_anti-spoofing_to_forms
        // JSession::checkToken();
        $task = JFactory::getApplication()->input->get('task');
        try
        {
            parent::execute($task);
        }
        catch(Exception $e)
        {
            echo new JResponseJson($e);
        }
    }
}

ใหม่ Subcontroller \ com_example \ controllers \ forajax.php

require_once JPATH_COMPONENT.'/controller.php';
class ExampleControllerForAjax extends ExampleController
{
    public function MyTaskName()
    {
        $app = JFactory::getApplication();

        $data['myRequest'] =$_REQUEST;
        $data['myFile'] =__FILE__;
        $data['myLine'] ='Line '.__LINE__;

        $app->enqueueMessage('This part was reached at line ' . __LINE__);
        $app->enqueueMessage('Then this part was reached at line ' . __LINE__);
        $app->enqueueMessage('Here was a small warning at line ' . __LINE__, 'warning');
        $app->enqueueMessage('Here was a big warning at line ' . __LINE__, 'error');

        $task_failed = false;
        echo new JResponseJson($data, 'My main response message',$task_failed);

        $app->close();
    }
}

แสดงผล JSON เอาต์พุต

{
    success: true,
    message: "My main response message",
    messages: {
        message: [
            "This part was reached at line 26",
            "Then this part was reached at line 27"
        ],
        warning: [
            "Here was a small warning at line 28"
        ],
        error: [
            "Here was a big warning at line 29"
        ]
    },
    data: {
        myRequest: {
            option: "com_example",
            task: "mytaskname",
            Itemid: null
        },
        myFile: "C:\mysite\components\com_example\controllers\forajax.php",
        myLine: "Line 24"
    }
}

11

คำตอบ Valentin เป็นสิ่งที่ดี แต่ค่อนข้างซับซ้อนเกินไปหากคุณต้องทำคือเพิ่ม ajax 1 หรือ 2 สายไปยังส่วนประกอบที่สร้างขึ้นแล้ว มันเป็นไปได้ที่ดีที่สุดที่จะได้รับไปด้วยไม่ได้ทำแยกต่างหากcontroller.raw.phpหรือview.raw.phpไฟล์

ในการโทร ajax นี้

index.php?format=raw&option=com_example&controller=job&task=keep_alive&tokenhash=1

ในส่วนควบคุมjobย่อย

public function keep_alive() {
    $this->ajax_check();

    //Do your processing and echo out whatever you want to return to the AJAX call
    header('HTTP/1.1 202 Accepted', true, 202);
    echo 'OK';

    JFactory::getApplication()->close();
}

// Verifies jtoken and does a basic check that this is actually an AJAX call
private function ajax_check() {
    if(!JSession::checkToken('GET') || !isset($_SERVER['HTTP_X_REQUESTED_WITH']) || strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) !== 'xmlhttprequest') {
        header('HTTP/1.1 403 Forbidden', true, 403);
        JFactory::getApplication()->close();
    }
}

7

คำตอบของ Valentin นั้นดี

ฉันชอบคอนโทรลเลอร์ json ที่จัดการการเข้ารหัสและการจัดการข้อผิดพลาดสำหรับสิ่งนี้ฉันสร้างคลาสฐาน json:

class itrControllerJson extends JControllerLegacy {

  /** @var array the response to the client */
  protected $response = array();

  public function addResponse($type, $message, $status=200) {

    array_push($this->response, array(
      'status' => $status,
      'type' => $type,
      'data' => $message
    ));

  }

  /**
   * Outputs the response
   * @return JControllerLegacy|void
   */
  public function display() {

    $response = array(
      'status' => 200,
      'type' => 'multiple',
      'count' => count($this->response),
      'messages' => $this->response
    );

    echo json_encode($response);
    jexit();
  }

}

คอนโทรลเลอร์นี้ขยายเพิ่มโดยคลาสคอนโทรลเลอร์ที่ทำงานได้ดังนี้:

require_once __DIR__.'json.php';

class componentControllerAddress extends itrControllerJson {
  public function get() {

    try {
      if (!JSession::checkToken()) {
        throw new Exception(JText::_('JINVALID_TOKEN'), 500);
      }
      $app = JFactory::getApplication();

      $id = $app->input->get('id', null, 'uint');
      if (is_null($id)) {
        throw new Exception('Invalid Parameter', 500);
      }

      $db = JFactory::getDbo();
      $query = $db->getQuery(true);
      $query->select('*');
      $query->from('#__table');
      $query->where('id = '.$db->quote($id));
      $db->setQuery($query);
      $response = $db->loadObject();

      $this->addResponse('message', $response, 200);

    } catch (Exception $e) {
      $this->addResponse('error', $e->getMessage(), 500);
    }

    $this->display();
  }
}

และคุณเรียกคำขอเช่นนี้:

index.php?option=com_component&task=address.get&format=json&id=1234&tokenhash=1

โทเค็นแฮชสร้างโดย JSession :: getFormToken () ดังนั้นการโทรที่สมบูรณ์อาจมีลักษณะเช่นนี้:

$link = JRoute::_('index.php?option=com_component&task=address.get&format=json&id=1234&'.JSession::getFormToken().'=1', false);

พารามิเตอร์ที่สองถูกตั้งค่าเป็น "false" เพื่อให้เราสามารถใช้สิ่งนี้ในการเรียกใช้จาวาสคริปต์โดยไม่ต้องเขียน xml อีกครั้ง


1
ดีมาก แต่ทำไมไม่ใช้JResponseJsonคลาสนี้จัดการมันล่ะ?
Dmitry Rekun

JResponseJson ได้รับการแนะนำใน Joomla 3
Anibal

ไม่มี Joomla SE ที่ฉันสามารถถามได้)
Harald Leithner

4

หากคุณแน่ใจว่า 100% ไม่มีปลั๊กอินของบุคคลที่สามที่เพิ่มเอาต์พุต Javascript ใด ๆ json_encode ล้วนๆก็ใช้ได้

แต่ ... ตัวอย่างเช่น JomSocial เพิ่ม "" ในเว็บไซต์ทั้งหมด

ดังนั้น ... เคล็ดลับที่มีประโยชน์ห่อ json_encode ด้วยแท็กและประมวลผลทางด้าน Javascript

echo '@START@' . json_encode(...) . '@END@';

3

คุณสามารถเข้าถึงคอนโทรลเลอร์ได้โดยตรงโดยใช้ชื่อคอนโทรลเลอร์ในงาน:

index.php?option=com_similar&task=controller.abc&format=raw

จะโทร: controller.raw.php (return เป็น raw)

index.php?option=com_similar&task=controller.abc

จะโทร: controller.php (return เป็น html ถ้าคุณไม่ได้ใช้die;)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.