赞
踩
头条新闻发布和浏览平台,主要包含业务如下:
前端技术栈
后端技术栈
后端项目搭建:
package com.doug.headline.common; /** * 全局统一返回结果类 * */ public class Result<T> { // 返回码 private Integer code; // 返回消息 private String message; // 返回数据 private T data; public Result(){} // 返回数据 protected static <T> Result<T> build(T data) { Result<T> result = new Result<T>(); if (data != null) result.setData(data); return result; } public static <T> Result<T> build(T body, Integer code, String message) { Result<T> result = build(body); result.setCode(code); result.setMessage(message); return result; } public static <T> Result<T> build(T body, ResultCodeEnum resultCodeEnum) { Result<T> result = build(body); result.setCode(resultCodeEnum.getCode()); result.setMessage(resultCodeEnum.getMessage()); return result; } /** * 操作成功 * @param data baseCategory1List * @param <T> * @return */ public static<T> Result<T> ok(T data){ Result<T> result = build(data); return build(data, ResultCodeEnum.SUCCESS); } public Result<T> message(String msg){ this.setMessage(msg); return this; } public Result<T> code(Integer code){ this.setCode(code); return this; } public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public T getData() { return data; } public void setData(T data) { this.data = data; } }
package com.doug.headline.common; /** * 统一返回结果状态信息类 * */ public enum ResultCodeEnum { /** * 自定义业务码 成功 200 */ SUCCESS(200,"success"), USERNAME_ERROR(501,"usernameError"), PASSWORD_ERROR(503,"passwordError"), NOTLOGIN(504,"notLogin"), USERNAME_USED(505,"userNameUsed") ; private Integer code; private String message; private ResultCodeEnum(Integer code, String message) { this.code = code; this.message = message; } public Integer getCode() { return code; } public String getMessage() { return message; } }
package com.doug.headline.util; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public final class MD5Util { public static String encrypt(String strSrc) { try { char hexChars[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; byte[] bytes = strSrc.getBytes(); MessageDigest md = MessageDigest.getInstance("MD5"); md.update(bytes); bytes = md.digest(); int j = bytes.length; char[] chars = new char[j * 2]; int k = 0; for (int i = 0; i < bytes.length; i++) { byte b = bytes[i]; chars[k++] = hexChars[b >>> 4 & 0xf]; chars[k++] = hexChars[b & 0xf]; } return new String(chars); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); throw new RuntimeException("MD5加密出错!!+" + e); } } }
package com.doug.headline.util; import com.alibaba.druid.pool.DruidDataSourceFactory; import javax.sql.DataSource; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.SQLException; import java.util.Properties; public class JDBCUtil { private static ThreadLocal<Connection> threadLocal =new ThreadLocal<>(); private static DataSource dataSource; // 初始化连接池 static{ // 可以帮助我们读取.properties配置文件 Properties properties =new Properties(); InputStream resourceAsStream = JDBCUtil.class.getClassLoader().getResourceAsStream("jdbc.properties"); try { properties.load(resourceAsStream); } catch (IOException e) { throw new RuntimeException(e); } try { dataSource = DruidDataSourceFactory.createDataSource(properties); } catch (Exception e) { throw new RuntimeException(e); } } /*1 向外提供连接池的方法*/ public static DataSource getDataSource(){ return dataSource; } /*2 向外提供连接的方法*/ public static Connection getConnection(){ Connection connection = threadLocal.get(); if (null == connection) { try { connection = dataSource.getConnection(); } catch (SQLException e) { throw new RuntimeException(e); } threadLocal.set(connection); } return connection; } /*定义一个归还连接的方法 (解除和ThreadLocal之间的关联关系) */ public static void releaseConnection(){ Connection connection = threadLocal.get(); if (null != connection) { threadLocal.remove(); // 把连接设置回自动提交的连接 try { connection.setAutoCommit(true); // 自动归还到连接池 connection.close(); } catch (SQLException e) { throw new RuntimeException(e); } } } }
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/top_news
username=root
password=root
initialSize=5
maxActive=10
maxWait=1000
package com.doug.headline.util; import com.alibaba.druid.util.StringUtils; import io.jsonwebtoken.*; import java.util.Date; public class JwtHelper { private static long tokenExpiration = 24*60*60*1000; private static String tokenSignKey = "123456"; //生成token字符串 public static String createToken(Long userId) { String token = Jwts.builder() .setSubject("YYGH-USER") .setExpiration(new Date(System.currentTimeMillis() + tokenExpiration)) .claim("userId", userId) .signWith(SignatureAlgorithm.HS512, tokenSignKey) .compressWith(CompressionCodecs.GZIP) .compact(); return token; } //从token字符串获取userid public static Long getUserId(String token) { if(StringUtils.isEmpty(token)) { return null; } Jws<Claims> claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token); Claims claims = claimsJws.getBody(); Integer userId = (Integer)claims.get("userId"); return userId.longValue(); } //判断token是否有效 public static boolean isExpiration(String token){ try { boolean isExpire = Jwts.parser() .setSigningKey(tokenSignKey) .parseClaimsJws(token) .getBody() .getExpiration().before(new Date()); //没有过期,有效,返回false return isExpire; }catch(Exception e) { //过期出现异常,返回true return true; } } }
package com.doug.headline.util; import com.doug.headline.common.Result; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.BufferedReader; import java.io.IOException; import java.text.SimpleDateFormat; public class WebUtil { private static ObjectMapper objectMapper; // 初始化objectMapper static{ objectMapper=new ObjectMapper(); // 设置JSON和Object转换时的时间日期格式 objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); } // 从请求中获取JSON串并转换为Object public static <T> T readJson(HttpServletRequest request,Class<T> clazz){ T t =null; BufferedReader reader = null; try { reader = request.getReader(); StringBuffer buffer =new StringBuffer(); String line =null; while((line = reader.readLine())!= null){ buffer.append(line); } t= objectMapper.readValue(buffer.toString(),clazz); } catch (IOException e) { throw new RuntimeException(e); } return t; } // 将Result对象转换成JSON串并放入响应对象 public static void writeJson(HttpServletResponse response, Result result){ response.setContentType("application/json;charset=UTF-8"); try { String json = objectMapper.writeValueAsString(result); response.getWriter().write(json); } catch (IOException e) { throw new RuntimeException(e); } } }
vo : value object 针对分页查询的对象
HeadlineQueryVo : 多页查询需要的数据
BaseDao基础类
,封装了公共的查询方法和公共的增删改方法
package com.doug.headline.dao; import com.doug.headline.util.JDBCUtil; import java.lang.reflect.Field; import java.sql.*; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; public class BaseDao { // 公共的查询方法 返回的是单个对象 public <T> T baseQueryObject(Class<T> clazz, String sql, Object ... args) { T t = null; Connection connection = JDBCUtil.getConnection(); PreparedStatement preparedStatement = null; ResultSet resultSet = null; int rows = 0; try { // 准备语句对象 preparedStatement = connection.prepareStatement(sql); // 设置语句上的参数 for (int i = 0; i < args.length; i++) { preparedStatement.setObject(i + 1, args[i]); } // 执行 查询 resultSet = preparedStatement.executeQuery(); if (resultSet.next()) { t = (T) resultSet.getObject(1); } } catch (Exception e) { throw new RuntimeException(e); } finally { if (null != resultSet) { try { resultSet.close(); } catch (SQLException e) { throw new RuntimeException(e); } } if (null != preparedStatement) { try { preparedStatement.close(); } catch (SQLException e) { throw new RuntimeException(e); } } JDBCUtil.releaseConnection(); } return t; } // 公共的查询方法 返回的是对象的集合 public <T> List<T> baseQuery(Class clazz, String sql, Object ... args){ List<T> list =new ArrayList<>(); Connection connection = JDBCUtil.getConnection(); PreparedStatement preparedStatement=null; ResultSet resultSet =null; int rows = 0; try { // 准备语句对象 preparedStatement = connection.prepareStatement(sql); // 设置语句上的参数 for (int i = 0; i < args.length; i++) { preparedStatement.setObject(i+1,args[i]); } // 执行 查询 resultSet = preparedStatement.executeQuery(); ResultSetMetaData metaData = resultSet.getMetaData(); int columnCount = metaData.getColumnCount(); // 将结果集通过反射封装成实体类对象 while (resultSet.next()) { // 使用反射实例化对象 Object obj =clazz.getDeclaredConstructor().newInstance(); for (int i = 1; i <= columnCount; i++) { String columnName = metaData.getColumnLabel(i); Object value = resultSet.getObject(columnName); // 处理datetime类型字段和java.util.Data转换问题 if(value.getClass().equals(LocalDateTime.class)){ value= Timestamp.valueOf((LocalDateTime) value); } Field field = clazz.getDeclaredField(columnName); field.setAccessible(true); field.set(obj,value); } list.add((T)obj); } } catch (Exception e) { throw new RuntimeException(e); } finally { if (null !=resultSet) { try { resultSet.close(); } catch (SQLException e) { throw new RuntimeException(e); } } if (null != preparedStatement) { try { preparedStatement.close(); } catch (SQLException e) { throw new RuntimeException(e); } } JDBCUtil.releaseConnection(); } return list; } // 通用的增删改方法 public int baseUpdate(String sql,Object ... args) { // 获取连接 Connection connection = JDBCUtil.getConnection(); PreparedStatement preparedStatement=null; int rows = 0; try { // 准备语句对象 preparedStatement = connection.prepareStatement(sql); // 设置语句上的参数 for (int i = 0; i < args.length; i++) { preparedStatement.setObject(i+1,args[i]); } // 执行 增删改 executeUpdate rows = preparedStatement.executeUpdate(); // 释放资源(可选) } catch (SQLException e) { throw new RuntimeException(e); } finally { if (null != preparedStatement) { try { preparedStatement.close(); } catch (SQLException e) { throw new RuntimeException(e); } } JDBCUtil.releaseConnection(); } // 返回的是影响数据库记录数 return rows; } }
BaseController 用于将路径关联到处理方法的基础控制器
package com.doug.headline.controller; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.lang.reflect.Method; public class BaseController extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 响应的MIME类型和乱码问题 resp.setContentType("application/json;charset=UTF-8"); String requestURI = req.getRequestURI(); String[] split = requestURI.split("/"); String methodName =split[split.length-1]; // 通过反射获取要执行的方法 Class clazz = this.getClass(); try { Method method=clazz.getDeclaredMethod(methodName,HttpServletRequest.class,HttpServletResponse.class); // 设置方法可以访问 method.setAccessible(true); // 通过反射执行代码 method.invoke(this,req,resp); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e.getMessage()); } } }
/**
* @Description:
* 门户 控制器
* 即 当用户没有登录时候 所看到的页面(首页)
* 不需要做增删改查门户页请求都在这
*/
@WebServlet("/portal")
public class PortalController extends BaseController{
}
- 同源策略(
Same origin policy
)是浏览器最核心也最基本的安全功能,- 如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。
- 可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。
- 同源策略会阻止一个域的
javascript
脚本和另外一个域的内容进行交互。- 所谓同源(即指在同一个域)就是两个页面具有相同的协议(
protocol
),主机(host
)和端口号
- 前后端分离模式下,客户端请求前端服务器获取视图资源,
- 然后客户端自行向后端服务器获取数据资源,
- 前端服务器的 协议,IP和端口和后端服务器很可能是不一样的,这样就产生了跨域
即:
前后端分离项目中:
- 浏览器 访问 前端服务器 获取视图资源
- 浏览器 访问 后端服务器 获取数据资源
- 访问中 前端 和 后端 服务器的 协议 IP 端口 可能不同
- 导致跨域问题(报错 发送数据失败)
前端项目代理模式处理 :
后端跨域过滤器方式处理
package com.doug.headline.filters; import jakarta.servlet.*; import jakarta.servlet.annotation.WebFilter; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; @WebFilter("/*") public class CrosFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletResponse response = (HttpServletResponse) servletResponse; HttpServletRequest request =(HttpServletRequest) servletRequest; response.setHeader("Access-Control-Allow-Origin", "*"); response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, HEAD"); response.setHeader("Access-Control-Max-Age", "3600"); response.setHeader("Access-Control-Allow-Headers", "access-control-allow-origin, authority, content-type, version-info, X-Requested-With"); // 非预检请求,放行即可,预检请求,则到此结束,不需要放行 if(!request.getMethod().equalsIgnoreCase("OPTIONS")){ filterChain.doFilter(servletRequest, servletResponse); } } }
@CrossOrigin
就可以解决跨域问题了
Postman
是一个接口测试工具
,- 在做接口测试的时候,
Postman
相当于一个客户端,- 它可以模拟用户发起的各类
HTTP请求
,将请求数据发送至服务端,获取对应的响应结果, 从而验证响应中的结果数据是否和预期值相匹配;- 并确保开发人员能够及时处理接口中的bug,进而保证产品上线之后的稳定性和安全性。
- 它主要是用来模拟各种HTTP请求的(如:get/post/delete/put…等等),
- Postman与浏览器的区别在于有的浏览器不能输出Json格式,而Postman更直观接口返回的结果。
官网下载地址: https://www.getpostman.com
不使用postman , 用浏览器获取 响应JSON数据,比较麻烦
修改为当前时间:
UPDATE news_headline SET create_time=NOW(),update_time=NOW();
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。