ОНОЛ 12

Вэб Програмчлал

ЛЕКЦ 12: ВЭБ ПРОГРАМЧЛАЛ (Java Web Programming)

Хичээлийн зорилго: HTTP протокол, Servlet, JSP, MVC загвар, Spring Boot Web (Thymeleaf + REST), CRUD үйлдлүүд, Session/Cookie удирдлага, Form validation, аюулгүй байдал, вэб аппликейшний шилдэг туршлагуудыг эзэмшүүлэх.

Хамрах хүрээ: HTTP Request/Response, Servlet Lifecycle, JSP (Expression Language, JSTL), MVC Pattern, Spring Boot Web, Thymeleaf, Form Handling, CRUD Operations, Session & Cookie, CSRF Protection, Filter & Interceptor, Error Handling, Static Resources, Deployment.

Эх сурвалж: Lectures Web I-III, Lecture 07 (Servlets & JSP), Lecture 08 (Web CRUD), Lecture 09 (State Management & Security), Lecture 11 (Web App Best Practices), Tim Downey — Guide to Web Development with Java.



ХЭСЭГ 1: ОНОЛЫН СУУРЬ (Theory & Foundations)

1.1 HTTP Протокол — Вэбийн суурь

💡 Зүйрлэл: HTTP = Шуудангийн систем. Client (та) = Захидал бичигч. Server = Шуудангийн алба. Request = Захидал. Response = Хариу захидал. URL = Хаяг.

HTTP Request бүтэц:

┌─────────────────────────────────────────┐
│  GET /api/students/1 HTTP/1.1           │ ← Request Line (Method + URL + Version)
│  Host: localhost:8080                   │ ← Headers
│  Accept: text/html                      │
│  Cookie: JSESSIONID=abc123              │
│                                         │
│  (Body — GET-д ихэвчлэн байхгүй)       │ ← Request Body
└─────────────────────────────────────────┘

HTTP Response бүтэц:

┌─────────────────────────────────────────┐
│  HTTP/1.1 200 OK                        │ ← Status Line
│  Content-Type: text/html; charset=UTF-8 │ ← Headers
│  Set-Cookie: JSESSIONID=abc123          │
│                                         │
│  <html><body>Hello</body></html>        │ ← Response Body
└─────────────────────────────────────────┘

HTTP Method-ууд:

MethodЗорилгоIdempotentBody
GETӨгөгдөл АВАХ (унших)✅ Тийм❌ Байхгүй
POSTӨгөгдөл ҮҮСГЭХ (бичих)❌ Үгүй✅ Байна
PUTӨгөгдөл бүрэн ШИНЭЧЛЭХ✅ Тийм✅ Байна
DELETEӨгөгдөл УСТГАХ✅ Тийм❌ Байхгүй
PATCHӨгөгдөл хэсэгчлэн шинэчлэх❌ Үгүй✅ Байна

HTTP Status Code:

КодУтгаЖишээ
200OK — АмжилттайGET хариу
201Created — ҮүсгэсэнPOST хариу
301Moved Permanently — ШилжсэнRedirect
302Found — Түр шилжүүлэхLogin дараа redirect
400Bad Request — Буруу хүсэлтValidation алдаа
401Unauthorized — НэвтрэлтгүйLogin хэрэгтэй
403Forbidden — ЭрхгүйЗөвшөөрөлгүй
404Not Found — ОлдсонгүйURL буруу
500Internal Server Error — Серверийн алдааException

1.2 Servlet — Java Web-ийн суурь

Servlet гэж юу вэ?

Servlet = Java CLASS бөгөөд HTTP хүсэлт хүлээн авч, боловсруулж, хариу буцаана. Java web-ийн хамгийн доод түвшний технологи.

Client (Browser)
    │
    │  HTTP Request
    ▼
┌──────────────────┐
│   Web Server     │
│  (Tomcat)        │
│                  │
│  ┌────────────┐  │
│  │  Servlet   │  │  ← Java class
│  │  Container │  │
│  └────────────┘  │
│                  │
└──────────────────┘
    │
    │  HTTP Response
    ▼
Client (Browser)

Servlet Lifecycle (Амьдралын мөчлөг):

