1. 格式化RedisTemplate保存的对象为json格式
@Configuration
public class RedisTemplateConfig {
@Autowired
public RedisTemplateConfig(RedisTemplate redisTemplate) {
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(
Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL,
JsonTypeInfo.As.WRAPPER_ARRAY);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
StringRedisSerializer stringKeySerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(stringKeySerializer);
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashKeySerializer(stringKeySerializer);
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
}
}
2. 创建objecMapper配置类
@Configuration
public class BeanConfig {
@Bean
public ObjectMapper getObjectMapper() {
return new ObjectMapper();
}
}
3. 属性赋值, 业务key
@GetMapping("/admin-user")
public AdminDTO admin(String token) {
Admin admin = (Admin) redisTemplate.opsForValue()
.get(RedisPrefix.TOKEN_KEY + token);
AdminDTO adminDTO = new AdminDTO();
BeanUtils.copyProperties(admin, adminDTO);
return adminDTO;
}
public interface RedisPrefix {
String TOKEN_KEY = "TOKEN:";
String VIDEO_LIKE_COUNT_PREFIX = "VIDEO_LIKE_COUNT:";
String USER_LIKED = "USER_LIKED:";
String VIDEO_PLAYED_COUNT_PREFIX = "VIDEO_PLAYED_COUNT:";
String USER_DISLIKE_PREFIX = "USER_DISLIKE:";
String SESSION = "session:";
}
4. 自定义注解, 获取redis中的token
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequiredToken {
}
@Configuration
public class MvcConfig implements WebMvcConfigurer {
@Autowired
private TokenInterceptors tokenInterceptors;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(tokenInterceptors)
.addPathPatterns("/**");
}
}
@Component
@Slf4j
public class TokenInterceptors implements HandlerInterceptor {
@Autowired
private StringRedisTemplate redisTemplate;
@Autowired
private ObjectMapper objectMapper;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
boolean requiredToken = ((HandlerMethod) handler).getMethod().isAnnotationPresent(RequiredToken.class);
if (requiredToken) {
String token = request.getParameter("token");
log.info("拦截器获取的token信息为{}", token);
String tokenKey = "session_" + token;
String userStr = redisTemplate.opsForValue().get(tokenKey);
User user = objectMapper.readValue(userStr, User.class);
if (user == null) throw new RuntimeException("无效的token");
request.setAttribute("token", token);
request.setAttribute("user", user);
}
return true;
}
}
![在这里插入图片描述](https://img-blog.csdnimg.cn/d78cdb6005634a229bb71f6adf1248e5.png)
5. 自定义过滤器配置, 设置需要token才可以访问的接口
@Component
public class TokenGatewayFilterFactory extends AbstractGatewayFilterFactory<TokenGatewayFilterFactory.Config> {
@Autowired
private StringRedisTemplate redisTemplate;
public TokenGatewayFilterFactory() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
if (config.requiredToken) {
if (exchange.getRequest().getQueryParams().get(0) == null) throw new RuntimeException("非法异常!");
String token = exchange.getRequest().getQueryParams().get("token").get(0);
if (!redisTemplate.hasKey(RedisPreFix.TOKEN_KEY + token)) throw new RuntimeException("不合法的令牌!");
}
return chain.filter(exchange);
}
};
}
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("requiredToken", "name");
}
public static class Config {
private boolean requiredToken;
public boolean isRequiredToken() {
return requiredToken;
}
public void setRequiredToken(boolean requiredToken) {
this.requiredToken = requiredToken;
}
}
}
server:
port: 9999
spring:
application:
name: API-GATEWAY
cloud:
nacos:
server-addr: 192.168.216.128:8848
gateway:
routes: #断言 用来配置路由规则 id uri path
#admins router
- id: admins_router
uri: lb://API-ADMINS #负载均衡写法
predicates:
#采用这种路径断言办法,请求进入网关断言,但是不能已这种方式发送给controller,因为没有此路径的请求
- Path=/admin/demos,/admin/tokens,/admin/admin-user,/admin/tokens
6.全局异常处理
@Configuration
public class GlobalExceptionConfiguration implements ErrorWebExceptionHandler {
@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
Map<String, String> result = new HashMap<>();
ServerHttpResponse response = exchange.getResponse();
if (response.isCommitted()) {
return Mono.error(ex);
}
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
if (ex instanceof IllegalTokenException) {
response.setStatusCode(HttpStatus.FORBIDDEN);
} else {
response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
}
return response
.writeWith(Mono.fromSupplier(() -> {
DataBufferFactory bufferFactory = response.bufferFactory();
result.put("msg", ex.getMessage());
ObjectMapper objectMapper = new ObjectMapper();
try {
return bufferFactory.wrap(objectMapper.writeValueAsBytes(result));
} catch (JsonProcessingException e) {
e.printStackTrace();
return null;
}
}));
}
}
7. 用户上传接口, 使用MQ异步上传
@PostMapping("/user/videos")
@RequiredToken
public Video publishVideos(MultipartFile file, Video video,
Integer category_id, HttpServletRequest request) throws IOException {
String originalFilename = file.getOriginalFilename();
String ext = FilenameUtils.getExtension(originalFilename);
String uuidFileName = UUID.randomUUID().toString().replace("-", "");
String newFileName = uuidFileName + "" + ext;
String url = OSSUtils.upload(file.getInputStream(), "videos", newFileName);
log.info("上传成功返回的路径为:{}", url);
String cover = url
+ "?x-oss-process=video/snapshot,t_30000,f_jpg,w_0,h_0,m_fast,ar_auto";
log.info("阿里云oss根据ur1截取视频封面: {}", cover);
User user = (User) request.getAttribute("user");
video.setUid(user.getId());
video.setLink(url);
video.setCover(cover);
video.setCategoryId(category_id);
Video videoResult = videosClient.publish(video);
log.info("保存成功之后返回的视频信息:{}", videoResult);
return videoResult;
}
VideoClient层代码, 转发到消费者代码
@PostMapping("publish")
public Video publish(@RequestBody Video video) {
log.info("接收到的video对象为:{}", video);
return videoService.insert(video);
}
Service层, mq对象
@Override
public Video insert(Video video) {
video.setCreatedAt(new Date());
video.setUpdatedAt(new Date());
video.setLikes(0L);
videoMapper.insert(video);
VideoVo videoVO = this.getVideoVO(video);
rabbitTemplate.convertAndSend("videos", "",
JSONUtils.writeJSON(videoVO));
return video;
}
服务消费者代码, 在search微服务里面, 同时保存到el中, 方便搜索
@Component
@Slf4j
public class VideoConsumer {
@Autowired
@Qualifier("elasticsearchClient")
private RestHighLevelClient restHighLevelClient;
@Autowired
private ObjectMapper objectMapper;
@RabbitListener(bindings = @QueueBinding(
value = @Queue,
exchange = @Exchange(name = "videos", type = "fanout")
))
public void receive(String message) throws IOException {
log.info("消费者MQ接收到的video信息为:{}", message);
VideoVo videoVo = objectMapper.readValue(message, VideoVo.class);
IndexRequest indexRequest = new IndexRequest("video", "video", videoVo.getId().toString());
indexRequest.source(message, XContentType.JSON);
IndexResponse indexResponse = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
log.info(" video录入ES中的信息为:{}", indexResponse);
}
}
7. 分布式搜索Elasticsearch
@Configuration
public class RestClientConfig extends AbstractElasticsearchConfiguration {
@Bean
@Override
public RestHighLevelClient elasticsearchClient() {
ClientConfiguration clientConfiguration = ClientConfiguration.builder()
.connectedTo("localhost:9200")
.build();
return RestClients.create(clientConfiguration).rest();
}
}
@Service
public class VideoServiceSearchImpl implements VideoSearchService {
@Qualifier("elasticsearchClient")
@Autowired
private RestHighLevelClient restHighLevelClient;
@Autowired
private ObjectMapper objectMapper;
@Override
public Map<String, Object> videos(String q, Integer page, Integer rows) {
HashMap<String, Object> map = new HashMap<>();
int start = (page - 1) * rows;
SearchRequest request = new SearchRequest();
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder
.from(start).size(rows)
.query(QueryBuilders.termQuery("title", q));
request.indices("video").types("video").source(searchSourceBuilder);
SearchResponse searchResponse = null;
try {
searchResponse = restHighLevelClient.search(request, RequestOptions.DEFAULT);
Long totalHits = searchResponse.getHits().getTotalHits().value;
ArrayList<VideoVo> videoVos = new ArrayList<>();
if (totalHits > 0) {
map.put("total_count", totalHits);
SearchHit[] hits = searchResponse.getHits().getHits();
for (SearchHit hit : hits) {
String videoVOJSON = hit.getSourceAsString();
VideoVo videoVo = objectMapper.readValue(videoVOJSON, VideoVo.class);
videoVo.setId(Integer.parseInt(hit.getId()));
videoVos.add(videoVo);
}
}
map.put("items", videoVos);
return map;
} catch (IOException e) {
e.printStackTrace();
}
return map;
}
}