Guli mall notes + stepping on the pit (10) - mall homepage and Nginx reverse proxy, thymeleaf+ dynamic display of three-level classification

Table of contents

1. Introduction to the mall system

2. Template technology

2.1. Introduction 

2.2. The difference between template technology and front-end framework 

3. Integrate thymeleaf to render the home page of the mall

3.1. Environment preparation 

3.1.1, [commodity module] import thymeleaf and hot deployment dependencies

3.1.2. Add static resources to the product module

3.1.3, the controller package is renamed app, and the web package is added 

3.1.4, yml closes thymeleaf cache 

3.1.5, start the test

3.2. Develop home page jump function

3.3. Write an interface for querying the first-level directory list 

3.4. Classification of the first-level rendering of the homepage of the mall

3.5. The expected response JSON of the second and third-level classification of commodities

3.6. Query the second-level and third-level categories according to the id of the first-level category

3.6.1. New Catalog2Vo entity class

3.6.2, new interface

3.6.3. Newly query the List collection whose parent ID is parent_cid

3.6.4. Modification request

3.6.5. Restart the service and test

3.7. Summary of springboot page jump

3.7.1. Static pages

3.7.2. Dynamic pages

4. nginx builds domain name access environment

4.1. Demand

4.2. Review of reverse proxy

4.3, configure reverse proxy

4.3.1, hosts file configuration domain name and ip mapping

4.3.2. Configure nginx.conf

4.3.3. Configure /etc/nginx/conf.d/gulimall.conf 

4.3.4, gateway configuration "gulimall.com" routing rules

4.3.5. Pit point: When Nginx proxies to the gateway, it will lose the requested host and other information

4.3.6, the test passed

4.3.7 Summary, reverse proxy process


1. Introduction to the mall system

Our mall system should also be separated from the front and back ends , just like the background management system. However, for teaching considerations, if the front and back ends are separated, many details will be blocked, so we develop server-side page rendering (somewhat similar to freemarker )

Dynamic and static separation: When nginx is deployed later, we can deploy the static resources of the pages in the microservice to nginx. Share the pressure of microservices.

Static refers to static resources such as pictures, js, css (in the form of actual files)

Each microservice only manages its own pages , and finally each microservice can be deployed, run, and upgraded independently .

Each microservice is independent and autonomous , and the database and technology of each microservice are autonomous. Commodities and services are not necessarily developed with java, but with php and js. They are independent and autonomous in terms of technology, architecture and business.

The user first accesses Nginx, and the Nginx reverse proxy forwards the request to the gateway, which routes the request to various services. The gateway can request routing, authentication, current limiting, etc.
insert image description here

2. Template technology

2.1. Introduction 

Template technology: replace the static data in the page with the data from the background database.

This operation can be achieved with jsp. However, the Spring boot architecture does not recommend the use of Jsp, and the support is not good, so if you use spring boot, generally use Freemarker or Thymeleaf.

The Thymeleaf template engine is a natural language, and the front end of the written page can be used directly. 

2.2. The difference between template technology and front-end framework 

insert image description here

3. Integrate thymeleaf to render the home page of the mall

3.1. Environment preparation 

3.1.1, [commodity module] import thymeleaf and hot deployment dependencies


<!--        页面渲染我们使用thymeleaf,这应该和freemarker是差不多的,都是模板引擎-->
<!--        优点:它是一个自然化语言,编写的语言前端可以直接使用,方便前后人员的分工合作-->
<!--        缺点:性能比其他模板引擎要低一点,但是我们在生产环境开启了它的缓存功能,性能也是很高的-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
<!--热部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>

3.1.2. Add static resources to the product module

Copy static resources to static, index.html to templates.

3.1.3, the controller package is renamed app, and the web package is added 

Rename the controller package to app, and add a web package. In the future, all rest interfaces will be provided from the app package, and all controllers will be provided from the web package.

The controller under the web package is returned to thymeleaf. Its default prefix and suffix are classpath:/templates/and respectively .html. When springboot accesses the project, it will automatically find index.html

