| <!doctype html> |
| <html class="docs-version-3.11" lang="en" dir="ltr"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width,initial-scale=1"> |
| <meta name="ahrefs-site-verification" content="c2f7370ecf46173f4fb25f114e74c97e0a2976d4f02f61c9b00a9d7d34e34698"> |
| <meta name="generator" content="Docusaurus v2.0.0-beta.6"> |
| <link rel="search" type="application/opensearchdescription+xml" title="Apache APISIX® -- Cloud-Native API Gateway and AI Gateway" href="/opensearch.xml"> |
| <script type="application/ld+json">{"@context":"https://schema.org","@type":"WebSite","name":"Apache APISIX","url":"https://apisix.apache.org"}</script> |
| <script src="https://widget.kapa.ai/kapa-widget.bundle.js" data-website-id="24b59d9a-682e-4c3d-9e83-bf2ee85cdc19" data-project-name="APISIX" data-project-color="#E8442E" data-project-logo="https://static.apiseven.com/202202/apache-apisix.png" data-modal-disclaimer="This is a custom LLM for APISIX with access to all developer documentation, GitHub issues and discussions." data-modal-example-questions="How to set up canary release in APISIX?,How to develop a custom APISIX plugin?,How to use custom NGINX configuration in APISIX?,How to configure mTLS between clients and APISIX?,How to only allow a specific APISIX consumer to access special services or routes?" async></script><title data-react-helmet="true">Plugin Develop | Apache APISIX® -- Cloud-Native API Gateway and AI Gateway</title><meta data-react-helmet="true" property="og:image" content="https://static.apiseven.com/202202/apache-apisix.png"><meta data-react-helmet="true" name="twitter:image" content="https://static.apiseven.com/202202/apache-apisix.png"><meta data-react-helmet="true" property="og:url" content="https://apisix.apache.org/docs/apisix/3.11/plugin-develop/"><meta data-react-helmet="true" name="docsearch:language" content="en"><meta data-react-helmet="true" name="docsearch:version" content="3.11"><meta data-react-helmet="true" name="docsearch:docusaurus_tag" content="docs-docs-apisix-3.11"><meta data-react-helmet="true" name="robots" content="index,follow"><meta data-react-helmet="true" name="twitter:card" content="summary"><meta data-react-helmet="true" property="og:title" content="Plugin Develop | Apache APISIX® -- Cloud-Native API Gateway and AI Gateway"><meta data-react-helmet="true" name="description" content="This documentation is about developing plugin in Lua. For other languages, |
| see external plugin."><meta data-react-helmet="true" property="og:description" content="This documentation is about developing plugin in Lua. For other languages, |
| see external plugin."><link data-react-helmet="true" rel="shortcut icon" href="https://static.apiseven.com/202202/favicon.png"><link data-react-helmet="true" rel="canonical" href="https://apisix.apache.org/docs/apisix/3.11/plugin-develop/"><link data-react-helmet="true" rel="alternate" href="https://apisix.apache.org/docs/apisix/3.11/plugin-develop/" hreflang="en"><link data-react-helmet="true" rel="alternate" href="https://apisix.apache.org/zh/docs/apisix/3.11/plugin-develop/" hreflang="zh"><link data-react-helmet="true" rel="alternate" href="https://apisix.apache.org/docs/apisix/3.11/plugin-develop/" hreflang="x-default"><link data-react-helmet="true" rel="preconnect" href="https://38VC84A2WJ-dsn.algolia.net" crossorigin="anonymous"><link rel="preload" href="https://static.apiseven.com/202202/MaisonNeue-Medium.otf" as="font" type="font/otf" crossorigin> |
| <link rel="preload" href="https://static.apiseven.com/202202/MaisonNeue-Bold.otf" as="font" type="font/otf" crossorigin> |
| <link rel="preload" href="https://static.apiseven.com/202202/MaisonNeue-Light.otf" as="font" type="font/otf" crossorigin> |
| <link rel="preload" href="https://static.apiseven.com/202202/MaisonNeue-Demi.otf" as="font" type="font/otf" crossorigin> |
| <link rel="preload" href="https://static.apiseven.com/202202/MaisonNeue-ExtraBold.otf" as="font" type="font/otf" crossorigin> |
| <link rel="preload" href="https://apisix-website-static.apiseven.com/assets/js/runtime~main.0f7288cd.js" as="script"> |
| <link rel="preload" href="https://apisix-website-static.apiseven.com/assets/js/main.cd029f6b.js" as="script"> |
| <link rel="stylesheet" href="https://apisix-website-static.apiseven.com/assets/css/styles.8de0825e.css"> |
| |
| <script>var _paq=window._paq=window._paq||[];_paq.push(["disableCookies"]),_paq.push(["trackPageView"]),_paq.push(["enableLinkTracking"]),function(){var a="https://analytics.apache.org/";_paq.push(["setTrackerUrl",a+"matomo.php"]),_paq.push(["setSiteId","17"]);var e=document,p=e.createElement("script"),t=e.getElementsByTagName("script")[0];p.async=!0,p.src=a+"matomo.js",t.parentNode.insertBefore(p,t)}()</script> |
| |
| </head> |
| <body> |
| <script>!function(){function t(t){document.documentElement.setAttribute("data-theme",t)}var e=function(){var t=null;try{t=localStorage.getItem("theme")}catch(t){}return t}();t(null!==e?e:"light")}(),document.documentElement.setAttribute("data-announcement-bar-initially-dismissed",function(){try{return"true"===localStorage.getItem("docusaurus.announcement.dismiss")}catch(t){}return!1}())</script><div id="__docusaurus"> |
| <div><a href="#" class="skipToContent_OuoZ">Skip to main content</a></div><div class="announcementBar_axC9" style="background-color:#e8433e;color:white" role="banner"><div class="announcementBarPlaceholder_xYHE"></div><div class="announcementBarContent_6uhP">🤔 Introducing APISIX AI Gateway – Built for LLMs and AI workloads. <a target="_blank" rel="noopener noreferrer" href="/ai-gateway/"> Learn More</a></div><button type="button" class="clean-btn close announcementBarClose_A3A1" aria-label="Close"><svg viewBox="0 0 24 24" width="14" height="14" fill="currentColor"><path d="M24 20.188l-8.315-8.209 8.2-8.282-3.697-3.697-8.212 8.318-8.31-8.203-3.666 3.666 8.321 8.24-8.206 8.313 3.666 3.666 8.237-8.318 8.285 8.203z"></path></svg></button></div><nav class="navbar navbar--fixed-top navbarHideable_RReh"><div class="navbar__inner"><div class="navbar__items"><button aria-label="Navigation bar toggle" class="navbar__toggle clean-btn" type="button" tabindex="0"><svg width="30" height="30" viewBox="0 0 30 30" aria-hidden="true"><path stroke="currentColor" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" d="M4 7h22M4 15h22M4 23h22"></path></svg></button><a target="_parent" class="navbar__brand" href="/"><img src="/img/logo2.svg" alt="Apache APISIX®" class="themedImage_TMUO themedImage--light_4Vu1 navbar__logo"><img src="/img/logo2.svg" alt="Apache APISIX®" class="themedImage_TMUO themedImage--dark_uzRr navbar__logo"><b class="navbar__title">Apache APISIX®</b></a></div><div class="navbar__items navbar__items--right"><div class="navbar__item dropdown dropdown--hoverable dropdown--right"><a aria-current="page" class="navbar__link" target="_parent" href="/docs/">Docs</a><ul class="dropdown__menu"><li><a class="dropdown__link" target="_parent" href="/docs/apisix/getting-started/">Apache APISIX®️</a></li><li><a class="dropdown__link" target="_parent" href="/docs/apisix/next/dashboard/">Apache APISIX®️ Dashboard</a></li><li><a class="dropdown__link" target="_parent" href="/docs/ingress-controller/overview/">Apache APISIX®️ Ingress Controller</a></li><li><a class="dropdown__link" target="_parent" href="/docs/helm-chart/apisix/">Apache APISIX®️ Helm Charts</a></li><li><a class="dropdown__link" target="_parent" href="/docs/docker/build/">Apache APISIX®️ Docker</a></li><li><a class="dropdown__link" target="_parent" href="/docs/java-plugin-runner/development/">Apache APISIX®️ Java Plugin Runner</a></li><li><a class="dropdown__link" target="_parent" href="/docs/go-plugin-runner/getting-started/">Apache APISIX®️ Go Plugin Runner</a></li><li><a class="dropdown__link" target="_parent" href="/docs/python-plugin-runner/getting-started/">Apache APISIX®️ Python Plugin Runner</a></li><li><a class="dropdown__link" target="_parent" href="/docs/general/join/">General</a></li></ul></div><a class="navbar__item navbar__link" target="_parent" href="/blog/">Blog</a><a class="navbar__item navbar__link" target="_parent" href="/blog/tags/case-studies/">Case Studies</a><a class="navbar__item navbar__link" target="_parent" href="/downloads/">Downloads</a><a class="navbar__item navbar__link" target="_parent" href="/help/">Help</a><a class="navbar__item navbar__link" target="_parent" href="/team/">Team</a><div class="navbar__item dropdown dropdown--hoverable dropdown--right"><a class="navbar__link">Resources</a><ul class="dropdown__menu"><li><a class="dropdown__link" target="_parent" href="/showcase/">Showcase</a></li><li><a class="dropdown__link" target="_parent" href="/docs/general/code-samples/">Code Samples</a></li><li><a class="dropdown__link" target="_parent" href="/plugins/">PluginHub</a></li><li><a class="dropdown__link" target="_parent" href="/docs/general/join/">Community</a></li><li><a class="dropdown__link" target="_parent" href="/docs/general/events/">Events</a></li><li><a href="https://github.com/apache/apisix/milestones" target="_parent" rel="noopener noreferrer" class="dropdown__link">Roadmap</a></li></ul></div><div class="navbar__item dropdown dropdown--hoverable dropdown--right"><a href="#" class="navbar__link"><span><svg viewBox="0 0 20 20" width="20" height="20" aria-hidden="true" class="iconLanguage_zID8"><path fill="currentColor" d="M19.753 10.909c-.624-1.707-2.366-2.726-4.661-2.726-.09 0-.176.002-.262.006l-.016-2.063 3.525-.607c.115-.019.133-.119.109-.231-.023-.111-.167-.883-.188-.976-.027-.131-.102-.127-.207-.109-.104.018-3.25.461-3.25.461l-.013-2.078c-.001-.125-.069-.158-.194-.156l-1.025.016c-.105.002-.164.049-.162.148l.033 2.307s-3.061.527-3.144.543c-.084.014-.17.053-.151.143.019.09.19 1.094.208 1.172.018.08.072.129.188.107l2.924-.504.035 2.018c-1.077.281-1.801.824-2.256 1.303-.768.807-1.207 1.887-1.207 2.963 0 1.586.971 2.529 2.328 2.695 3.162.387 5.119-3.06 5.769-4.715 1.097 1.506.256 4.354-2.094 5.98-.043.029-.098.129-.033.207l.619.756c.08.096.206.059.256.023 2.51-1.73 3.661-4.515 2.869-6.683zm-7.386 3.188c-.966-.121-.944-.914-.944-1.453 0-.773.327-1.58.876-2.156a3.21 3.21 0 011.229-.799l.082 4.277a2.773 2.773 0 01-1.243.131zm2.427-.553l.046-4.109c.084-.004.166-.01.252-.01.773 0 1.494.145 1.885.361.391.217-1.023 2.713-2.183 3.758zm-8.95-7.668a.196.196 0 00-.196-.145h-1.95a.194.194 0 00-.194.144L.008 16.916c-.017.051-.011.076.062.076h1.733c.075 0 .099-.023.114-.072l1.008-3.318h3.496l1.008 3.318c.016.049.039.072.113.072h1.734c.072 0 .078-.025.062-.076-.014-.05-3.083-9.741-3.494-11.04zm-2.618 6.318l1.447-5.25 1.447 5.25H3.226z"></path></svg><span>English</span></span></a><ul class="dropdown__menu"><li><a href="/docs/apisix/3.11/plugin-develop/" target="_self" rel="noopener noreferrer" class="dropdown__link dropdown__link--active" style="text-transform:capitalize">English</a></li><li><a href="/zh/docs/apisix/3.11/plugin-develop/" target="_self" rel="noopener noreferrer" class="dropdown__link" style="text-transform:capitalize">简体中文</a></li></ul></div><div class="react-toggle toggle_2i4l react-toggle--disabled"><div class="react-toggle-track" role="button" tabindex="-1"><div class="react-toggle-track-check"><span class="toggle_iYfV">🌜</span></div><div class="react-toggle-track-x"><span class="toggle_iYfV">🌞</span></div><div class="react-toggle-thumb"></div></div><input type="checkbox" class="react-toggle-screenreader-only" aria-label="Switch between dark and light mode"></div><div class="searchBox_fBfG"><button type="button" class="DocSearch DocSearch-Button" aria-label="Search"><span class="DocSearch-Button-Container"><svg width="20" height="20" class="DocSearch-Search-Icon" viewBox="0 0 20 20"><path d="M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z" stroke="currentColor" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round"></path></svg><span class="DocSearch-Button-Placeholder">Search</span></span><span class="DocSearch-Button-Keys"></span></button></div></div></div><div role="presentation" class="navbar-sidebar__backdrop"></div></nav><div class="main-wrapper docs-wrapper docs-doc-page"><div class="docPage_GMj9"><button class="clean-btn backToTopButton_i9tI" type="button"><svg viewBox="0 0 24 24" width="28"><path d="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z" fill="currentColor"></path></svg></button><aside class="docSidebarContainer_k0Pq"><div class="sidebar_LIo8 sidebarWithHideableNavbar_CMI-"><a target="_parent" tabindex="-1" class="sidebarLogo_P87M" href="/"><img src="/img/logo2.svg" alt="Apache APISIX®" class="themedImage_TMUO themedImage--light_4Vu1"><img src="/img/logo2.svg" alt="Apache APISIX®" class="themedImage_TMUO themedImage--dark_uzRr"><b>Apache APISIX®</b></a><div class="sidebarVersionSwitch_0QIZ">Version:<div class="navbar__item dropdown dropdown--hoverable"><a class="navbar__link" href="/docs/apisix/3.11/getting-started/README/">3.11</a><ul class="dropdown__menu"><li><a class="dropdown__link" href="/docs/apisix/next/plugin-develop/"><div>Next</div></a></li><li><a class="dropdown__link" href="/docs/apisix/plugin-develop/"><div>3.14<div class="badge_6FVu Latest_oyqS">Latest</div></div></a></li><li><a class="dropdown__link" href="/docs/apisix/3.13/plugin-develop/"><div>3.13</div></a></li><li><a class="dropdown__link" href="/docs/apisix/3.12/plugin-develop/"><div>3.12</div></a></li><li><a aria-current="page" class="dropdown__link dropdown__link--active" href="/docs/apisix/3.11/plugin-develop/"><div>3.11</div></a></li><li><a class="dropdown__link" href="/docs/apisix/3.10/plugin-develop/"><div>3.10</div></a></li><li><a href="https://apache-apisix.netlify.app/docs/apisix/3.9/getting-started/readme/" target="_blank" rel="noopener noreferrer" class="dropdown__link"><span>3.9<svg width="12" height="12" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_wgqa"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></span></a></li><li><a href="https://apache-apisix.netlify.app/docs/apisix/3.8/getting-started/readme/" target="_blank" rel="noopener noreferrer" class="dropdown__link"><span>3.8<svg width="12" height="12" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_wgqa"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></span></a></li><li><a href="https://apache-apisix.netlify.app/docs/apisix/3.7/getting-started/readme/" target="_blank" rel="noopener noreferrer" class="dropdown__link"><span>3.7<svg width="12" height="12" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_wgqa"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></span></a></li><li><a href="https://apache-apisix.netlify.app/docs/apisix/3.6/getting-started/readme/" target="_blank" rel="noopener noreferrer" class="dropdown__link"><span>3.6<svg width="12" height="12" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_wgqa"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></span></a></li><li><a href="https://apache-apisix.netlify.app/docs/apisix/3.5/getting-started/readme/" target="_blank" rel="noopener noreferrer" class="dropdown__link"><span>3.5<svg width="12" height="12" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_wgqa"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></span></a></li><li><a href="https://apache-apisix.netlify.app/docs/apisix/3.4/getting-started/readme/" target="_blank" rel="noopener noreferrer" class="dropdown__link"><span>3.4<svg width="12" height="12" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_wgqa"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></span></a></li><li><a href="https://apache-apisix.netlify.app/docs/apisix/3.3/getting-started/readme/" target="_blank" rel="noopener noreferrer" class="dropdown__link"><span>3.3<svg width="12" height="12" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_wgqa"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></span></a></li><li><a href="https://apache-apisix.netlify.app/docs/apisix/3.2/getting-started/" target="_blank" rel="noopener noreferrer" class="dropdown__link"><span>3.2<svg width="12" height="12" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_wgqa"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></span></a></li><li><a href="https://apache-apisix.netlify.app/docs/apisix/3.1/getting-started/" target="_blank" rel="noopener noreferrer" class="dropdown__link"><span>3.1<svg width="12" height="12" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_wgqa"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></span></a></li><li><a href="https://apache-apisix.netlify.app/docs/apisix/3.0/getting-started/" target="_blank" rel="noopener noreferrer" class="dropdown__link"><span>3.0<svg width="12" height="12" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_wgqa"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></span></a></li><li><a href="https://apache-apisix.netlify.app/docs/apisix/2.15/getting-started/" target="_blank" rel="noopener noreferrer" class="dropdown__link"><span>2.15<svg width="12" height="12" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_wgqa"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></span></a></li><li><a href="https://apache-apisix.netlify.app/docs/apisix/2.14/getting-started/" target="_blank" rel="noopener noreferrer" class="dropdown__link"><span>2.14<svg width="12" height="12" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_wgqa"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></span></a></li><li><a href="https://apache-apisix.netlify.app/docs/apisix/2.13/getting-started/" target="_blank" rel="noopener noreferrer" class="dropdown__link"><span>2.13<svg width="12" height="12" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_wgqa"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></span></a></li><li><a href="https://apache-apisix.netlify.app/docs/apisix/2.12/getting-started/" target="_blank" rel="noopener noreferrer" class="dropdown__link"><span>2.12<svg width="12" height="12" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_wgqa"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></span></a></li><li><a href="https://apache-apisix.netlify.app/docs/apisix/2.11/getting-started/" target="_blank" rel="noopener noreferrer" class="dropdown__link"><span>2.11<svg width="12" height="12" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_wgqa"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></span></a></li><li><a href="https://apache-apisix.netlify.app/docs/apisix/2.10/getting-started/" target="_blank" rel="noopener noreferrer" class="dropdown__link"><span>2.10<svg width="12" height="12" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_wgqa"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></span></a></li><li><a href="https://apache-apisix.netlify.app/docs/apisix/2.9/getting-started/" target="_blank" rel="noopener noreferrer" class="dropdown__link"><span>2.9<svg width="12" height="12" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_wgqa"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></span></a></li><li><a href="https://apache-apisix.netlify.app/docs/apisix/2.8/getting-started/" target="_blank" rel="noopener noreferrer" class="dropdown__link"><span>2.8<svg width="12" height="12" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_wgqa"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></span></a></li><li><a href="https://apache-apisix.netlify.app/docs/apisix/2.7/getting-started/" target="_blank" rel="noopener noreferrer" class="dropdown__link"><span>2.7<svg width="12" height="12" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_wgqa"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></span></a></li><li><a href="https://apache-apisix.netlify.app/docs/apisix/2.6/getting-started/" target="_blank" rel="noopener noreferrer" class="dropdown__link"><span>2.6<svg width="12" height="12" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_wgqa"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></span></a></li><li><a href="https://apache-apisix.netlify.app/docs/apisix/2.5/getting-started/" target="_blank" rel="noopener noreferrer" class="dropdown__link"><span>2.5<svg width="12" height="12" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_wgqa"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></span></a></li><li><a href="https://apache-apisix.netlify.app/docs/apisix/2.4/getting-started/" target="_blank" rel="noopener noreferrer" class="dropdown__link"><span>2.4<svg width="12" height="12" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_wgqa"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></span></a></li></ul></div></div><nav class="menu thin-scrollbar menu_oAhv menuWithAnnouncementBar_IVfW"><ul class="theme-doc-sidebar-menu menu__list"><li class="theme-doc-sidebar-item-category menu__list-item menu__list-item--collapsed"><a class="menu__link menu__link--sublist" href="#">Getting Started</a></li><li class="theme-doc-sidebar-item-link menu__list-item"><a class="menu__link" href="/docs/apisix/3.11/installation-guide/">Installation</a></li><li class="theme-doc-sidebar-item-link menu__list-item"><a class="menu__link" href="/docs/apisix/3.11/architecture-design/apisix/">Architecture</a></li><li class="theme-doc-sidebar-item-category menu__list-item menu__list-item--collapsed"><a class="menu__link menu__link--sublist" href="#">Tutorials</a></li><li class="theme-doc-sidebar-item-category menu__list-item menu__list-item--collapsed"><a class="menu__link menu__link--sublist" href="#">Terminology</a></li><li class="theme-doc-sidebar-item-category menu__list-item menu__list-item--collapsed"><a class="menu__link menu__link--sublist" href="#">Plugins</a></li><li class="theme-doc-sidebar-item-category menu__list-item menu__list-item--collapsed"><a class="menu__link menu__link--sublist" href="#">API</a></li><li class="theme-doc-sidebar-item-category menu__list-item"><a class="menu__link menu__link--sublist menu__link--active" href="#">Development</a><ul style="display:block;overflow:visible;height:auto" class="menu__list"><li class="theme-doc-sidebar-item-link menu__list-item"><a class="menu__link" tabindex="0" href="/docs/apisix/3.11/building-apisix/">Building APISIX from source</a></li><li class="theme-doc-sidebar-item-link menu__list-item"><a class="menu__link" tabindex="0" href="/docs/apisix/3.11/build-apisix-dev-environment-on-mac/">Build development environment on Mac</a></li><li class="theme-doc-sidebar-item-link menu__list-item"><a class="menu__link" tabindex="0" href="/docs/apisix/3.11/support-fips-in-apisix/">Support FIPS in APISIX</a></li><li class="theme-doc-sidebar-item-link menu__list-item"><a class="menu__link" tabindex="0" href="/docs/apisix/3.11/external-plugin/">External Plugin</a></li><li class="theme-doc-sidebar-item-link menu__list-item"><a class="menu__link" tabindex="0" href="/docs/apisix/3.11/wasm/">Wasm</a></li><li class="theme-doc-sidebar-item-link menu__list-item"><a href="https://github.com/apache/apisix/blob/master/CODE_STYLE.md" target="_blank" rel="noopener noreferrer" class="menu__link" tabindex="0"><span>CODE_STYLE<svg width="13.5" height="13.5" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_wgqa"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></span></a></li><li class="theme-doc-sidebar-item-category menu__list-item menu__list-item--collapsed"><a class="menu__link menu__link--sublist" href="#" tabindex="0">internal</a></li><li class="theme-doc-sidebar-item-link menu__list-item"><a class="menu__link menu__link--active" aria-current="page" tabindex="0" href="/docs/apisix/3.11/plugin-develop/">Plugin Develop</a></li><li class="theme-doc-sidebar-item-link menu__list-item"><a class="menu__link" tabindex="0" href="/docs/apisix/3.11/debug-mode/">Debug mode</a></li></ul></li><li class="theme-doc-sidebar-item-link menu__list-item"><a class="menu__link" href="/docs/apisix/3.11/deployment-modes/">Deployment modes</a></li><li class="theme-doc-sidebar-item-link menu__list-item"><a class="menu__link" href="/docs/apisix/3.11/FAQ/">FAQ</a></li><li class="theme-doc-sidebar-item-category menu__list-item menu__list-item--collapsed"><a class="menu__link menu__link--sublist" href="#">Others</a></li><li class="theme-doc-sidebar-item-link menu__list-item"><a href="https://github.com/apache/apisix/blob/master/CHANGELOG.md" target="_blank" rel="noopener noreferrer" class="menu__link"><span>CHANGELOG<svg width="13.5" height="13.5" aria-hidden="true" viewBox="0 0 24 24" class="iconExternalLink_wgqa"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></span></a></li><li class="theme-doc-sidebar-item-link menu__list-item"><a class="menu__link" href="/docs/apisix/3.11/upgrade-guide-from-2.15.x-to-3.0.0/">Upgrade Guide</a></li></ul></nav><button type="button" title="Collapse sidebar" aria-label="Collapse sidebar" class="button button--secondary button--outline collapseSidebarButton_EBxv"><svg width="20" height="20" aria-hidden="true" class="collapseSidebarButtonIcon_AF9Q"><g fill="#7a7a7a"><path d="M9.992 10.023c0 .2-.062.399-.172.547l-4.996 7.492a.982.982 0 01-.828.454H1c-.55 0-1-.453-1-1 0-.2.059-.403.168-.551l4.629-6.942L.168 3.078A.939.939 0 010 2.528c0-.548.45-.997 1-.997h2.996c.352 0 .649.18.828.45L9.82 9.472c.11.148.172.347.172.55zm0 0"></path><path d="M19.98 10.023c0 .2-.058.399-.168.547l-4.996 7.492a.987.987 0 01-.828.454h-3c-.547 0-.996-.453-.996-1 0-.2.059-.403.168-.551l4.625-6.942-4.625-6.945a.939.939 0 01-.168-.55 1 1 0 01.996-.997h3c.348 0 .649.18.828.45l4.996 7.492c.11.148.168.347.168.55zm0 0"></path></g></svg></button></div></aside><main class="docMainContainer_Q970"><div class="container padding-top--md padding-bottom--lg"><div class="row"><div class="col docItemCol_zHA2"><div class="theme-doc-version-banner alert alert--warning margin-bottom--md" role="alert"><div>This is documentation for Apache APISIX® -- Cloud-Native API Gateway and AI Gateway <b>3.11</b>, which is no longer actively maintained.</div><div class="margin-top--md">For up-to-date documentation, see the <b><a href="/docs/apisix/plugin-develop/">latest version</a></b> (3.14).</div></div><div class="docItemContainer_oiyr"><article><span class="theme-doc-version-badge badge badge--secondary">Version: 3.11</span><div class="tocCollapsible_aw-L theme-doc-toc-mobile tocMobile_Tx6Y"><button type="button" class="clean-btn tocCollapsibleButton_zr6a">On this page</button></div><div class="theme-doc-markdown markdown"><header><h1>Plugin Develop</h1></header><p>This documentation is about developing plugin in Lua. For other languages, |
| see <a href="/docs/apisix/3.11/external-plugin/">external plugin</a>.</p><h2><a aria-hidden="true" tabindex="-1" class="anchor anchor__h2 anchorWithHideOnScrollNavbar_3ly5" id="where-to-put-your-plugins"></a>where to put your plugins<a class="hash-link" href="#where-to-put-your-plugins" title="Direct link to heading">#</a></h2><p>There are two ways to add new features based on APISIX.</p><ol><li>modify the source of APISIX and redistribute it (not so recommended)</li><li>setup the <code>extra_lua_path</code> and <code>extra_lua_cpath</code> in <code>conf/config.yaml</code> to load your own code. Your own code will be loaded instead of the builtin one with the same name, so you can use this way to override the builtin behavior if needed.</li></ol><p>For example, you can create a directory structure like this:</p><div class="codeBlockContainer_EiTO"><div class="codeBlockContent_X2I6 bash"><pre tabindex="0" class="prism-code language-bash codeBlock_UxnK thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_W6UD"><span class="token-line" style="color:#393A34"><span class="token plain">├── example</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│ └── apisix</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│ ├── plugins</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│ │ └── 3rd-party.lua</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│ └── stream</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│ └── plugins</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">│ └── 3rd-party.lua</span><br></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_V-PD clean-btn">Copy</button></div></div><div class="admonition admonition-note alert alert--secondary"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</h5></div><div class="admonition-content"><p>If you need to customize the directory of plugin, please create a subdirectory of <code>/apisix/plugins</code> under this directory.</p></div></div><p>Then add this configuration into your <code>conf/config.yaml</code>:</p><div class="codeBlockContainer_EiTO"><div class="codeBlockContent_X2I6 yaml"><pre tabindex="0" class="prism-code language-yaml codeBlock_UxnK thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_W6UD"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">apisix</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">extra_lua_path</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"/path/to/example/?.lua"</span><br></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_V-PD clean-btn">Copy</button></div></div><p>Now using <code>require "apisix.plugins.3rd-party"</code> will load your plugin, just like <code>require "apisix.plugins.jwt-auth"</code> will load the <code>jwt-auth</code> plugin.</p><p>Sometimes you may want to override a method instead of a whole file. In this case, you can configure <code>lua_module_hook</code> in <code>conf/config.yaml</code> |
| to introduce your hook.</p><p>Assumed your configuration is:</p><div class="codeBlockContainer_EiTO"><div class="codeBlockContent_X2I6 yaml"><pre tabindex="0" class="prism-code language-yaml codeBlock_UxnK thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_W6UD"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">apisix</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">extra_lua_path</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"/path/to/example/?.lua"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">lua_module_hook</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"my_hook"</span><br></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_V-PD clean-btn">Copy</button></div></div><p>The <code>example/my_hook.lua</code> will be loaded when APISIX starts, and you can use this hook to replace a method in APISIX. |
| The example of <a href="https://github.com/apache/apisix/blob/master/example/my_hook.lua" rel="noopener noreferrer">my_hook.lua</a> can be found under the <code>example</code> directory of this project.</p><h2><a aria-hidden="true" tabindex="-1" class="anchor anchor__h2 anchorWithHideOnScrollNavbar_3ly5" id="check-dependencies"></a>check dependencies<a class="hash-link" href="#check-dependencies" title="Direct link to heading">#</a></h2><p>if you have dependencies on external libraries, check the dependent items. if your plugin needs to use shared memory, it |
| needs to declare via <a href="/docs/apisix/3.11/customize-nginx-configuration/">customizing Nginx configuration</a>, for example :</p><div class="codeBlockContainer_EiTO"><div class="codeBlockContent_X2I6 yaml"><pre tabindex="0" class="prism-code language-yaml codeBlock_UxnK thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_W6UD"><span class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic"># put this in config.yaml:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token key atrule" style="color:#00a4db">nginx_config</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">http_configuration_snippet</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">|</span><span class="token scalar string" style="color:#e3116c"></span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c"> # for openid-connect plugin</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c"> lua_shared_dict discovery 1m; # cache for discovery metadata documents</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c"> lua_shared_dict jwks 1m; # cache for JWKs</span><br></span><span class="token-line" style="color:#393A34"><span class="token scalar string" style="color:#e3116c"> lua_shared_dict introspection 10m; # cache for JWT verification results</span><br></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_V-PD clean-btn">Copy</button></div></div><p>The plugin itself provides the init method. It is convenient for plugins to perform some initialization after |
| the plugin is loaded. If you need to clean up the initialization, you can put it in the corresponding destroy method.</p><p>Note : if the dependency of some plugin needs to be initialized when Nginx start, you may need to add logic to the initialization |
| method "http_init" in the file <strong>apisix/init.lua</strong>, and you may need to add some processing on generated part of Nginx |
| configuration file in <strong>apisix/cli/ngx_tpl.lua</strong> file. But it is easy to have an impact on the overall situation according to the |
| existing plugin mechanism, <strong>we do not recommend this unless you have a complete grasp of the code</strong>.</p><h2><a aria-hidden="true" tabindex="-1" class="anchor anchor__h2 anchorWithHideOnScrollNavbar_3ly5" id="name-priority-and-the-others"></a>name, priority and the others<a class="hash-link" href="#name-priority-and-the-others" title="Direct link to heading">#</a></h2><p>Determine the name and priority of the plugin, and add to conf/config.yaml. For example, for the example-plugin plugin, |
| you need to specify the plugin name in the code (the name is the unique identifier of the plugin and cannot be |
| duplicate), you can see the code in file "<strong>apisix/plugins/example-plugin.lua</strong>" :</p><div class="codeBlockContainer_EiTO"><div class="codeBlockContent_X2I6 lua"><pre tabindex="0" class="prism-code language-lua codeBlock_UxnK thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_W6UD"><span class="token-line" style="color:#393A34"><span class="token plain">local plugin_name = "example-plugin"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">local _M = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> version = 0.1,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> priority = 0,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> name = plugin_name,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> schema = schema,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> metadata_schema = metadata_schema,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_V-PD clean-btn">Copy</button></div></div><p>Note: The priority of the new plugin cannot be same to any existing ones, you can use the <code>/v1/schema</code> method of <a href="/docs/apisix/3.11/control-api/#get-v1schema">control API</a> to view the priority of all plugins. In addition, plugins with higher priority value will be executed first in a given phase (see the definition of <code>phase</code> in <a href="#choose-phase-to-run">choose-phase-to-run</a>). For example, the priority of example-plugin is 0 and the priority of ip-restriction is 3000. Therefore, the ip-restriction plugin will be executed first, then the example-plugin plugin. It's recommended to use priority 1 ~ 99 for your plugin unless you want it to run before some builtin plugins.</p><p>By default, most APISIX plugins are <a href="https://github.com/apache/apisix/blob/master/apisix/cli/config.lua" rel="noopener noreferrer">enabled</a>:</p><div class="codeBlockContainer_EiTO"><div style="color:#393A34;background-color:#f6f8fa" class="codeBlockTitle_PQMO">apisix/cli/config.lua</div><div class="codeBlockContent_X2I6 lua"><pre tabindex="0" class="prism-code language-lua codeBlock_UxnK thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_W6UD"><span class="token-line" style="color:#393A34"><span class="token plain">local _M = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> ...</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> plugins = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> "real-ip",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> "ai",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> "client-control",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> "proxy-control",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> "request-id",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> "zipkin",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> "ext-plugin-pre-req",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> "fault-injection",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> "mocking",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> "serverless-pre-function",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> ...</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> },</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> ...</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_V-PD clean-btn">Copy</button></div></div><p>Note: the order of the plugins is not related to the order of execution.</p><p>To enable your custom plugin, add the list of plugins into <code>conf/config.yaml</code> and append your plugin name. For instance:</p><div class="codeBlockContainer_EiTO"><div class="codeBlockContent_X2I6 yaml"><pre tabindex="0" class="prism-code language-yaml codeBlock_UxnK thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_W6UD"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">plugins</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># see `conf/config.yaml.example` for an example</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">...</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic"># add existing plugins</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> your</span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain">plugin </span><span class="token comment" style="color:#999988;font-style:italic"># add your custom plugin</span><br></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_V-PD clean-btn">Copy</button></div></div><p>If your plugin has a new code directory of its own, and you need to redistribute it with the APISIX source code, you will need to modify the <code>Makefile</code> to create directory, such as:</p><div class="codeBlockContainer_EiTO"><div class="codeBlockContent_X2I6 bash"><pre tabindex="0" class="prism-code language-bash codeBlock_UxnK thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_W6UD"><span class="token-line" style="color:#393A34"><span class="token variable" style="color:#36acaa">$(</span><span class="token variable" style="color:#36acaa">INSTALL</span><span class="token variable" style="color:#36acaa">)</span><span class="token plain"> -d </span><span class="token variable" style="color:#36acaa">$(</span><span class="token variable" style="color:#36acaa">INST_LUADIR</span><span class="token variable" style="color:#36acaa">)</span><span class="token plain">/apisix/plugins/skywalking</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token variable" style="color:#36acaa">$(</span><span class="token variable" style="color:#36acaa">INSTALL</span><span class="token variable" style="color:#36acaa">)</span><span class="token plain"> apisix/plugins/skywalking/*.lua </span><span class="token variable" style="color:#36acaa">$(</span><span class="token variable" style="color:#36acaa">INST_LUADIR</span><span class="token variable" style="color:#36acaa">)</span><span class="token plain">/apisix/plugins/skywalking/</span><br></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_V-PD clean-btn">Copy</button></div></div><p>There are other fields in the <code>_M</code> which affect the plugin's behavior.</p><div class="codeBlockContainer_EiTO"><div class="codeBlockContent_X2I6 lua"><pre tabindex="0" class="prism-code language-lua codeBlock_UxnK thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_W6UD"><span class="token-line" style="color:#393A34"><span class="token plain">local _M = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> ...</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> type = 'auth',</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> run_policy = 'prefer_route',</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_V-PD clean-btn">Copy</button></div></div><p><code>run_policy</code> field can be used to control the behavior of the plugin execution. |
| When this field set to <code>prefer_route</code>, and the plugin has been configured both |
| in the global and at the route level, only the route level one will take effect.</p><p><code>type</code> field is required to be set to <code>auth</code> if your plugin needs to work with consumer. See the section below.</p><h2><a aria-hidden="true" tabindex="-1" class="anchor anchor__h2 anchorWithHideOnScrollNavbar_3ly5" id="schema-and-check"></a>schema and check<a class="hash-link" href="#schema-and-check" title="Direct link to heading">#</a></h2><p>Write <a href="https://json-schema.org" target="_blank" rel="noopener noreferrer">JSON Schema</a> descriptions and check functions. Similarly, take the example-plugin plugin as an example to see its |
| configuration data:</p><div class="codeBlockContainer_EiTO"><div class="codeBlockContent_X2I6 json"><pre tabindex="0" class="prism-code language-json codeBlock_UxnK thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_W6UD"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token property" style="color:#36acaa">"example-plugin"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token property" style="color:#36acaa">"i"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token property" style="color:#36acaa">"s"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"s"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token property" style="color:#36acaa">"t"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_V-PD clean-btn">Copy</button></div></div><p>Let's look at its schema description :</p><div class="codeBlockContainer_EiTO"><div class="codeBlockContent_X2I6 lua"><pre tabindex="0" class="prism-code language-lua codeBlock_UxnK thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_W6UD"><span class="token-line" style="color:#393A34"><span class="token plain">local schema = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> type = "object",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> properties = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> i = {type = "number", minimum = 0},</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> s = {type = "string"},</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> t = {type = "array", minItems = 1},</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> ip = {type = "string"},</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> port = {type = "integer"},</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> },</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> required = {"i"},</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_V-PD clean-btn">Copy</button></div></div><p>The schema defines a non-negative number <code>i</code>, a string <code>s</code>, a non-empty array of <code>t</code>, and <code>ip</code> / <code>port</code>. Only <code>i</code> is required.</p><p>At the same time, we need to implement the <strong>check_schema(conf)</strong> method to complete the specification verification.</p><div class="codeBlockContainer_EiTO"><div class="codeBlockContent_X2I6 lua"><pre tabindex="0" class="prism-code language-lua codeBlock_UxnK thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_W6UD"><span class="token-line" style="color:#393A34"><span class="token plain">function _M.check_schema(conf, schema_type)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> return core.schema.check(schema, conf)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end</span><br></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_V-PD clean-btn">Copy</button></div></div><p>Note: the project has provided the public method "<strong>core.schema.check</strong>", which can be used directly to complete JSON |
| verification.</p><p>In addition, if the plugin needs to use some metadata, we can define the plugin <code>metadata_schema</code>, and then we can dynamically manage these metadata through the <code>Admin API</code>. Example:</p><div class="codeBlockContainer_EiTO"><div class="codeBlockContent_X2I6 lua"><pre tabindex="0" class="prism-code language-lua codeBlock_UxnK thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_W6UD"><span class="token-line" style="color:#393A34"><span class="token plain">local metadata_schema = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> type = "object",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> properties = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> ikey = {type = "number", minimum = 0},</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> skey = {type = "string"},</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> },</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> required = {"ikey", "skey"},</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">local plugin_name = "example-plugin"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">local _M = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> version = 0.1,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> priority = 0, -- TODO: add a type field, may be a good idea</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> name = plugin_name,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> schema = schema,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> metadata_schema = metadata_schema,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_V-PD clean-btn">Copy</button></div></div><p>You might have noticed the key-auth plugin has <code>type = 'auth'</code> in its definition. |
| When we set the type of plugin to <code>auth</code>, it means that this plugin is an authentication plugin.</p><p>An authentication plugin needs to choose a consumer after execution. For example, in key-auth plugin, it calls the <code>consumer.attach_consumer</code> to attach a consumer, which is chosen via the <code>apikey</code> header.</p><p>To interact with the <code>consumer</code> resource, this type of plugin needs to provide a <code>consumer_schema</code> to check the <code>plugins</code> configuration in the <code>consumer</code>.</p><p>Here is the consumer configuration for key-auth plugin:</p><div class="codeBlockContainer_EiTO"><div class="codeBlockContent_X2I6 json"><pre tabindex="0" class="prism-code language-json codeBlock_UxnK thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_W6UD"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token property" style="color:#36acaa">"username"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Joe"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token property" style="color:#36acaa">"plugins"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token property" style="color:#36acaa">"key-auth"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token property" style="color:#36acaa">"key"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Joe's key"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_V-PD clean-btn">Copy</button></div></div><p>It will be used when you try to create a <a href="/docs/apisix/3.11/admin-api/#consumer">Consumer</a></p><p>To validate the configuration, the plugin uses a schema like this:</p><div class="codeBlockContainer_EiTO"><div class="codeBlockContent_X2I6 lua"><pre tabindex="0" class="prism-code language-lua codeBlock_UxnK thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_W6UD"><span class="token-line" style="color:#393A34"><span class="token plain">local consumer_schema = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> type = "object",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> properties = {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> key = {type = "string"},</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> },</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> required = {"key"},</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">}</span><br></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_V-PD clean-btn">Copy</button></div></div><p>Note the difference between key-auth's <strong>check_schema(conf)</strong> method to example-plugin's:</p><div class="codeBlockContainer_EiTO"><div class="codeBlockContent_X2I6 lua"><pre tabindex="0" class="prism-code language-lua codeBlock_UxnK thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_W6UD"><span class="token-line" style="color:#393A34"><span class="token plain">-- key-auth</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">function _M.check_schema(conf, schema_type)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> if schema_type == core.schema.TYPE_CONSUMER then</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> return core.schema.check(consumer_schema, conf)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> else</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> return core.schema.check(schema, conf)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> end</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end</span><br></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_V-PD clean-btn">Copy</button></div></div><div class="codeBlockContainer_EiTO"><div class="codeBlockContent_X2I6 lua"><pre tabindex="0" class="prism-code language-lua codeBlock_UxnK thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_W6UD"><span class="token-line" style="color:#393A34"><span class="token plain">-- example-plugin</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">function _M.check_schema(conf, schema_type)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> return core.schema.check(schema, conf)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end</span><br></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_V-PD clean-btn">Copy</button></div></div><h3><a aria-hidden="true" tabindex="-1" class="anchor anchor__h3 anchorWithHideOnScrollNavbar_3ly5" id="encrypted-storage-fields"></a>encrypted storage fields<a class="hash-link" href="#encrypted-storage-fields" title="Direct link to heading">#</a></h3><p>Specify the parameters to be stored encrypted. (Requires APISIX version >= 3.1.0)</p><p>Some plugins require parameters to be stored encrypted, such as the <code>password</code> parameter of the <code>basic-auth</code> plugin. This plugin needs to specify in the <code>schema</code> which parameters need to be stored encrypted.</p><div class="codeBlockContainer_EiTO"><div class="codeBlockContent_X2I6 lua"><pre tabindex="0" class="prism-code language-lua codeBlock_UxnK thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_W6UD"><span class="token-line" style="color:#393A34"><span class="token plain">encrypt_fields = {"password"}</span><br></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_V-PD clean-btn">Copy</button></div></div><p>If it is a nested parameter, such as the <code>clickhouse.password</code> parameter of the <code>error-log-logger</code> plugin, it needs to be separated by <code>.</code>:</p><div class="codeBlockContainer_EiTO"><div class="codeBlockContent_X2I6 lua"><pre tabindex="0" class="prism-code language-lua codeBlock_UxnK thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_W6UD"><span class="token-line" style="color:#393A34"><span class="token plain">encrypt_fields = {"clickhouse.password"}</span><br></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_V-PD clean-btn">Copy</button></div></div><p>Currently not supported yet:</p><ol><li>more than two levels of nesting</li><li>fields in arrays</li></ol><p>Parameters can be stored encrypted by specifying <code>encrypt_fields = {"password"}</code> in the <code>schema</code>. APISIX will provide the following functionality.</p><ul><li>When adding and updating resources via the <code>Admin API</code>, APISIX automatically encrypts the parameters declared in <code>encrypt_fields</code> and stores them in etcd</li><li>When fetching resources via the <code>Admin API</code> and when running the plugin, APISIX automatically decrypts the parameters declared in <code>encrypt_fields</code></li></ul><p>How to enable this feature?</p><p>Enable <code>data_encryption</code> in <code>config.yaml</code>.</p><div class="codeBlockContainer_EiTO"><div class="codeBlockContent_X2I6 yaml"><pre tabindex="0" class="prism-code language-yaml codeBlock_UxnK thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_W6UD"><span class="token-line" style="color:#393A34"><span class="token key atrule" style="color:#00a4db">apisix</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">data_encryption</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">enable</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean important" style="color:#36acaa">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token key atrule" style="color:#00a4db">keyring</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> edd1c9f0985e76a2</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">-</span><span class="token plain"> qeddd145sfvddff4</span><br></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_V-PD clean-btn">Copy</button></div></div><p>APISIX will try to decrypt the data with keys in the order of the keys in the keyring (only for parameters declared in <code>encrypt_fields</code>). If the decryption fails, the next key will be tried until the decryption succeeds.</p><p>If none of the keys in <code>keyring</code> can decrypt the data, the original data is used.</p><h2><a aria-hidden="true" tabindex="-1" class="anchor anchor__h2 anchorWithHideOnScrollNavbar_3ly5" id="choose-phase-to-run"></a>choose phase to run<a class="hash-link" href="#choose-phase-to-run" title="Direct link to heading">#</a></h2><p>Determine which phase to run, generally access or rewrite. If you don't know the <a href="https://github.com/openresty/lua-nginx-module/blob/master/README.markdown#directives" target="_blank" rel="noopener noreferrer">OpenResty lifecycle</a>, it's |
| recommended to know it in advance. For example key-auth is an authentication plugin, thus the authentication should be completed |
| before forwarding the request to any upstream service. Therefore, the plugin must be executed in the rewrite phases. |
| Similarly, if you want to modify or process the response body or headers you can do that in the <code>body_filter</code> or in the <code>header_filter</code> phases respectively.</p><p>The following code snippet shows how to implement any logic relevant to the plugin in the OpenResty log phase.</p><div class="codeBlockContainer_EiTO"><div class="codeBlockContent_X2I6 lua"><pre tabindex="0" class="prism-code language-lua codeBlock_UxnK thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_W6UD"><span class="token-line" style="color:#393A34"><span class="token plain">function _M.log(conf, ctx)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">-- Implement logic here</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end</span><br></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_V-PD clean-btn">Copy</button></div></div><p><strong>Note : we can't invoke <code>ngx.exit</code>, <code>ngx.redirect</code> or <code>core.respond.exit</code> in rewrite phase and access phase. if need to exit, just return the status and body, the plugin engine will make the exit happen with the returned status and body. <a href="https://github.com/apache/apisix/blob/35269581e21473e1a27b11cceca6f773cad0192a/apisix/plugins/limit-count.lua#L177" rel="noopener noreferrer">example</a></strong></p><h3><a aria-hidden="true" tabindex="-1" class="anchor anchor__h3 anchorWithHideOnScrollNavbar_3ly5" id="extra-phase"></a>extra phase<a class="hash-link" href="#extra-phase" title="Direct link to heading">#</a></h3><p>Besides OpenResty's phases, we also provide extra phases to satisfy specific purpose:</p><ul><li><code>delayed_body_filter</code></li></ul><div class="codeBlockContainer_EiTO"><div class="codeBlockContent_X2I6 lua"><pre tabindex="0" class="prism-code language-lua codeBlock_UxnK thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_W6UD"><span class="token-line" style="color:#393A34"><span class="token plain">function _M.delayed_body_filter(conf, ctx)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> -- delayed_body_filter is called after body_filter</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> -- it is used by the tracing plugins to end the span right after body_filter</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end</span><br></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_V-PD clean-btn">Copy</button></div></div><h2><a aria-hidden="true" tabindex="-1" class="anchor anchor__h2 anchorWithHideOnScrollNavbar_3ly5" id="implement-the-logic"></a>implement the logic<a class="hash-link" href="#implement-the-logic" title="Direct link to heading">#</a></h2><p>Write the logic of the plugin in the corresponding phase. There are two parameters <code>conf</code> and <code>ctx</code> in the phase method, take the <code>limit-conn</code> plugin configuration as an example.</p><div class="admonition admonition-note alert alert--secondary"><div class="admonition-heading"><h5><span class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" width="14" height="16" viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</h5></div><div class="admonition-content"><p>You can fetch the <code>admin_key</code> from <code>config.yaml</code> and save to an environment variable with the following command:</p><div class="codeBlockContainer_EiTO"><div class="codeBlockContent_X2I6 bash"><pre tabindex="0" class="prism-code language-bash codeBlock_UxnK thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_W6UD"><span class="token-line" style="color:#393A34"><span class="token assign-left variable" style="color:#36acaa">admin_key</span><span class="token operator" style="color:#393A34">=</span><span class="token variable" style="color:#36acaa">$(</span><span class="token variable" style="color:#36acaa">yq </span><span class="token variable string" style="color:#e3116c">'.deployment.admin.admin_key[0].key'</span><span class="token variable" style="color:#36acaa"> conf/config.yaml </span><span class="token variable operator" style="color:#393A34">|</span><span class="token variable" style="color:#36acaa"> </span><span class="token variable function" style="color:#d73a49">sed</span><span class="token variable" style="color:#36acaa"> </span><span class="token variable string" style="color:#e3116c">'s/"//g'</span><span class="token variable" style="color:#36acaa">)</span><br></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_V-PD clean-btn">Copy</button></div></div></div></div><div class="codeBlockContainer_EiTO"><div class="codeBlockContent_X2I6 shell"><pre tabindex="0" class="prism-code language-shell codeBlock_UxnK thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_W6UD"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">curl</span><span class="token plain"> http://127.0.0.1:9180/apisix/admin/routes/1 -H </span><span class="token string" style="color:#e3116c">"X-API-KEY: </span><span class="token string variable" style="color:#36acaa">$admin_key</span><span class="token string" style="color:#e3116c">"</span><span class="token plain"> -X PUT -d </span><span class="token string" style="color:#e3116c">'</span><br></span><span class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c">{</span><br></span><span class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c"> "methods": ["GET"],</span><br></span><span class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c"> "uri": "/index.html",</span><br></span><span class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c"> "id": 1,</span><br></span><span class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c"> "plugins": {</span><br></span><span class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c"> "limit-conn": {</span><br></span><span class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c"> "conn": 1,</span><br></span><span class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c"> "burst": 0,</span><br></span><span class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c"> "default_conn_delay": 0.1,</span><br></span><span class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c"> "rejected_code": 503,</span><br></span><span class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c"> "key": "remote_addr"</span><br></span><span class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c"> }</span><br></span><span class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c"> },</span><br></span><span class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c"> "upstream": {</span><br></span><span class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c"> "type": "roundrobin",</span><br></span><span class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c"> "nodes": {</span><br></span><span class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c"> "127.0.0.1:1980": 1</span><br></span><span class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c"> }</span><br></span><span class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c"> }</span><br></span><span class="token-line" style="color:#393A34"><span class="token string" style="color:#e3116c">}'</span><br></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_V-PD clean-btn">Copy</button></div></div><h3><a aria-hidden="true" tabindex="-1" class="anchor anchor__h3 anchorWithHideOnScrollNavbar_3ly5" id="conf-parameter"></a>conf parameter<a class="hash-link" href="#conf-parameter" title="Direct link to heading">#</a></h3><p>The <code>conf</code> parameter is the relevant configuration information of the plugin, you can use <code>core.log.warn(core.json.encode(conf))</code> to output it to <code>error.log</code> for viewing, as shown below:</p><div class="codeBlockContainer_EiTO"><div class="codeBlockContent_X2I6 lua"><pre tabindex="0" class="prism-code language-lua codeBlock_UxnK thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_W6UD"><span class="token-line" style="color:#393A34"><span class="token plain">function _M.access(conf, ctx)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> core.log.warn(core.json.encode(conf))</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> ......</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end</span><br></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_V-PD clean-btn">Copy</button></div></div><p>conf:</p><div class="codeBlockContainer_EiTO"><div class="codeBlockContent_X2I6 json"><pre tabindex="0" class="prism-code language-json codeBlock_UxnK thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_W6UD"><span class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token property" style="color:#36acaa">"rejected_code"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">503</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token property" style="color:#36acaa">"burst"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token property" style="color:#36acaa">"default_conn_delay"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token property" style="color:#36acaa">"conn"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token property" style="color:#36acaa">"key"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"remote_addr"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_V-PD clean-btn">Copy</button></div></div><h3><a aria-hidden="true" tabindex="-1" class="anchor anchor__h3 anchorWithHideOnScrollNavbar_3ly5" id="ctx-parameter"></a>ctx parameter<a class="hash-link" href="#ctx-parameter" title="Direct link to heading">#</a></h3><p>The <code>ctx</code> parameter caches data information related to the request. You can use <code>core.log.warn(core.json.encode(ctx, true))</code> to output it to <code>error.log</code> for viewing, as shown below :</p><div class="codeBlockContainer_EiTO"><div class="codeBlockContent_X2I6 lua"><pre tabindex="0" class="prism-code language-lua codeBlock_UxnK thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_W6UD"><span class="token-line" style="color:#393A34"><span class="token plain">function _M.access(conf, ctx)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> core.log.warn(core.json.encode(ctx, true))</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> ......</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end</span><br></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_V-PD clean-btn">Copy</button></div></div><h2><a aria-hidden="true" tabindex="-1" class="anchor anchor__h2 anchorWithHideOnScrollNavbar_3ly5" id="register-public-api"></a>register public API<a class="hash-link" href="#register-public-api" title="Direct link to heading">#</a></h2><p>A plugin can register API which exposes to the public. Take batch-requests plugin as an example, this plugin registers <code>POST /apisix/batch-requests</code> to allow developers to group multiple API requests into a single HTTP request/response cycle:</p><div class="codeBlockContainer_EiTO"><div class="codeBlockContent_X2I6 lua"><pre tabindex="0" class="prism-code language-lua codeBlock_UxnK thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_W6UD"><span class="token-line" style="color:#393A34"><span class="token plain">function batch_requests()</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> -- ...</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">function _M.api()</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> -- ...</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> return {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> methods = {"POST"},</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> uri = "/apisix/batch-requests",</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> handler = batch_requests,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end</span><br></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_V-PD clean-btn">Copy</button></div></div><p>Note that the public API will not be exposed by default, you will need to use the <a href="/docs/apisix/3.11/plugins/public-api/">public-api plugin</a> to expose it.</p><h2><a aria-hidden="true" tabindex="-1" class="anchor anchor__h2 anchorWithHideOnScrollNavbar_3ly5" id="register-control-api"></a>register control API<a class="hash-link" href="#register-control-api" title="Direct link to heading">#</a></h2><p>If you only want to expose the API to the localhost or intranet, you can expose it via <a href="/docs/apisix/3.11/control-api/">Control API</a>.</p><p>Take a look at example-plugin plugin:</p><div class="codeBlockContainer_EiTO"><div class="codeBlockContent_X2I6 lua"><pre tabindex="0" class="prism-code language-lua codeBlock_UxnK thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_W6UD"><span class="token-line" style="color:#393A34"><span class="token plain">local function hello()</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> local args = ngx.req.get_uri_args()</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> if args["json"] then</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> return 200, {msg = "world"}</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> else</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> return 200, "world\n"</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> end</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">function _M.control_api()</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> return {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> methods = {"GET"},</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> uris = {"/v1/plugin/example-plugin/hello"},</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> handler = hello,</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end</span><br></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_V-PD clean-btn">Copy</button></div></div><p>If you don't change the default control API configuration, the plugin will be expose <code>GET /v1/plugin/example-plugin/hello</code> which can only be accessed via <code>127.0.0.1</code>. Test with the following command:</p><div class="codeBlockContainer_EiTO"><div class="codeBlockContent_X2I6 shell"><pre tabindex="0" class="prism-code language-shell codeBlock_UxnK thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_W6UD"><span class="token-line" style="color:#393A34"><span class="token function" style="color:#d73a49">curl</span><span class="token plain"> -i -X GET </span><span class="token string" style="color:#e3116c">"http://127.0.0.1:9090/v1/plugin/example-plugin/hello"</span><br></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_V-PD clean-btn">Copy</button></div></div><p><a href="/docs/apisix/3.11/control-api/">Read more about control API introduction</a></p><h2><a aria-hidden="true" tabindex="-1" class="anchor anchor__h2 anchorWithHideOnScrollNavbar_3ly5" id="register-custom-variable"></a>register custom variable<a class="hash-link" href="#register-custom-variable" title="Direct link to heading">#</a></h2><p>We can use variables in many places of APISIX. For example, customizing log format in http-logger, using it as the key of <code>limit-*</code> plugins. In some situations, the builtin variables are not enough. Therefore, APISIX allows developers to register their variables globally, and use them as normal builtin variables.</p><p>For instance, let's register a variable called <code>a6_labels_zone</code> to fetch the value of the <code>zone</code> label in a route:</p><div class="codeBlockContainer_EiTO"><div class="codeBlockContent_X2I6 bash"><pre tabindex="0" class="prism-code language-bash codeBlock_UxnK thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_W6UD"><span class="token-line" style="color:#393A34"><span class="token builtin class-name">local</span><span class="token plain"> core </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> require </span><span class="token string" style="color:#e3116c">"apisix.core"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">core.ctx.register_var</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"a6_labels_zone"</span><span class="token plain">, function</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ctx</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token builtin class-name">local</span><span class="token plain"> route </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> ctx.matched_route and ctx.matched_route.value</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> route and route.labels </span><span class="token keyword" style="color:#00009f">then</span><span class="token plain"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token builtin class-name">return</span><span class="token plain"> route.labels.zone</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> end</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> </span><span class="token builtin class-name">return</span><span class="token plain"> nil</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">end</span><span class="token punctuation" style="color:#393A34">)</span><br></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_V-PD clean-btn">Copy</button></div></div><p>After that, any get operation to <code>$a6_labels_zone</code> will call the registered getter to fetch the value.</p><p>Note that the custom variables can't be used in features that depend on the Nginx directive, like <code>access_log_format</code>.</p><h2><a aria-hidden="true" tabindex="-1" class="anchor anchor__h2 anchorWithHideOnScrollNavbar_3ly5" id="write-test-case"></a>write test case<a class="hash-link" href="#write-test-case" title="Direct link to heading">#</a></h2><p>For functions, write and improve the test cases of various dimensions, do a comprehensive test for your plugin! The |
| test cases of plugins are all in the "<strong>t/plugin</strong>" directory. You can go ahead to find out. APISIX uses |
| <a href="https://github.com/openresty/test-nginx" target="_blank" rel="noopener noreferrer">*<strong>*test-nginx**</strong></a> as the test framework. A test case (.t file) is usually |
| divided into prologue and data parts by __data__. Here we will briefly introduce the data part, that is, the part |
| of the real test case. For example, the key-auth plugin:</p><div class="codeBlockContainer_EiTO"><div class="codeBlockContent_X2I6 perl"><pre tabindex="0" class="prism-code language-perl codeBlock_UxnK thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_W6UD"><span class="token-line" style="color:#393A34"><span class="token plain">=== TEST 1: sanity</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">--- config</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> location /t {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> content_by_lua_block {</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> local plugin = require("apisix.plugins.key-auth")</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> local ok, err = plugin.check_schema({key = 'test-key'}, core.schema.TYPE_CONSUMER)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> if not ok then</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> ngx.say(err)</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> end</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> ngx.say("done")</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain"> }</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">--- request</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">GET /t</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">--- response_body</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">done</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">--- no_error_log</span><br></span><span class="token-line" style="color:#393A34"><span class="token plain">[error]</span><br></span></code></pre><button type="button" aria-label="Copy code to clipboard" class="copyButton_V-PD clean-btn">Copy</button></div></div><p>A test case consists of three parts :</p><ul><li><strong>Program code</strong> : configuration content of Nginx location</li><li><strong>Input</strong> : http request information</li><li><strong>Output check</strong> : status, header, body, error log check</li></ul><p>When we request <strong>/t</strong>, which config in the configuration file, the Nginx will call "<strong>content_by_lua_block</strong>" instruction to |
| complete the Lua script, and finally return. The assertion of the use case is response_body return "done", |
| "<strong>no_error_log</strong>" means to check the "<strong>error.log</strong>" of Nginx. There must be no ERROR level record. The log files for the unit test |
| are located in the following folder: 't/servroot/logs'.</p><p>The above test case represents a simple scenario. Most scenarios will require multiple steps to validate. To do this, create multiple tests <code>=== TEST 1</code>, <code>=== TEST 2</code>, and so on. These tests will be executed sequentially, allowing you to break down scenarios into a sequence of atomic steps.</p><p>Additionally, there are some convenience testing endpoints which can be found <a href="https://github.com/apache/apisix/blob/master/t/lib/server.lua#L36" rel="noopener noreferrer">here</a>. For example, see <a href="https://github.com/apache/apisix/blob/master/t/plugin/proxy-rewrite.t" rel="noopener noreferrer">proxy-rewrite</a>. In test 42, the upstream <code>uri</code> is made to redirect <code>/test?new_uri=hello</code> to <code>/hello</code> (which always returns <code>hello world</code>). In test 43, the response body is confirmed to equal <code>hello world</code>, meaning the proxy-rewrite configuration added with test 42 worked correctly.</p><p>Refer the following <a href="/docs/apisix/3.11/building-apisix/">document</a> to setup the testing framework.</p><h3><a aria-hidden="true" tabindex="-1" class="anchor anchor__h3 anchorWithHideOnScrollNavbar_3ly5" id="attach-the-test-nginx-execution-process"></a>attach the test-nginx execution process:<a class="hash-link" href="#attach-the-test-nginx-execution-process" title="Direct link to heading">#</a></h3><p>According to the path we configured in the makefile and some configuration items at the front of each <strong>.t</strong> file, the |
| framework will assemble into a complete nginx.conf file. "<strong>t/servroot</strong>" is the working directory of Nginx and start the |
| Nginx instance. according to the information provided by the test case, initiate the http request and check that the |
| return items of HTTP include HTTP status, HTTP response header, HTTP response body and so on.</p></div><footer class="theme-doc-footer docusaurus-mt-lg"><div class="theme-doc-footer-edit-meta-row row"><div class="col"><a href="/edit#https://github.com/apache/apisix/edit/release/3.11/docs/en/latest/plugin-develop.md" target="_blank" rel="noreferrer noopener" class="theme-edit-this-page"><svg fill="currentColor" height="20" width="20" viewBox="0 0 40 40" class="iconEdit_mS5F" aria-hidden="true"><g><path d="m34.5 11.7l-3 3.1-6.3-6.3 3.1-3q0.5-0.5 1.2-0.5t1.1 0.5l3.9 3.9q0.5 0.4 0.5 1.1t-0.5 1.2z m-29.5 17.1l18.4-18.5 6.3 6.3-18.4 18.4h-6.3v-6.2z"></path></g></svg>Edit this page</a></div><div class="col lastUpdated_mt2f"></div></div></footer></article><nav class="pagination-nav docusaurus-mt-lg" aria-label="Docs pages navigation"><div class="pagination-nav__item"><a class="pagination-nav__link" href="/docs/apisix/3.11/internal/testing-framework/"><div class="pagination-nav__sublabel">Previous</div><div class="pagination-nav__label">« Introducing APISIX's testing framework</div></a></div><div class="pagination-nav__item pagination-nav__item--next"><a class="pagination-nav__link" href="/docs/apisix/3.11/debug-mode/"><div class="pagination-nav__sublabel">Next</div><div class="pagination-nav__label">Debug mode »</div></a></div></nav></div></div><div class="col col--3"><div class="tableOfContents_vrFS thin-scrollbar"><ul class="table-of-contents table-of-contents__left-border"><li><a href="#where-to-put-your-plugins" class="table-of-contents__link">where to put your plugins</a></li><li><a href="#check-dependencies" class="table-of-contents__link">check dependencies</a></li><li><a href="#name-priority-and-the-others" class="table-of-contents__link">name, priority and the others</a></li><li><a href="#schema-and-check" class="table-of-contents__link">schema and check</a><ul><li><a href="#encrypted-storage-fields" class="table-of-contents__link">encrypted storage fields</a></li></ul></li><li><a href="#choose-phase-to-run" class="table-of-contents__link">choose phase to run</a><ul><li><a href="#extra-phase" class="table-of-contents__link">extra phase</a></li></ul></li><li><a href="#implement-the-logic" class="table-of-contents__link">implement the logic</a><ul><li><a href="#conf-parameter" class="table-of-contents__link">conf parameter</a></li><li><a href="#ctx-parameter" class="table-of-contents__link">ctx parameter</a></li></ul></li><li><a href="#register-public-api" class="table-of-contents__link">register public API</a></li><li><a href="#register-control-api" class="table-of-contents__link">register control API</a></li><li><a href="#register-custom-variable" class="table-of-contents__link">register custom variable</a></li><li><a href="#write-test-case" class="table-of-contents__link">write test case</a><ul><li><a href="#attach-the-test-nginx-execution-process" class="table-of-contents__link">attach the test-nginx execution process:</a></li></ul></li></ul></div></div></div></div></main></div></div><footer class="container_MP5Z"><div class="linksRow_iwpv"><div class="linksCol_a1ec"><div>ASF</div><ul><li class="footer__item"><a href="https://www.apache.org/" target="_blank" rel="noopener noreferrer"><span></span><span>Foundation</span></a></li><li class="footer__item"><a href="https://www.apache.org/licenses/" target="_blank" rel="noopener noreferrer"><span></span><span>License</span></a></li><li class="footer__item"><a href="https://www.apache.org/events/" target="_blank" rel="noopener noreferrer"><span></span><span>Events</span></a></li><li class="footer__item"><a href="https://www.apache.org/security/" target="_blank" rel="noopener noreferrer"><span></span><span>Security</span></a></li><li class="footer__item"><a href="https://www.apache.org/foundation/sponsorship.html" target="_blank" rel="noopener noreferrer"><span></span><span>Sponsorship</span></a></li><li class="footer__item"><a href="https://www.apache.org/foundation/thanks.html" target="_blank" rel="noopener noreferrer"><span></span><span>Thanks</span></a></li></ul></div><div class="linksCol_a1ec"><div>Community</div><ul><li class="footer__item"><a href="https://github.com/apache/apisix/issues" target="_blank" rel="noopener noreferrer"><span></span><span>GitHub</span></a></li><li class="footer__item"><a href="/docs/general/join/"><span></span><span>Slack</span></a></li><li class="footer__item"><a href="https://twitter.com/ApacheAPISIX" target="_blank" rel="noopener noreferrer"><span></span><span>Twitter</span></a></li><li class="footer__item"><a href="https://www.youtube.com/channel/UCgPD18cMhOg5rmPVnQhAC8g" target="_blank" rel="noopener noreferrer"><span></span><span>YouTube</span></a></li></ul></div><div class="linksCol_a1ec"><div>More</div><ul><li class="footer__item"><a target="_parent" href="/blog/"><span></span><span>Blog</span></a></li><li class="footer__item"><a target="_parent" href="/showcase/"><span></span><span>Showcase</span></a></li><li class="footer__item"><a target="_parent" href="/plugins/"><span></span><span>Plugin Hub</span></a></li><li class="footer__item"><a href="https://github.com/apache/apisix/milestones" target="_parent" rel="noopener noreferrer"><span></span><span>Roadmap</span></a></li></ul></div></div><div class="copyright_ZfFh"><a href="https://www.apache.org/" target="_blank" rel="noopener noreferrer"><span style="display:inline-block;width:231.25px;height:40px"></span></a><div>Copyright © 2019-2025 The Apache Software Foundation. Apache APISIX, APISIX®, Apache, the Apache feather logo, and the Apache APISIX project logo are either registered trademarks or trademarks of the Apache Software Foundation.</div></div></footer></div> |
| <script src="https://apisix-website-static.apiseven.com/assets/js/runtime~main.0f7288cd.js"></script> |
| <script src="https://apisix-website-static.apiseven.com/assets/js/main.cd029f6b.js"></script> |
| </body> |
| </html> |