1. Loading:    Web server → Servlet class АЧААЛАХ
2. init():     Servlet ЭХЛҮҮЛЭХ (нэг удаа)
3. service():  Хүсэлт бүрт → doGet() / doPost() ДУУДАХ (олон удаа)
4. destroy():  Servlet УСТГАХ (нэг удаа)
@WebServlet("/students")
public class StudentServlet extends HttpServlet {

    @Override
    public void init() throws ServletException {
        // Нэг удаа — DB холболт, тохиргоо
        System.out.println("Servlet initialized!");
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // GET хүсэлт — Оюутнуудын жагсаалт
        List<Student> students = studentDAO.findAll();
        request.setAttribute("students", students);
        request.getRequestDispatcher("/WEB-INF/views/students.jsp")
               .forward(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // POST хүсэлт — Шинэ оюутан нэмэх
        String name = request.getParameter("name");
        String email = request.getParameter("email");
        studentDAO.save(new Student(name, email));
        response.sendRedirect("/students");  // PRG Pattern
    }

    @Override
    public void destroy() {
        // Нэг удаа — Нөөц чөлөөлөх
        System.out.println("Servlet destroyed!");
    }
}

Servlet тохиргоо:

АргаТайлбар
@WebServlet("/path")Annotation-аар тохируулах (шинэ)
web.xmlXML файлаар тохируулах (хуучин)
<!-- web.xml тохиргоо (хуучин арга) -->
<servlet>
    <servlet-name>studentServlet</servlet-name>
    <servlet-class>com.example.StudentServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>studentServlet</servlet-name>
    <url-pattern>/students</url-pattern>
</servlet-mapping>

1.3 JSP (JavaServer Pages)

JSP гэж юу вэ?

JSP = HTML дотор JAVA КОД бичих технологи. Servlet-ийн VIEW хэсэг. Server дээр HTML болж хөрвөнө.

┌─────────────────────────┐
│  students.jsp            │
│                          │
│  <html>                  │
│  <h1>${title}</h1>       │  ← Expression Language (EL)
│  <c:forEach ...>         │  ← JSTL Tag
│    ${student.name}       │
│  </c:forEach>            │
│  </html>                 │
└─────────────────────────┘
         │
         │  Server дээр хөрвөнө
         ▼
┌─────────────────────────┐
│  HTML (browser-д)        │
│                          │
│  <html>                  │
│  <h1>Оюутнууд</h1>      │
│  <p>Бат</p>              │
│  <p>Сараа</p>            │
│  </html>                 │
└─────────────────────────┘

JSP Syntax:

SyntaxЗорилгоЖишээ
<%= ... %>Expression (утга хэвлэх)<%= student.getName() %>
<% ... %>Scriptlet (Java код)<% for(...) { } %>
<%! ... %>Declaration (field, method)<%! int count = 0; %>
${...}EL (Expression Language)${student.name}ЗӨВЛӨМЖТЭЙ
<c:forEach>JSTL (Tag library)Давталт, нөхцөл

JSP + JSTL жишээ:

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head><title>Оюутнууд</title></head>
<body>
    <h1>Оюутнуудын жагсаалт</h1>

    <table border="1">
        <tr>
            <th>ID</th>
            <th>Нэр</th>
            <th>Email</th>
            <th>Үйлдэл</th>
        </tr>
        <c:forEach var="student" items="${students}">
            <tr>
                <td>${student.id}</td>
                <td>${student.name}</td>
                <td>${student.email}</td>
                <td>
                    <a href="/students/edit?id=${student.id}">Засах</a>
                    <a href="/students/delete?id=${student.id}">Устгах</a>
                </td>
            </tr>
        </c:forEach>
    </table>