3.1.4, yml closes thymeleaf cache 

# 关闭thymeleaf缓存,以便于修改代码后刷新页面能看出修改
spring:
  thymeleaf:
    cache: false

3.1.5, start the test

Start the commodity module, and you can find that all static resources are accessible. springboot accesses index.html by default.

3.2. Develop home page jump function

Added gulimall/product/web/IndexController.java to the product module

package xx.xx.gulimall.product.web;

@Controller
public class IndexController {
    @Autowired
    CategoryService categoryService;

    @GetMapping({"/","index.html"})
    public String indexPage(Model model){    //传参Model类
        // TODO 1、查出所有1级分类
        List<CategoryEntity> categoryEntitys = categoryService.getLevel1Categorys();
        model.addAttribute("categories",categoryEntities);
        return "index";    //相当于return "index.html"; 拦截GetMapping路径后转到首页
    }
}

3.3. Write an interface for querying the first-level directory list 

gulimall-product/src/main/java/site/zhourui/gulimall/product/service/CategoryService.java

new interface

    List<CategoryEntity> getLevel1Categorys();

gulimall-product/src/main/java/site/zhourui/gulimall/product/service/impl/CategoryServiceImpl.java

Implement the new interface

  /**
     * 查询一级分类。
     * 父ID是0, 或者  层级是1
     */
    @Override
    public List<CategoryEntity> getLevel1Categorys() {
        System.out.println("调用了 getLevel1Categorys  查询了数据库........【一级分类】");
        return baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", 0));
    }

3.4. Classification of the first-level rendering of the homepage of the mall

Home using thymeleaf

Add xmlns:th="http://www.thymeleaf.org" to the html tag

<html lang="en" xmlns:th="http://www.thymeleaf.org">

Traverse the categories passed by IndexController

       <div class="header_main_left">
          <ul>
            <li th:each=" category : ${categorys}">
              <a href="http://search.gulimall.com/" class="header_main_left_a" th:attr="ctg-data=${category.catId}"><b th:text="${category.name}">家用电器</b></a>
            </li>
          </ul>

        </div>

Restart commodity service

The first-level directory becomes the directory queried by the database

image-20211027154132182

3.5. The expected response JSON of the second and third-level classification of commodities

The data of the second-level and third-level classification of commodities is in gulimall-product/src/main/resources/static/index/json/catalog.json

image-20211027155817198

We need to encapsulate data with the same structure as catalog.json

Format catalog.json

It is found that it is a map type structure. The key is the value of the first-level category id, and the value is an array of all the second-level directories under the first-level category.

image-20211027160346325

Open the secondary directory array

Find an array of objects, each object represents a secondary classification, and catalog3List is the collection of the three-level classification of the second-level classification

image-20211027161916363

Open catalog3List

is a three-level classification

image-20211027162041402

3.6. Query the second-level and third-level categories according to the id of the first-level category

It is found that catalog.json is called at gulimall-product/src/main/resources/static/index/js/catalogLoader.js

image-20211027162225339

3.6.1. New Catalog2Vo entity class

Created according to the structure analyzed in Chapter 8

package xx.xx.gulimall.product.vo;
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Catalog2Vo {

    private String catalog1Id;  // 1级父分类ID
    private List<Catalog3Vo> catalog3List;// 3级子分类集合
    private String id;  // 2级分类ID
    private String name;  // 2级分类name

    /**
     * 三级分类Vo
     */
    @NoArgsConstructor
    @AllArgsConstructor
    @Data
    public static class Catalog3Vo {
        private String catalog2Id;  // 2级父分类ID
        private String id;  // 3级分类ID
        private String name;  // 3级分类name
    }
}

3.6.2, new interface

/**
 * 查出三级分类
 * 1级分类作为key,2级引用List
 */
@ResponseBody
@GetMapping("/index/catalog.json")
public Map<String, List<Catalog2Vo>> getCatalogJson() {
    Map<String, List<Catalog2Vo>> map = categoryService.getCatalogJson();
    return map;
}

