从零起步基于ElasticSearch的搜房网(前后端集成)实战(介绍与整体目录)点击即可
静态资源集成太多页面,我已经上传到博客资源链接,供下载。后期代码全部完善后,会上传到github上。
静态资源链接下载(点击)
系统架构设计
项目的结构分层:
新建一个web包,把之前的controller移到web包里面:
API结构设计:
RESTFul风格:
- 资源命名
- HTTP动词
- 统一数据格式
REST是REpresentational State Transfer的缩写(一般中文翻译为表述性状态转移),REST 是一种体系结构,而 HTTP 是一种包含了 REST 架构属性的协议,为了便于理解,我们把它的首字母拆分成不同的几个部分:
- 表述性(REpresentational): REST 资源实际上可以用各种形式来进行表述,包括 XML、JSON 甚至 HTML——最适合资源使用者的任意形式;
- 状态(State): 当使用 REST 的时候,我们更关注资源的状态而不是对资源采取的行为;
- 转义(Transfer): REST 涉及到转移资源数据,它以某种表述性形式从一个应用转移到另一个应用。
简单地说,REST 就是将资源的状态以适合客户端或服务端的形式从服务端转移到客户端(或者反过来)。在 REST 中,资源通过 URL 进行识别和定位,然后通过行为(即 HTTP 方法)来定义 REST 来完成怎样的功能。
API数据格式标准:
- code:自定义请求状态码
- message:自定义请求响应信息描述
- data:请求目标数据
开始项目的操作
新建一个base的package。存放一些标注结构体。
项目结构如下图所示:
ApiResponse:
package liangliang.bigdata.base;
/**
* api封装
*/
public class ApiResponse {
private int code;//自定义请求编码
private String message;//自定义请求响应信息描述
private Object data; //目标数据
private boolean more; //是否有更多的数据信息
//进行构造器
public ApiResponse(int code, String message, Object data) {
this.code = code;
this.message = message;
this.data = data;
}
//如果只确认状态信息,那么就用空的构造器来进行
public ApiResponse() {
this.code = Status.SUCCESS.getCode();
this.message = Status.SUCCESS.getStandardMessage();
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public boolean isMore() {
return more;
}
public void setMore(boolean more) {
this.more = more;
}
public static ApiResponse OfMessage(int code,String message){
return new ApiResponse(code,message,null);
}
public static ApiResponse OfSuccess(Object data){
return new ApiResponse(Status.SUCCESS.getCode(),Status.SUCCESS.getStandardMessage(),data);
}
public static ApiResponse OfStatus(Status status){
return new ApiResponse(status.getCode(),status.getStandardMessage(),null);
}
//自定义信息描述
//存储对应code对应的信息
public enum Status{
SUCCESS(200,"OK"),
BAD_REQUEST(400,"Bad Request"),
INTERNAL_SERVER_ERROR(500,"Unknow Internal Error"),
NOT_VALID_PARAM(40005,"Not valid Params"),
NOT_SUPPORTED_OPREATION(40006,"Operation not support"),
NOT_LOGIN(50000,"Not Login");
private int code;
private String standardMessage;
Status(int code, String standardMessage) {
this.code = code;
this.standardMessage = standardMessage;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getStandardMessage() {
return standardMessage;
}
public void setStandardMessage(String standardMessage) {
this.standardMessage = standardMessage;
}
}
}
增加路由看状态码显示的信息:
HomeController:
package liangliang.bigdata.web.controller;
import liangliang.bigdata.base.ApiResponse;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* 用于访问主路径
*/
@Controller
public class HomeController {
@GetMapping("/")
public String index(Model model) {
model.addAttribute("name","无痕");
return "index";
}
@GetMapping("/get")
@ResponseBody//返回结构体,也就是json数据
public ApiResponse get() {
return ApiResponse.OfMessage(200,"登录成功");
}
}
现在启动项目,我们访问我们测试的路由看一下信息:
异常拦截器:
- 为什么有异常拦截器:在我们应用项目运行时候可能出现很多问题,用户访问页面或者接口不存在,或者用户权限不足。
- 页面异常拦截器:404、403、500
- API异常拦截器:404、403、500
指定统一的拦截器帮我们处理一些出错的情况,用户友好性更强。
当我们访问错的接口,springBoot会自动帮我们生产一个错误的界面:
这个界面用户是看不懂的,不方便我们后期进行调试,我们需要自己定制化:
新建一个在controller,的AppErrorController。
AppErrorController:
package liangliang.bigdata.web.controller;
import liangliang.bigdata.base.ApiResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.ErrorAttributes;
import org.springframework.boot.autoconfigure.web.ErrorController;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
/**
* Web处理全局配置
*/
@Controller
public class AppErrorController implements ErrorController {
private static final String ERROR_PATH = "/error";
private ErrorAttributes errorAttributes;
@Override
public String getErrorPath() {
return ERROR_PATH;
}
@Autowired
public AppErrorController(ErrorAttributes errorAttributes){
this.errorAttributes = errorAttributes;
}
/**
* Web页面错误处理
*/
@RequestMapping(value = ERROR_PATH,produces = "text/html") //针对web页面的访问
public String errorPageHandler(HttpServletRequest request, HttpServletResponse response){
int status = response.getStatus();//获取当前状态码
switch(status){
case 403:
return "403";
case 404:
return "404";
case 500:
return "500";
}
//最后所有状态没有找返回主页
return "index";
}
/**
* 除Web页面外的的处理,包括json,xml
*/
@RequestMapping(value = ERROR_PATH)
@ResponseBody
public ApiResponse errorApiHandler(HttpServletRequest request){
RequestAttributes requestAttributes = new ServletRequestAttributes(request);
Map<String,Object> attr = this.errorAttributes.getErrorAttributes(requestAttributes,false);
int status = getStatus(request);
return ApiResponse.OfMessage(status, String.valueOf(attr.getOrDefault("message","error")));
}
private int getStatus(HttpServletRequest request){
Integer status = (Integer) request.getAttribute("javax.servlet.error.status_code");
if (status !=null){
return status;
}
return 500;
}
}
在templates文件夹中新建三个HTML页面:404、500、403
403.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Forbidden</title>
</head>
<body>
<h1>Oops!!! Access Denied</h1>
<h2>你无权访问该页面</h2>
<a href="/">返回首页</a>
</body>
</html>
404.html:
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>oops 404 for Website Template for free | Home :: w3layouts</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<link href="/static/css/main.css" rel='stylesheet' type='text/css'/>
<link href="/static/css/index.css" rel='stylesheet' type='text/css'/>
<style type="text/css">
body {
margin: 0;
background: #eaeaea;
}
.wrap {
margin-top: 100px;
}
.wrap .logo {
text-align: center;
height: 390px;
}
.wrap .logo img {
width: 350px;
}
.wrap .logo p {
color: #272727;
font-size: 40px;
margin-top: 30px;
margin-bottom: 30px;
}
.sub a {
color: #fff;
background: #272727;
text-decoration: none;
padding: 10px 20px;
font-size: 13px;
font-family: arial, serif;
font-weight: bold;
-webkit-border-radius: .5em;
-moz-border-radius: .5em;
-border-radius: .5em;
}
</style>
</head>
<body>
<div class="wrap container">
<div class="logo">
<p>OOPS! - Could Not Find it</p>
<img src="/static/images/404.png"/>
<div class="sub">
<p><a href="/">返回首页</a></p>
</div>
</div>
</div>
<div th:replace="common :: footer">footer</div>
</body>
</html>
500.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Error</title>
</head>
<body>
<h1>Internal Server Error</h1>
<h2>Sorry~发生了一些意想不到的问题</h2>
<a href="/">返回首页</a>
</body>
</html>
前端功能性的通用页面进行开发:
- 403权限限制提示页面
- 404Not Found 提示页面
- 500服务异常提示页面
上面我直接写上了。
修改前端引发的热加载,但是这时候需要配置才能生效。
application.properties:
spring.profiles.active=dev
#帮助我们在开发过程中执行的时候jpa我们执行的sql的语句
spring.jpa.show-sql=true
#hibernate 执行只进行sql的验证,不会对sql做一些增删改的操作的
spring.jpa.hibernate.ddl-auto=validate
#sql打印级别设置为debug
logging.level.org.hibernate.SQL = debug
#设置session会话存储的类型
spring.session.store-type = hash_map
#关闭http基本验证
security.basic.enabled=false
#thymeleaf
spring.thymeleaf.mode=HTML
spring.thymeleaf.suffix=.html
spring.thymeleaf.prefix=classpath:/templates/
server.error.whitelabel.enabled=false
spring.devtools.restart.exclude=templates/**,static/**
把之前写的config包下的WebMvcConfig中的SpringResourceTemplateResolver加上下面这句话,解决thymeleaf乱码问题。
WebMvcConfig:
package liangliang.bigdata.config;
import org.springframework.beans.BeansException;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.thymeleaf.spring4.SpringTemplateEngine;
import org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.spring4.view.ThymeleafViewResolver;
@Configuration
//帮助我们获取spring的上下文
public class WebMvcConfig extends WebMvcConfigurerAdapter implements ApplicationContextAware {
//私有化一个类
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//配置静态资源
registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
}
/**
* 对模板资源进行解析
* 模板资源解析器
*/
@Bean
//做一个前缀绑定,否则找不到路径
@ConfigurationProperties(prefix = "spring.thymeleaf")
public SpringResourceTemplateResolver templateResolver (){
//实例化一个
SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
//设置spring的上下文
templateResolver.setApplicationContext(this.applicationContext);
templateResolver.setCharacterEncoding("UTF-8");
return templateResolver;
}
/**
* Thymeleaf标准方言解析器
*/
@Bean
public SpringTemplateEngine templateEngine(){
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
//配置资源解析器的引擎
templateEngine.setTemplateResolver(templateResolver());
//设置支持EL表达式
templateEngine.setEnableSpringELCompiler(true);
return templateEngine;
}
/**
* 视图解析器
*/
@Bean
public ThymeleafViewResolver viewResolver(){
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
//配置Thymeleaf标准方言解析器
viewResolver.setTemplateEngine(templateEngine());
return viewResolver;
}
}
增加路由,然后验证页面:
HomeController:
package liangliang.bigdata.web.controller;
import liangliang.bigdata.base.ApiResponse;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* 用于访问主路径
*/
@Controller
public class HomeController {
@GetMapping("/")
public String index(Model model) {
model.addAttribute("name","无痕");
return "index";
}
@GetMapping("/get")
@ResponseBody//返回结构体,也就是json数据
public ApiResponse get() {
return ApiResponse.OfMessage(200,"登录成功");
}
@GetMapping("/404")
public String notFound () {
return "404";
}
@GetMapping("/403")
public String accessError () {
return "403";
}
@GetMapping("/500")
public String internalError () {
return "500";
}
}