    <h2>Шинэ оюутан нэмэх</h2>
    <form action="/students" method="POST">
        <input type="text" name="name" placeholder="Нэр" required>
        <input type="email" name="email" placeholder="Email" required>
        <button type="submit">Нэмэх</button>
    </form>
</body>
</html>

1.4 MVC Pattern — Вэб аппын бүтэц

MVC гэж юу вэ?

┌──────────────────────────────────────────────────────┐
│                    MVC Pattern                        │
│                                                       │
│  ┌─────────┐    ┌──────────────┐    ┌──────────────┐ │
│  │  VIEW   │◄───│  CONTROLLER  │───►│    MODEL     │ │
│  │ (JSP/   │    │  (Servlet/   │    │  (Entity/    │ │
│  │Thymeleaf)│   │  Controller) │    │   Service)   │ │
│  └─────────┘    └──────┬───────┘    └──────────────┘ │
│                        │                              │
│  HTML харуулах   Хүсэлт удирдах    Бизнес логик + DB │
└──────────────────────────────────────────────────────┘

Browser → Controller → Service → Repository → DB
                ↓
            View (HTML)
                ↓
            Browser
ХэсэгҮүрэгЖишээ
ModelӨгөгдөл + бизнес логикStudent entity, StudentService
ViewХаруулалт (UI)JSP, Thymeleaf template
ControllerХүсэлт хүлээн авч, удирдахServlet, @Controller

1.5 Spring Boot Web — Орчин үеийн Java Web

Яагаад Spring Boot?

Servlet/JSPSpring Boot Web
web.xml тохиргооАвтомат тохиргоо
Tomcat тусад deployEmbedded Tomcat (JAR дотор)
Manual dependencySpring Starter
Servlet extend@Controller annotation
JSP (хуучин)Thymeleaf (орчин үе)

Spring Boot Web dependency:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

@Controller vs @RestController:

AnnotationБуцаах зүйлХэрэглээ
@ControllerVIEW нэр (HTML template)Вэб хуудас (Thymeleaf)
@RestControllerJSON/XML (Response body)REST API
// @Controller — HTML хуудас буцаана
@Controller
@RequestMapping("/students")
public class StudentController {

    @GetMapping
    public String list(Model model) {
        model.addAttribute("students", studentService.findAll());
        return "students/list";   // → templates/students/list.html
    }
}

// @RestController — JSON буцаана
@RestController
@RequestMapping("/api/students")
public class StudentApiController {

    @GetMapping
    public List<StudentResponse> list() {
        return studentService.findAll();  // → JSON
    }
}

1.6 Thymeleaf — Орчин үеийн Template Engine

Thymeleaf гэж юу вэ?

Thymeleaf = Server-side HTML template engine. JSP-ийн ОРЧИН ҮЕИЙН ОРЛУУЛАГЧ. HTML файлыг browser-д шууд нээж болно (Natural Template).

Thymeleaf vs JSP:

ШинжJSPThymeleaf
Файл.jsp.html
Syntax<%= %>, <c:forEach>th:text, th:each
BrowserШууд нээхгүй✅ Шууд нээж болно
Spring BootТусдаа тохиргооАвтомат
Байршил/WEB-INF/views//templates/

Thymeleaf syntax:

<!-- templates/students/list.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Оюутнууд</title>
    <link rel="stylesheet" th:href="@{/css/style.css}">
</head>
<body>
    <h1 th:text="${title}">Оюутнуудын жагсаалт</h1>

    <!-- Хүснэгт -->
    <table>
        <thead>
            <tr>
                <th>ID</th>
                <th>Нэр</th>
                <th>Email</th>
                <th>GPA</th>
                <th>Үйлдэл</th>
            </tr>
        </thead>
        <tbody>
            <tr th:each="student : ${students}">
                <td th:text="${student.id}">1</td>
                <td th:text="${student.name}">Бат</td>
                <td th:text="${student.email}">bat@test.com</td>
                <td th:text="${student.gpa}">3.5</td>
                <td>
                    <a th:href="@{/students/edit/{id}(id=${student.id})}">Засах</a>
                    <a th:href="@{/students/delete/{id}(id=${student.id})}"
                       onclick="return confirm('Устгах уу?')">Устгах</a>
                </td>
            </tr>
        </tbody>
    </table>

    <!-- Хоосон байвал -->
    <p th:if="${#lists.isEmpty(students)}">Оюутан бүртгэгдээгүй байна.</p>

    <a th:href="@{/students/new}">+ Шинэ оюутан нэмэх</a>
</body>
</html>

Thymeleaf гол attribute-ууд:

AttributeЗорилгоЖишээ
th:textТекст харуулахth:text="${student.name}"
th:eachДавталтth:each="s : ${students}"
th:if / th:unlessНөхцөлth:if="${students.size() > 0}"
th:hrefLink үүсгэхth:href="@{/students/{id}(id=${s.id})}"
th:actionForm actionth:action="@{/students}"
th:objectForm object bindth:object="${student}"
th:fieldForm field bindth:field="*{name}"
th:valueУтга оноохth:value="${student.name}"
th:srcImage srcth:src="@{/images/logo.png}"
th:classCSS classth:class="${error ? 'error' : ''}"
th:fragmentДахин ашиглах хэсэгLayout, header, footer

1.7 CRUD — Web Form ашиглан

Full CRUD Flow:

CREATE:  GET /students/new      → Form харуулах
         POST /students         → Хадгалах → Redirect

READ:    GET /students          → Жагсаалт
         GET /students/{id}     → Дэлгэрэнгүй

UPDATE:  GET /students/edit/{id} → Form + одоогийн утга
         POST /students/edit/{id} → Шинэчлэх → Redirect

DELETE:  GET /students/delete/{id} → Устгах → Redirect

Controller — Full CRUD:

@Controller
@RequestMapping("/students")
public class StudentController {

