谷粒商城十一整合thymeleaf渲染商城首页及nginx搭建域名访问环境

商城系统简介

我们的商城系统本应该也是前后端分离的,就像后台管理系统那样,然而出于教学考虑,前后端分离的话就会屏蔽掉很多细节,所以我们进行服务端的页面渲染式开发(有点儿类似freemarker)

这些页面直接粘贴到微服务中去就行了,

用户访问所有请求,全部先访问的是nginx,nginx作为反向代理将数据全部转发给网关,网关再路由到各个服务

nginx在后面部署的时候,我们可以将微服务中的页面的静态资源部署到nginx中,这样就在部署期间做到了动静分离,好处是可以分担微服务的压力

动静分离中的静指的是:图片、js、css等静态资源(以实际文件存在的方式)

每一个微服务只来管理自己的页面,最终做到每一个微服务都可以独立部署、运行、升级、独立自治的,每一个微服务的数据库、技术都是自治的,不一定商品服务用java开发,用php、js都可以,无论是从技术层面、架构层面还是业务都是独立自治的。
在这里插入图片描述

模板技术与前端框架的区别

在这里插入图片描述

整合thymeleaf渲染商城首页

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.atlinxi.gulimall</groupId>
    <artifactId>gulimall-product</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>gulimall-product</name>
    <description>谷粒商城-商品服务</description>
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>2020.0.4</spring-cloud.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

        <dependency>
            <groupId>com.atlinxi.gulimall</groupId>
            <artifactId>gulimall-common</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!--        由于SpringCloud Feign高版本不使用Ribbon而是使用spring-cloud-loadbalancer,
            所以需要引用spring-cloud-loadbalancer或者降版本-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-loadbalancer</artifactId>
        </dependency>

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

<!--        解决thymeleaf页面修改必须得重启服务器的问题,页面修改之后,需要重新build修改的页面-->
<!--        前提是需要关闭thymeleaf的缓存-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>


    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

将静态资源和页面复制到resources下,将controller包改名为app,新增web包,该包下的controller是返回给thymeleaf的,它默认的前缀和后缀分别是classpath:/templates/.html,springboot访问项目的时候自动会找index.html

// 关闭缓存
spring:
  thymeleaf:
    cache: false
// controller

package com.atlinxi.gulimall.product.web;

import com.atlinxi.gulimall.product.entity.CategoryEntity;
import com.atlinxi.gulimall.product.service.CategoryService;
import com.atlinxi.gulimall.product.vo.Catelog2Vo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.List;
import java.util.Map;

/**
 * 返回给thymeleaf用的 @Controller
 */
@Controller
public class IndexController {

    @Autowired
    CategoryService categoryService;

    @GetMapping({"/","/index.html"})
    public String indexPage(Model model){

        // 1. 查出所有的一级分类
        List<CategoryEntity> categoryEntities = categoryService.getLevel1Categorys();

        // model中的数据,springmvc就会放到页面的请求域中
        model.addAttribute("categorys",categoryEntities);
        return "index";
    }


    /**
     * 上面是跳转页面,不加@ResponseBody
     * 这儿是返回json,要加
     * @return
     */
    @GetMapping("/index/catalog.json")
    @ResponseBody
    public Map<String, List<Catelog2Vo>> getCatalogJson(){

        Map<String, List<Catelog2Vo>> map = categoryService.getCatalogJson();

        return map;
    }
}














// service 前端的js和页面都是复制进来的
@Override
    public List<CategoryEntity> getLevel1Categorys() {
        List<CategoryEntity> categoryEntities = baseMapper.selectList(new QueryWrapper<CategoryEntity>().eq("parent_cid", 0));
        return categoryEntities;
    }

    @Override
    public Map<String, List<Catelog2Vo>> getCatalogJson() {

        // 1. 查出所有一级分类数据
        List<CategoryEntity> level1Categorys = getLevel1Categorys();

        // 2. 封装数据
        // 这儿的key和value都是level1Categorys
        Map<String, List<Catelog2Vo>> parentCid = level1Categorys.stream().collect(Collectors.toMap(key -> key.getCatId().toString(), value -> {
            // 1. 每一个的一级分类,查询这个一级分类的二级分类
            List<CategoryEntity> categoryEntities = baseMapper.selectList(new QueryWrapper<CategoryEntity>()
                    .eq("parent_cid", value.getCatId()));

            // 2. 封装上面的结果
            List<Catelog2Vo> catelog2Vos = null;
            if (categoryEntities != null) {
                catelog2Vos = categoryEntities.stream().map(level2 -> {
                    Catelog2Vo catelog2Vo = new Catelog2Vo(value.getCatId().toString(), null, level2.getCatId().toString(), level2.getName());

                    // 1. 找当前二级分类的三级分类封装成vo
                    List<CategoryEntity> level3Catelog = baseMapper.selectList(new QueryWrapper<CategoryEntity>()
                            .eq("parent_cid", level2.getCatId()));

                    if (level3Catelog != null){
                        // 2. 封装成指定格式
                        List<Catelog2Vo.Catelog3Vo> collect = level3Catelog.stream().map(level3 -> {
                            Catelog2Vo.Catelog3Vo catelog3Vo = new Catelog2Vo.Catelog3Vo(level2.getCatId().toString(),level3.getCatId().toString(),level3.getName());
                            return catelog3Vo;
                        }).collect(Collectors.toList());
                        catelog2Vo.setCatalog3List(collect);

                    }

                    return catelog2Vo;
                }).collect(Collectors.toList());
            }

            return catelog2Vos;

        }));

        return parentCid;
    }