gulimall-product/src/main/java/com/atguigu/gulimall/product/service/CategoryService.java

    Map<String, List<Catalog2Vo>> getCatalogJson();

gulimall-product/src/main/java/com/atguigu/gulimall/product/service/impl/CategoryServiceImpl.java

@Override
    public Map<String, List<Catalog2Vo>> getCatalogJson() {
        // 一次性获取所有 数据
        List<CategoryEntity> selectList = baseMapper.selectList(null);
        System.out.println("调用了 getCatalogJson  查询了数据库........【三级分类】");
        // 1)、所有1级分类
        List<CategoryEntity> level1Categorys = getParent_cid(selectList, 0L);

        // 2)、封装数据
        Map<String, List<Catalog2Vo>> collect = level1Categorys.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), level1 -> {
            // 查到当前1级分类的2级分类
            List<CategoryEntity> category2level = getParent_cid(selectList, level1.getCatId());
            List<Catalog2Vo> catalog2Vos = null;
            if (category2level != null) {
                catalog2Vos = category2level.stream().map(level12 -> {
                    // 查询当前2级分类的3级分类
                    List<CategoryEntity> category3level = getParent_cid(selectList, level12.getCatId());
                    List<Catalog2Vo.Catalog3Vo> catalog3Vos = null;
                    if (category3level != null) {
                        catalog3Vos = category3level.stream().map(level13 -> {
                            return new Catalog2Vo.Catalog3Vo(level12.getCatId().toString(), level13.getCatId().toString(), level13.getName());
                        }).collect(Collectors.toList());
                    }
                    return new Catalog2Vo(level1.getCatId().toString(), catalog3Vos, level12.getCatId().toString(), level12.getName());
                }).collect(Collectors.toList());
            }
            return catalog2Vos;
        }));
        return collect;
    }

3.6.3. Newly query the List collection whose parent ID is parent_cid

gulimall-product/src/main/java/com/atguigu/gulimall/product/service/impl/CategoryServiceImpl.java

/**
     * 查询出父ID为 parent_cid的List集合
     */
    private List<CategoryEntity> getParent_cid(List<CategoryEntity> selectList, Long parent_cid) {
        return selectList.stream().filter(item -> item.getParentCid() == parent_cid).collect(Collectors.toList());
        //return baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", level.getCatId()));
    }

3.6.4. Modification request

gulimall-product/src/main/resources/static/index/js/catalogLoader.js

Modify the request path to the new interface path index/catalog.json in Chapter 9

3.6.5. Restart the service and test

Database modification three-level taxonomy name

image-20211027171226401

After the page is refreshed, it also changes accordingly, indicating that the data from the background is used

image-20211027171153020

3.7. Summary of springboot page jump

Static pages do not require any acquisition of background data, simply acquire this page, and vice versa for dynamic pages.

In the html under the springboot project template, it is not possible to use a click event to jump to the page from the background controller.

There are two folders under src/main/resources, static and templates. Springboot puts static pages and static resource files in static by default, and dynamic pages in templates. Dynamic page access requires Thymeleaf dependencies, and dynamic pages need to be accessed from the background . Controller jumps , and the static page is directly similar to: http://127.0.0.1:8080/index.html to access; if the static page cannot be accessed, first of all, you must ensure that your access to these files will not be accessed interceptor kill

In the static directory and templates directory of the SpringBoot project, static resource files are placed in static by default, such as: img, js, css, font, etc. If static HTML pages are placed under static, one can be directly accessed as static resources; in addition, if there is a In this case, if the page is placed under static and also needs to be redirected from the controller, then redirection can be used, because the default template of spring boot is stored under /resource/templates and will not go to the static directory to find . Redirect is actually redirecting to external resources; in fact, dynamic pages are placed under templates, and everyone knows that they need to be accessed from the Controller. These are some configurations that are customary in SpringBoot;

3.7.1. Static pages

Static pages can be accessed directly. Create a new hello.html in the static directory to directly access http://localhost:8080/hello.html in the browser, or jump through the controller layer.