    private final StudentService studentService;

    // 1. READ — Жагсаалт
    @GetMapping
    public String list(Model model) {
        model.addAttribute("students", studentService.findAll());
        return "students/list";
    }

    // 2. CREATE — Form харуулах
    @GetMapping("/new")
    public String showCreateForm(Model model) {
        model.addAttribute("student", new StudentForm());
        return "students/form";
    }

    // 3. CREATE — Хадгалах
    @PostMapping
    public String create(@Valid @ModelAttribute("student") StudentForm form,
                         BindingResult result,
                         RedirectAttributes redirectAttributes) {
        if (result.hasErrors()) {
            return "students/form";  // Алдаатай бол form руу буцах
        }
        studentService.create(form);
        redirectAttributes.addFlashAttribute("message", "Амжилттай нэмлээ!");
        return "redirect:/students";  // PRG Pattern
    }

    // 4. UPDATE — Form + одоогийн утга
    @GetMapping("/edit/{id}")
    public String showEditForm(@PathVariable Long id, Model model) {
        StudentForm form = studentService.getFormById(id);
        model.addAttribute("student", form);
        model.addAttribute("editMode", true);
        return "students/form";
    }

    // 5. UPDATE — Шинэчлэх
    @PostMapping("/edit/{id}")
    public String update(@PathVariable Long id,
                         @Valid @ModelAttribute("student") StudentForm form,
                         BindingResult result,
                         RedirectAttributes redirectAttributes) {
        if (result.hasErrors()) {
            return "students/form";
        }
        studentService.update(id, form);
        redirectAttributes.addFlashAttribute("message", "Амжилттай шинэчиллээ!");
        return "redirect:/students";
    }

    // 6. DELETE — Устгах
    @GetMapping("/delete/{id}")
    public String delete(@PathVariable Long id, RedirectAttributes redirectAttributes) {
        studentService.delete(id);
        redirectAttributes.addFlashAttribute("message", "Амжилттай устгалаа!");
        return "redirect:/students";
    }
}

Form Template (Create + Edit нэгтгэсэн):

<!-- templates/students/form.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head><title>Оюутан</title></head>
<body>
    <h1 th:text="${editMode} ? 'Оюутан засах' : 'Шинэ оюутан'">Form</h1>

    <form th:action="${editMode} ? @{/students/edit/{id}(id=${student.id})} : @{/students}"
          th:object="${student}" method="POST">

        <div>
            <label>Нэр:</label>
            <input type="text" th:field="*{name}">
            <span th:if="${#fields.hasErrors('name')}" th:errors="*{name}" class="error"></span>
        </div>

        <div>
            <label>Email:</label>
            <input type="email" th:field="*{email}">
            <span th:if="${#fields.hasErrors('email')}" th:errors="*{email}" class="error"></span>
        </div>

        <div>
            <label>GPA:</label>
            <input type="number" step="0.1" th:field="*{gpa}">
            <span th:if="${#fields.hasErrors('gpa')}" th:errors="*{gpa}" class="error"></span>
        </div>

        <button type="submit" th:text="${editMode} ? 'Шинэчлэх' : 'Нэмэх'">Submit</button>
        <a th:href="@{/students}">Буцах</a>
    </form>
</body>
</html>

PRG Pattern (Post-Redirect-Get):

POST /students (form submit)
    │
    │  Хадгалах
    │
    ▼
302 Redirect → GET /students
    │
    │  Жагсаалт харуулах
    │
    ▼
200 OK (HTML)

Яагаад PRG?
- Хэрэглэгч F5 (refresh) дарвал → POST давтагдахгүй!
- POST → Redirect → GET = Давхар бичлэг ҮҮСЭХГҮЙ

1.8 Form Validation

StudentForm + Validation:

public class StudentForm {