nginx搭建域名访问环境

反向代理配置

我们在访问项目的时候访问的域名是本地的,http://localhost:12000/#

我们希望它是一个合法的域名,而不是本地,

按照正常流程,项目上线是需要买一台服务器的,我们有一个公网ip地址,这样的话别人就都能访问到这个服务器,然后再为这个公网ip地址绑定域名,最后再做一些备案等操作,

那这样,别人访问我们的域名就能访问到我们的服务器,进而访问到项目。

对于现在正在开发的我们太麻烦了,我们先搭建出开发的基本环境,上线以后再做上面正规的流程。

nginx相关知识看这里 todo

修改windowshosts

windows域名解析会先找C:\Windows\System32\drivers\etc下的hosts文件,然后才找dns服务器

当我们在浏览器输入gulimall.com的时候,就会去找我们的虚拟机,此时虚拟机中的nginx是运行的话,就可以访问到nginx,因为nginx监听的是80端口

192.168.56.10	gulimall.com

配置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;
    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{
      server 192.168.56.1:88;
    }

    include /etc/nginx/conf.d/*.conf;


}

配置nginx/conf.d/gulimall.conf

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;
       proxy_pass http://gulimall;
    }

    #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;
    #}
}

在这里插入图片描述

gateway网关路由配置

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}

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

反向代理流程

  1. 客户端访问windows浏览器端gulimall.com,浏览器会访问我们配置好的虚拟机
  2. 虚拟机中的nginx监听80端口(server_name也配置了gulimall.com),nginx.conf中include /etc/nginx/conf.d/*.conf;配置代表包含conf.d下的所有.conf文件,用来配置server
  3. gulimall.conf配置 proxy_pass http://gulimall;,代表访问这个地址会转交到nginx.conf的upstream gulimall下,这里配置的是网关的地址
  4. 在上述nginx将请求移交给网关的时候,nginx会丢失好些数据,例如host、cookie等等,而我们网关配置的断言又是根据host,proxy_set_header Host $host;这个配置代表保留host信息
  5. gateway根据域名断言将请求转发给对应的服务

在这里插入图片描述

nginx代理给网关的时候,会丢失请求的Host信息,需要配置。

域名映射最终的效果

  1. 接口的调用可以通过gulimall.com
  2. 请求页面可以通过gulimall.com
  3. nginx直接代理给网关,网关判断 /api/** 转交给对应的服务器,如果满足域名,则转交给对应的服务

springboot 页面跳转

这个是之前做thymeleaf文章的时候总结的,总结完之后发现这个知识点不知道应用场景,先在这儿放着吧,有用的时候再说

关于SpringBoot的页面跳转问题,跳转页面一般分为静态页面和动态页面。

我理解静态页面就是不需要任何后台数据的获取,单纯获取这个页面,动态页面则反之。

在springboot项目template下的html用一个点击事件从后台controller跳转页面也不行。

在src/main/resources下面有两个文件夹,static和templates,springboot默认static中放静态页面和静态资源文件,而templates中放动态页面,动态页面访问的话需要Thymeleaf的依赖,动态页面需要从后台Controller跳转,静态页面直接类似于:http://127.0.0.1:8080/index.html 访问就可以了;如果static下的无法访问,首先你要保证了你这几个文件访问,不会被拦截器干掉

SpringBoot项目中static目录和templates目录,默认static中放静态资源文件,例如:img、js、css、font等等,如果静态html页面放在static下,一是可以直接当做静态资源访问;另外如果有一种情况,如果页面是放在static下面的,同时也需要从controller来跳转,那么可以采用重定向的方式,因为spring boot 默认的模板存放在 /resource/templates下,不会到 static 目录下去寻找。redirect其实就是重定向到外部资源;其实动态页面放在templates下,大家也都知道,需要从Controller来跳转访问,这些都是SpringBoot约定成俗的一些配置;

静态页面

静态页面是可以直接访问的,在static目录下新建一个hello.html就可以在浏览器直接访问http://localhost:8080/hello.html,也可以通过controller层跳转访问。

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

动态页面

动态页面的访问需要先请求服务器,访问后台的应用程序,然后再转向访问页面。
spring boot默认使用thymeleaf做动态页面,建议不要使用jsp,下面是spring boot整合thymeleaf的写法

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
  • 在tempates目录下新建hello.html文件,此时若直接访问http://localhost:8080/hello.html访问的是静态文件夹(static目录)下的hello.html,再通过controller层跳转访问static会报500错误,这是因为

    • 静态页面的return默认是跳转到static目录下的
    • 当引入thymeleaf之后,动态跳转会覆盖默认的静态跳转
    • 动态跳转默认跳转到templates目录下
    • 两者return代码区别:动态跳转有无.html后缀都可

使用thymeleaf之后的controller层

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

若在使用动态页面时还想跳转到/static/index.html,使用重定向return “redirect:/index.html”

部分内容转载自:
https://www.freesion.com/article/8977511878/

多佛邮车和往常一样“有好亲切”:警卫怀疑乘客,乘客既互相怀疑,也怀疑警卫,大家都怀疑别人,马车夫则除了那几匹马之外,什么也不相信。

双城记
狄更斯

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值