[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"question-jira-kak-sozdat-servlet-module-v-plagine":3},{"id":4,"slug":5,"topicId":6,"topicSlug":7,"topicName":8,"topicEmoji":9,"question":10,"answer":11,"codeLang":12,"codeSrc":12,"important":12,"commonMistakes":12,"modernUsage":12,"difficulty":13,"tags":14,"related":18,"progress":19,"seo":20},925,"kak-sozdat-servlet-module-v-plagine",27,"jira","Jira","📋","Как создать Servlet Module в плагине?","Servlet Module позволяет создавать веб-страницы внутри Jira с собственным UI, используется для административных панелей, конфигурационных страниц плагина, отчётов.\n\n### Объявление и реализация\n\n```xml\n\u003Cservlet key=\"config-servlet\" class=\"com.example.plugin.servlet.ConfigServlet\">\n    \u003Curl-pattern>\u002Fmy-plugin\u002Fconfig\u003C\u002Furl-pattern>\n\u003C\u002Fservlet>\n```\n\n\u003Cdetails>\n\u003Csummary>Код ConfigServlet\u003C\u002Fsummary>\n\n```java\n@Named\npublic class ConfigServlet extends HttpServlet {\n\n    private final TemplateRenderer templateRenderer;\n    private final JiraAuthenticationContext authContext;\n    private final PermissionManager permissionManager;\n    private final TaskConfigService configService;\n\n    @Inject\n    public ConfigServlet(@ComponentImport TemplateRenderer templateRenderer,\n                         @ComponentImport JiraAuthenticationContext authContext,\n                         @ComponentImport PermissionManager permissionManager,\n                         TaskConfigService configService) {\n        this.templateRenderer = templateRenderer;\n        this.authContext = authContext;\n        this.permissionManager = permissionManager;\n        this.configService = configService;\n    }\n\n    @Override\n    protected void doGet(HttpServletRequest req, HttpServletResponse resp)\n            throws IOException {\n        ApplicationUser user = authContext.getLoggedInUser();\n        if (user == null) {\n            resp.sendRedirect(\"\u002Flogin.jsp\");\n            return;\n        }\n\n        if (!permissionManager.hasPermission(GlobalPermissionKey.ADMINISTER, user)) {\n            resp.sendError(HttpServletResponse.SC_FORBIDDEN, \"Admin access required\");\n            return;\n        }\n\n        Map\u003CString, Object> context = new HashMap\u003C>();\n        context.put(\"configs\", configService.findAll());\n        context.put(\"currentUser\", user.getDisplayName());\n\n        resp.setContentType(\"text\u002Fhtml;charset=UTF-8\");\n        templateRenderer.render(\"templates\u002Fconfig-page.vm\", context, resp.getWriter());\n    }\n}\n```\n\n\u003C\u002Fdetails>\n\n### Velocity-шаблон\n\n\u003Cdetails>\n\u003Csummary>templates\u002Fconfig-page.vm\u003C\u002Fsummary>\n\n```html\n\u003Chtml>\n\u003Chead>\n    \u003Ctitle>Конфигурация плагина\u003C\u002Ftitle>\n    \u003Cmeta name=\"decorator\" content=\"atl.admin\"\u002F>\n    $webResourceManager.requireResource(\"com.example.my-plugin:my-resources\")\n\u003C\u002Fhead>\n\u003Cbody>\n    \u003Ch2>Настройки плагина\u003C\u002Fh2>\n\n    \u003Cform method=\"post\" class=\"aui\">\n        \u003Cdiv class=\"field-group\">\n            \u003Clabel for=\"projectKey\">Проект:\u003C\u002Flabel>\n            \u003Cinput type=\"text\" id=\"projectKey\" name=\"projectKey\" class=\"text\"\u002F>\n        \u003C\u002Fdiv>\n        \u003Cdiv class=\"buttons-container\">\n            \u003Cinput type=\"submit\" value=\"Сохранить\" class=\"aui-button aui-button-primary\"\u002F>\n        \u003C\u002Fdiv>\n    \u003C\u002Fform>\n\n    \u003Ch3>Существующие конфигурации\u003C\u002Fh3>\n    \u003Ctable class=\"aui\">\n        \u003Cthead>\n            \u003Ctr>\u003Cth>ID\u003C\u002Fth>\u003Cth>Проект\u003C\u002Fth>\u003Cth>Статус\u003C\u002Fth>\u003C\u002Ftr>\n        \u003C\u002Fthead>\n        \u003Ctbody>\n            #foreach($config in $configs)\n            \u003Ctr>\n                \u003Ctd>$config.getID()\u003C\u002Ftd>\n                \u003Ctd>$config.getProjectKey()\u003C\u002Ftd>\n                \u003Ctd>#if($config.isEnabled()) Активна #else Отключена #end\u003C\u002Ftd>\n            \u003C\u002Ftr>\n            #end\n        \u003C\u002Ftbody>\n    \u003C\u002Ftable>\n\u003C\u002Fbody>\n\u003C\u002Fhtml>\n```\n\n\u003C\u002Fdetails>\n\nURL доступа: `https:\u002F\u002Fjira.company.com\u002Fplugins\u002Fservlet\u002Fmy-plugin\u002Fconfig`\n\n### Частые ошибки\n\n- Забыть декоратор `atl.admin` — страница отображается без навигации Jira\n- XSS-уязвимости — не экранировать пользовательский ввод. Используйте `$!{htmlUtil.htmlEncode($value)}`\n- Не проверять права доступа — сервлет доступен любому аутентифицированному пользователю\n- Использование ComponentAccessor вместо DI — антипаттерн, затрудняет тестирование\n\n### Как используется в 2026\n\n- Servlet Module остаётся актуальным для административных страниц плагинов\n- Тренд: минимум серверного рендеринга, максимум SPA (React\u002FVue) + REST API плагина\n- AUI (Atlassian UI) — по-прежнему рекомендованная UI-библиотека для DC\n\n> **На собеседовании:** покажите знание Velocity-шаблонов и декораторов (`atl.admin`). Обязательно упомяните `$webResourceManager.requireResource(...)` для подключения CSS\u002FJS. Главная ошибка — не проверять права и не экранировать вывод.","","middle",[15,16,17,7],"servlet","velocity","jira-plugin",[],null,{"title":21,"description":22,"ogTitle":23,"ogDescription":24,"keywords":25,"schemaAnswer":34,"featuredSnippetReady":35},"Как создать Servlet Module в плагине Jira DC — Gymterview","Servlet Module для Jira DC: HttpServlet, Velocity-шаблоны, декоратор atl.admin, AUI, webResourceManager. Создание административных страниц плагина.","Servlet Module: веб-страницы в Jira DC плагине — Gymterview","Создание веб-страниц плагина: HttpServlet, Velocity-шаблоны, декоратор atl.admin, AUI-компоненты, защита от XSS.",[26,27,28,29,30,31,32,33],"Servlet Module","Jira плагин","Velocity","atl.admin","AUI","HttpServlet","административная страница","Jira DC","Servlet Module объявляется в atlassian-plugin.xml с url-pattern. Реализация наследует HttpServlet с @Named и DI через @Inject. Velocity — стандартный шаблонизатор, декоратор atl.admin оборачивает страницу в layout Jira. $webResourceManager.requireResource подключает CSS\u002FJS. Обязательна проверка прав и экранирование вывода (htmlEncode).",true]