    private Long id;

    @NotBlank(message = "Нэр хоосон байж болохгүй")
    @Size(min = 2, max = 50, message = "Нэр 2-50 тэмдэгт байна")
    private String name;

    @NotBlank(message = "Email хоосон байж болохгүй")
    @Email(message = "Email формат буруу")
    private String email;

    @NotNull(message = "GPA хоосон байж болохгүй")
    @DecimalMin(value = "0.0", message = "GPA 0.0-аас багагүй")
    @DecimalMax(value = "4.0", message = "GPA 4.0-аас ихгүй")
    private Double gpa;

    // getters, setters
}

Validation алдаа Thymeleaf-д:

<!-- Бүх алдааг нэг дор харуулах -->
<div th:if="${#fields.hasAnyErrors()}" class="alert alert-danger">
    <ul>
        <li th:each="err : ${#fields.allErrors()}" th:text="${err}"></li>
    </ul>
</div>

<!-- Field бүрийн алдаа -->
<input type="text" th:field="*{name}" th:classappend="${#fields.hasErrors('name')} ? 'is-invalid'">
<div th:if="${#fields.hasErrors('name')}" th:errors="*{name}" class="invalid-feedback"></div>

HTTP = Stateless → Session/Cookie-аар state удирдах

┌────────┐                          ┌────────┐
│ Client │ ── Request + Cookie ───► │ Server │
│        │ ◄── Response + Set-Cookie│        │
│        │                          │        │
│ Cookie:│                          │Session:│
│ JSESSIONID=abc123                 │ abc123:│
│                                   │  user: "Бат"│
│                                   │  role: "ADMIN"│
└────────┘                          └────────┘
ШинжSessionCookie
ХадгалахSERVER дээрCLIENT (browser) дээр
ХэмжээХязгааргүй4KB
АюулгүйИлүү аюулгүйXSS-д эмзэг
ХугацааTimeout (30 мин)Тохируулах боломжтой
ХэрэглээLogin, cartRemember me, preferences

HttpSession ашиглах:

@Controller
public class AuthController {

    // Login
    @PostMapping("/login")
    public String login(@RequestParam String email,
                        @RequestParam String password,
                        HttpSession session,
                        RedirectAttributes redirectAttributes) {
        User user = userService.authenticate(email, password);
        if (user != null) {
            session.setAttribute("currentUser", user);
            session.setAttribute("role", user.getRole());
            return "redirect:/dashboard";
        }
        redirectAttributes.addFlashAttribute("error", "Email эсвэл password буруу!");
        return "redirect:/login";
    }

    // Logout
    @GetMapping("/logout")
    public String logout(HttpSession session) {
        session.invalidate();  // Session бүрэн устгах
        return "redirect:/login";
    }

    // Dashboard — Session шалгах
    @GetMapping("/dashboard")
    public String dashboard(HttpSession session, Model model) {
        User user = (User) session.getAttribute("currentUser");
        if (user == null) {
            return "redirect:/login";  // Нэвтрээгүй бол
        }
        model.addAttribute("user", user);
        return "dashboard";
    }
}
// Cookie тохируулах
@GetMapping("/set-theme")
public String setTheme(@RequestParam String theme, HttpServletResponse response) {
    Cookie cookie = new Cookie("theme", theme);
    cookie.setMaxAge(30 * 24 * 60 * 60);  // 30 хоног
    cookie.setHttpOnly(true);              // XSS хамгаалалт
    cookie.setSecure(true);                // HTTPS only
    cookie.setPath("/");
    response.addCookie(cookie);
    return "redirect:/";
}

// Cookie унших
@GetMapping("/")
public String home(@CookieValue(value = "theme", defaultValue = "light") String theme,
                   Model model) {
    model.addAttribute("theme", theme);
    return "home";
}

1.10 Filter & Interceptor — Хүсэлт шүүх

Filter (Servlet түвшин):

@Component
@Order(1)
public class LoggingFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        long start = System.currentTimeMillis();

        chain.doFilter(request, response);  // Дараагийн filter/servlet руу

        long duration = System.currentTimeMillis() - start;
        System.out.println(req.getMethod() + " " + req.getRequestURI() + " → " + duration + "ms");
    }
}

Interceptor (Spring MVC түвшин):