@Controller
public class HelloController {
    @RequestMapping("/hello")
    public String hello() { return "hello.html"; }
}

The return value here is the page. If the method is annotated with @ResponseBody, the return value is a string. 

3.7.2. Dynamic pages

The access of dynamic pages needs to request the server first, access the application in the background, and then turn to the access page.
Spring boot uses thymeleaf as a dynamic page by default. It is recommended not to use jsp. The following is how spring boot integrates thymeleaf

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
  • Create a new hello.html file in the tempates directory. At this time, if you directly access http://localhost:8080/hello.html to access hello.html in the static folder (static directory), then access static through the controller layer jump 500 error will be reported, this is because

    • The return of a static page defaults to jumping to the static directory
    • When thymeleaf is introduced, the dynamic jump will override the default static jump
    • Dynamic jump jumps to the templates directory by default
    • The difference between the return codes of the two: dynamic jumps are available with or without the .html suffix

The controller layer after using thymeleaf

@Controller
public class HelloController {
    @RequestMapping("/hello")
    public String hello() { return "hello"; }
}

If you want to jump to /static/index.html when using a dynamic page, use redirection return "redirect:/index.html"

4. nginx builds domain name access environment

4.1. Demand

Visit "gulimall.com", Nginx reverse proxy to the gateway address " http://localhost:88/# ", display the product page.

Gateway port is 88.

4.2. Review of reverse proxy

Forward proxy: The client sends a request to the proxy server and specifies the target server, and the proxy forwards the request to the target server and returns the obtained content to the client.

Reverse proxy: Users can directly access the reverse proxy server to obtain the resources of the target server. The reverse proxy server unifies the access entrance.

4.3, configure reverse proxy

4.3.1, hosts file configuration domain name and ip mapping

Method 1: Direct modification 

Windows domain name resolution will first find the hosts file under C:\Windows\System32\drivers\etc, and then find the dns server

When we enter gulimall.com in the browser, we will find our virtual machine. At this time, if nginx in the virtual machine is running, we can access nginx, because nginx listens to port 80

192.168.56.10	gulimall.com

Method 2: Open the software SwitchHosts as an administrator and add a new scheme:

Test success: 

4.3.2. Configure nginx.conf

First, docker installs Nginx and configures self-starting. 