@Component
public class AuthInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {
        HttpSession session = request.getSession(false);
        if (session == null || session.getAttribute("currentUser") == null) {
            response.sendRedirect("/login");
            return false;  // Controller руу ДАМЖУУЛАХГҮЙ
        }
        return true;  // Controller руу дамжуулах
    }
}

// Interceptor бүртгэх
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new AuthInterceptor())
                .addPathPatterns("/dashboard/**", "/students/**")
                .excludePathPatterns("/login", "/register", "/css/**", "/js/**");
    }
}

Filter vs Interceptor:

ШинжFilterInterceptor
ТүвшинServlet (доод)Spring MVC (дээд)
ХамрахБүх хүсэлт (static ч)Controller хүсэлт
Тохиргоо@ComponentWebMvcConfigurer
ХэрэглээLogging, CORS, encodingAuth, role шалгалт

1.11 Thymeleaf Layout — Дахин ашиглах

Fragment ашиглах:

<!-- templates/fragments/header.html -->
<header th:fragment="header">
    <nav>
        <a th:href="@{/}">Нүүр</a>
        <a th:href="@{/students}">Оюутнууд</a>
        <span th:if="${session.currentUser != null}">
            <span th:text="${session.currentUser.name}">User</span>
            <a th:href="@{/logout}">Гарах</a>
        </span>
        <span th:unless="${session.currentUser != null}">
            <a th:href="@{/login}">Нэвтрэх</a>
        </span>
    </nav>
</header>
<!-- templates/fragments/footer.html -->
<footer th:fragment="footer">
    <p>&copy; 2024 Student Management System</p>
</footer>
<!-- templates/students/list.html — Fragment ашиглах -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head><title>Оюутнууд</title></head>
<body>
    <div th:replace="~{fragments/header :: header}"></div>

    <main>
        <!-- Амжилтын мэдэгдэл -->
        <div th:if="${message}" class="alert alert-success" th:text="${message}"></div>

        <h1>Оюутнуудын жагсаалт</h1>
        <!-- Хүснэгт -->
    </main>

    <div th:replace="~{fragments/footer :: footer}"></div>
</body>
</html>

1.12 Static Resources & Error Handling

Static файлууд:

src/main/resources/
├── static/
│   ├── css/
│   │   └── style.css
│   ├── js/
│   │   └── app.js
│   └── images/
│       └── logo.png
├── templates/
│   ├── students/
│   │   ├── list.html
│   │   └── form.html
│   ├── fragments/
│   │   ├── header.html
│   │   └── footer.html
│   ├── error/
│   │   ├── 404.html
│   │   └── 500.html
│   └── index.html
└── application.yml

Error page (Custom):

<!-- templates/error/404.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head><title>404 - Олдсонгүй</title></head>
<body>
    <h1>404</h1>
    <p>Хайсан хуудас олдсонгүй.</p>
    <a th:href="@{/}">Нүүр хуудас руу буцах</a>
</body>
</html>

Global Exception Handler:

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(StudentNotFoundException.class)
    public String handleNotFound(StudentNotFoundException ex, Model model) {
        model.addAttribute("error", ex.getMessage());
        return "error/404";
    }

    @ExceptionHandler(Exception.class)
    public String handleGeneral(Exception ex, Model model) {
        model.addAttribute("error", "Серверийн алдаа гарлаа.");
        return "error/500";
    }
}

1.13 Вэб аппын шилдэг туршлагууд (Best Practices)

#ЗарчимТайлбар
1PRG PatternPOST → Redirect → GET (давхар submit хамгаалалт)
2Input ValidationServer-side @Valid + Client-side HTML5
3CSRF ProtectionSpring Security CSRF token (form-д)
4XSS PreventionThymeleaf автомат escape (th:text)
5Session Timeoutserver.servlet.session.timeout=30m
6HttpOnly Cookiecookie.setHttpOnly(true)
7Error PageCustom 404, 500 хуудас
8Fragment/LayoutHeader, footer дахин ашиглах
9Flash MessageRedirectAttributes амжилт/алдаа мэдэгдэл
10SeparationController → Service → Repository давхаргалах
11Static ResourcesCSS, JS, Image → /static/
12PaginationИх өгөгдлийг хуудаслах (Pageable)
13ResponsiveMobile-friendly дизайн (Bootstrap)
14LoggingХүсэлт, алдаа бүрийг лог бичих
15HTTPSProduction-д заавал HTTPS


ХЭСЭГ 2: ТҮЛХҮҮР ҮГ БА МЭРГЭЖЛИЙН НЭР ТОМЬЁО (Keywords & Glossary)

#Англи нэр томьёоМонгол утгаДэлгэрэнгүй тайлбар
1HTTPГипертекст дамжуулах протоколClient-Server хоорондын хүсэлт/хариу дамжуулах протокол.
2ServletСеровлетHTTP хүсэлт хүлээн авч, боловсруулж, хариу буцаах Java class.
3JSPJava серверийн хуудасHTML дотор Java код бичих template технологи.
4ThymeleafТаймлифОрчин үеийн server-side HTML template engine — Natural template.
5MVCЗагвар-Харагдац-УдирдлагаModel-View-Controller — Вэб аппын бүтэц хуваах загвар.
6ControllerУдирдагчHTTP хүсэлт хүлээн авч, Service дуудаж, View буцаах.
7ModelЗагварӨгөгдөл + бизнес логик (Entity, Service).
8ViewХарагдацХэрэглэгчид харуулах UI (HTML template).
9CRUDҮүсгэх-Унших-Шинэчлэх-УстгахCreate, Read, Update, Delete — Өгөгдлийн 4 үндсэн үйлдэл.
10SessionСессияServer дээр хэрэглэгчийн state хадгалах механизм.
11CookieКүүкиClient (browser) дээр жижиг өгөгдөл хадгалах механизм.
12JSESSIONIDСессийн IDTomcat-ийн session cookie — Хэрэглэгчийг таних.
13PRG PatternPost-Redirect-GetPOST → 302 Redirect → GET — Давхар submit хамгаалалт.
14CSRFХуурамч хүсэлтCross-Site Request Forgery — Хэрэглэгчийн нэрээр хуурамч хүсэлт.
15XSSХөндлөн скриптCross-Site Scripting — Хортой JavaScript оруулах халдлага.
16FilterШүүлтүүрServlet түвшинд хүсэлт/хариуг шүүх (logging, encoding).
17InterceptorТасалдуулагчSpring MVC түвшинд хүсэлтийг шалгах (auth, role).
18@ControllerКонтроллерSpring MVC — HTML view буцаах controller annotation.
19@RestControllerREST контроллерJSON/XML буцаах controller — @Controller + @ResponseBody.
20@RequestMappingХүсэлт зураглалтURL path-г controller method-д холбох.
21@GetMappingGET зураглалтHTTP GET хүсэлт хүлээн авах method.
22@PostMappingPOST зураглалтHTTP POST хүсэлт хүлээн авах method.
23@PathVariableЗамын хувьсагчURL-аас утга авах — /students/{id}.
24@RequestParamХүсэлтийн параметрQuery parameter авах — ?name=Бат.
25@ModelAttributeЗагварын атрибутForm өгөгдлийг Java object-д автомат bind хийх.
26@ValidБаталгаажуулалтForm object-ийн validation annotation-уудыг шалгах.
27BindingResultХолболтын үр дүнValidation алдааг хадгалах object.
28RedirectAttributesЧиглүүлэлтийн атрибутRedirect хийхэд flash message дамжуулах.
29th:eachДавталтThymeleaf — Collection давталт (for-each).
30th:textТекстThymeleaf — Элементийн текстийг тохируулах + XSS escape.
31th:fieldТалбарThymeleaf — Form field-г object property-д bind хийх.
32th:fragmentХэсэгThymeleaf — Дахин ашиглах HTML хэсэг (header, footer).
33Expression LanguageИлэрхийллийн хэл${...} — JSP/Thymeleaf-д утга харуулах.
34JSTLJSP стандарт тагийн санJSP-д <c:forEach>, <c:if> зэрэг tag-ууд.
35TomcatТомкатApache-ийн Servlet container / Web server.
36Web ContainerВэб контейнерServlet ажиллуулах орчин (Tomcat, Jetty).
37Embedded ServerСуулгасан серверSpring Boot-д Tomcat JAR дотор ажиллана.
38Static ResourceСтатик нөөцCSS, JS, Image — Server боловсруулахгүй, шууд буцаах.
39@ControllerAdviceКонтроллер зөвлөмжБүх controller-д хамаарах global exception handler.
40PaginationХуудаслалтИх өгөгдлийг хуудас хуудсаар харуулах (Page, Pageable).