cd /mydata/nginx
cd conf
vi nginx.conf

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;    #支持的mime类型
    default_type  application/octet-stream;    #默认数据类型

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '    #日志格式
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;    #日志地址

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;    #开启压缩
    
    upstream gulimall{        #配置上游服务器,起名为gulimall
      server 192.168.56.1:88;    #配置上游服务器为网关地址
    }

    include /etc/nginx/conf.d/*.conf;    #该路径下的配置文件会全部合并到这里


}

4.3.3. Configure /etc/nginx/conf.d/gulimall.conf 

This configuration file is included by Nginx's http global block configuration.

server {
    listen       80;    #监听此端口
    server_name  gulimall.com;    #监听此域名

    #charset koi8-r;
    #access_log  /var/log/nginx/log/host.access.log  main;

    location / {    #配置请求的路由
       proxy_set_header Host $host;    #坑点:Nginx代理给网关时会丢失请求的host等信息
       proxy_pass http://gulimall;    #因为主配置文件配置了上游服务器为网关地址,所以可以请求路由到http://192.168.xxx.xxx:10000/
    }

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    # proxy the PHP scripts to Apache listening on 127.0.0.1:80
    #
    #location ~ \.php$ {
    #    proxy_pass   http://127.0.0.1;
    #}

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    #location ~ \.php$ {
    #    root           html;
    #    fastcgi_pass   127.0.0.1:9000;
    #    fastcgi_index  index.php;
    #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
    #    include        fastcgi_params;
    #}

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    #location ~ /\.ht {
    #    deny  all;
    #}
}

After the configuration is complete, restart the Nginx container.

insert image description here

4.3.4, gateway configuration "gulimall.com" routing rules

Note: domain name mapping can only be placed at the end, otherwise it will overwrite the above "/api/xxx" path 

spring:
  cloud:
    gateway:
      routes:
#        - id: test_route
#          uri: https://www.baidu.com
#          predicates:
#            #              根据参数来匹配
#            - Query=url,baidu

        # 和admin_route顺序不能乱,否则页面访问报404,因为被它拦截了
        # 我们一般把精确的路由放在上面,优先级高
        # 匹配了这个路由之后,不会匹配下面的路由
        - id: product_route
          uri: lb://gulimall-product
          predicates:
            - Path=/api/product/**
          # 前端的请求是 http://localhost:88/api/product/category/list/tree
          # 后端实际需要的请求是,http://localhost:12000/product/category/list/tree
          filters:
            - RewritePath=/api/(?<segment>.*),/$\{segment}

            #  http://localhost:88/api/thirdparty/oss/policy
        - id: third_party_route
          #  lb 负载均衡
          uri: lb://gulimall-third-party
           # 匹配所有以api开头的请求
          predicates:
            - Path=/api/thirdparty/**
          filters:
            #  路径重写
            # (?<segment>.*)  $\{segment} 相当于片段
            - RewritePath=/api/thirdparty/(?<segment>.*),/$\{segment}

        - id: member_route
          #  lb 负载均衡
          uri: lb://gulimall-member
          # 匹配所有以api开头的请求
          predicates:
            - Path=/api/member/**
          filters:
            #  路径重写
            # (?<segment>.*)  $\{segment} 相当于片段
            - RewritePath=/api/(?<segment>.*),/$\{segment}

        - id: ware_route
          uri: lb://gulimall-ware
          predicates:
            - Path=/api/ware/**
          filters:
            - RewritePath=/api/(?<segment>.*),/$\{segment}

        #  前端项目发送请求都以 /api 开头
        - id: admin_route
          #  lb 负载均衡  到renren-fast服务
          uri: lb://renren-fast
          # 匹配所有以api开头的请求
          predicates:
            - Path=/api/**
          filters:
            #  路径重写
            # http://localhost:88/api/captcha.jpg 在网关匹配到相应的规则后
            #  就变成了 http://localhost:8080/api/captcha.jpg
            # 但实际上我们需要真正访问的是 http://localhost:8080/renren-fast/captcha.jpg
            # (?<segment>.*)  $\{segment} 相当于片段
            - RewritePath=/api/(?<segment>.*),/renren-fast/$\{segment}

        # 域名映射只能放在最后面,否则会覆盖上面的api路径
        # 例如我们访问product的api路径,优先匹配到这个的话就会去直接找product
        # 不会再路径重写了
        - id: gulimall_host_route
          uri: lb://gulimall-product
          predicates:
            - Host=**.gulimall.com

4.3.5. Pit point: When Nginx proxies to the gateway, it will lose the requested host and other information

4.3.6, the test passed

Restart the Nginx container and visit "gulimall.com":

Access interface:

4.3.7 Summary, reverse proxy process

  1. The client accesses the windows browser gulimall.com, and the browser will access our configured virtual machine
  2. nginx in the virtual machine listens to port 80 (server_name is also configured with gulimall.com), and include /etc/nginx/conf.d/*.conf;the configuration in nginx.conf includes all .conf files under conf.d to configure the server
  3. The gulimall.conf configuration proxy_pass http://gulimall;means that access to this address will be transferred to nginx.conf upstream gulimall, where the address of the gateway is configured
  4. When the above-mentioned nginx hands over the request to the gateway, nginx will lose a lot of data, such as host, cookie, etc., and the assertion of our gateway configuration is based on the host. This proxy_set_header Host $host;configuration represents the retention of host information
  5. The gateway forwards the request to the corresponding service according to the domain name assertion

insert image description here

When nginx proxies to the gateway, the requested Host information will be lost, which needs to be configured.

Guess you like

Origin blog.csdn.net/qq_40991313/article/details/129782919