欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

利用Spring Boot如何開發(fā)REST服務詳解

 更新時間:2017年12月09日 09:18:01   作者:JadePeng  
這篇文章主要給大家介紹了關于利用Spring Boot如何開發(fā)REST服務的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧。

REST服務介紹

RESTful service是一種架構模式,近幾年比較流行了,它的輕量級web服務,發(fā)揮HTTP協(xié)議的原生的GET,PUT,POST,DELETE。 REST模式的Web服務與復雜的SOAP和XML-RPC對比來講明顯的更加簡潔,越來越多的web服務開始采用REST風格設計和實現(xiàn)。例如,Amazon.com提供接近REST風格的Web服務進行圖書查找;雅虎提供的Web服務也是REST風格的。REST 并非始終是正確的選擇。 它作為一種設計 Web 服務的方法而變得流行,這種方法對專有中間件(例如某個應用程序服務器)的依賴比基于 SOAP 和 WSDL 的方法更少。 在某種意義上,通過強調URI和HTTP等早期 Internet 標準,REST 是對大型應用程序服務器時代之前的 Web 方式的回歸。

如下圖示例:

使用REST的關鍵是如何抽象資源,抽象得越精確,對REST的應用就越好。

REST服務關鍵原則:

       1. 給一切物體一個ID

       2.連接物體在一起

       3.使用標準方法

       4.資源多重表述

       5.無狀態(tài)通信

本文介紹如何基于Spring Boot搭建一個簡易的REST服務框架,以及如何通過自定義注解實現(xiàn)Rest服務鑒權

搭建框架

pom.xml

首先,引入相關依賴,數(shù)據(jù)庫使用mongodb,同時使用redis做緩存

注意:這里沒有使用tomcat,而是使用undertow

 <dependency>  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter</artifactId>
 </dependency>
 <dependency>  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-test</artifactId>
  <scope>test</scope>
 </dependency>
 <dependency>  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
  <exclusions>
  <exclusion>   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-tomcat</artifactId>
  </exclusion>
  </exclusions>
 </dependency>
 <dependency>  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-undertow</artifactId>
 </dependency>
 <!--redis支持-->
 <dependency>  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
 </dependency>
 <!--mongodb支持-->
 <dependency>  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-mongodb</artifactId>
 </dependency>

引入spring-boot-starter-web支持web服務

引入spring-boot-starter-data-redis 和spring-boot-starter-data-mongodb就可以方便的使用mongodb和redis了

配置文件

profiles功能

為了方便 區(qū)分開發(fā)環(huán)境和線上環(huán)境,可以使用profiles功能,在application.properties里增加
spring.profiles.active=dev

然后增加application-dev.properties作為dev配置文件。

mondb配置

配置數(shù)據(jù)庫地址即可

spring.data.mongodb.uri=mongodb://ip:port/database?readPreference=primaryPreferred

redis配置

spring.redis.database=0 
# Redis服務器地址
spring.redis.host=ip
# Redis服務器連接端口
spring.redis.port=6379 
# Redis服務器連接密碼(默認為空)
spring.redis.password=
# 連接池最大連接數(shù)(使用負值表示沒有限制)
spring.redis.pool.max-active=8 
# 連接池最大阻塞等待時間(使用負值表示沒有限制)
spring.redis.pool.max-wait=-1 
# 連接池中的最大空閑連接
spring.redis.pool.max-idle=8 
# 連接池中的最小空閑連接
spring.redis.pool.min-idle=0 
# 連接超時時間(毫秒)
spring.redis.timeout=0 

數(shù)據(jù)訪問

mongdb

mongdb訪問很簡單,直接定義接口extends MongoRepository即可,另外可以支持JPA語法,例如:

@Component
public interface UserRepository extends MongoRepository<User, Integer> {
 public User findByUserName(String userName);
}

使用時,加上@Autowired注解即可。

@Component
public class AuthService extends BaseService {
 @Autowired
 UserRepository userRepository;
 }

Redis訪問

使用StringRedisTemplate即可直接訪問Redis

@Component
public class BaseService {
 @Autowired
 protected MongoTemplate mongoTemplate;
 @Autowired
 protected StringRedisTemplate stringRedisTemplate;

 }

儲存數(shù)據(jù):

.stringRedisTemplate.opsForValue().set(token_key, user.getId()+"",token_max_age, TimeUnit.SECONDS);

刪除數(shù)據(jù):

stringRedisTemplate.delete(getFormatToken(accessToken,platform));

Web服務

定義一個Controller類,加上RestController即可,使用RequestMapping用來設置url route

@RestController
public class AuthController extends BaseController {
 @RequestMapping(value = {"/"}, produces = "application/json;charset=utf-8", method = {RequestMethod.GET, RequestMethod.POST})
 @ResponseBody
 public String main() {
 return "hello world!";
 }
}

現(xiàn)在啟動,應該就能看到hello world!了

服務鑒權

簡易accessToken機制

提供登錄接口,認證成功后,生成一個accessToken,以后訪問接口時,帶上accessToken,服務端通過accessToken來判斷是否是合法用戶。

為了方便,可以將accessToken存入redis,設定有效期。

  String token = EncryptionUtils.sha256Hex(String.format("%s%s", user.getUserName(), System.currentTimeMillis()));
  String token_key = getFormatToken(token, platform);
  this.stringRedisTemplate.opsForValue().set(token_key, user.getId()+"",token_max_age, TimeUnit.SECONDS);

攔截器身份認證

為了方便做統(tǒng)一的身份認證,可以基于Spring的攔截器機制,創(chuàng)建一個攔截器來做統(tǒng)一認證。

public class AuthCheckInterceptor implements HandlerInterceptor {
}

要使攔截器生效,還需要一步,增加配置:

@Configuration
public class SessionConfiguration extends WebMvcConfigurerAdapter {
 @Autowired
 AuthCheckInterceptor authCheckInterceptor;
 @Override
 public void addInterceptors(InterceptorRegistry registry) {
  super.addInterceptors(registry);
  // 添加攔截器
  registry.addInterceptor(authCheckInterceptor).addPathPatterns("/**");
 }
}

自定義認證注解

為了精細化權限認證,比如有的接口只能具有特定權限的人才能訪問,可以通過自定義注解輕松解決。在自定義的注解里,加上roles即可。

/**
 * 權限檢驗注解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AuthCheck {

 /**
  * 角色列表
  * @return
  */
 String[] roles() default {};
}

檢驗邏輯:

只要接口加上了AuthCheck注解,就必須是登陸用戶

如果指定了roles,則除了登錄外,用戶還應該具備相應的角色。

 String[] ignoreUrls = new String[]{
   "/user/.*",
   "/cat/.*",
   "/app/.*",
   "/error"
 };
 public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler) throws Exception {
  // 0 檢驗公共參數(shù)
  if(!checkParams("platform",httpServletRequest,httpServletResponse)){
   return false;
  }
  // 1、忽略驗證的URL
  String url = httpServletRequest.getRequestURI().toString();
  for(String ignoreUrl :ignoreUrls){
   if(url.matches(ignoreUrl)){
    return true;
   }
  }
  // 2、查詢驗證注解
  HandlerMethod handlerMethod = (HandlerMethod) handler;
  Method method = handlerMethod.getMethod();
  // 查詢注解
  AuthCheck authCheck = method.getAnnotation(AuthCheck.class);
  if (authCheck == null) {
   // 無注解,不需要
   return true;
  }
  // 3、有注解,先檢查accessToken
  if(!checkParams("accessToken",httpServletRequest,httpServletResponse)){
   return false;
  }
  // 檢驗token是否過期
  Integer userId = authService.getUserIdFromToken(httpServletRequest.getParameter("accessToken"),
    httpServletRequest.getParameter("platform"));
  if(userId==null){
   logger.debug("accessToken timeout");
   output(ResponseResult.Builder.error("accessToken已過期").build(),httpServletResponse);
   return false;
  }
  // 4、再檢驗是否包含必要的角色
  if(authCheck.roles()!=null&&authCheck.roles().length>0){
   User user = authService.getUser(userId);
   boolean isMatch = false;
   for(String role : authCheck.roles()){
    if(user.getRole().getName().equals(role)){
     isMatch = true;
     break;
    }
   }
   // 角色未匹配,驗證失敗
   if(!isMatch){
    return false;
   }
  }
  return true;
 }

服務響應結果封裝

增加一個Builder,方便生成最終結果

public class ResponseResult {
 public static class Builder{
  ResponseResult responseResult;
  Map<String,Object> dataMap = Maps.newHashMap();
  public Builder(){
   this.responseResult = new ResponseResult();
  }
  public Builder(String state){
   this.responseResult = new ResponseResult(state);
  }
  public static Builder newBuilder(){
   return new Builder();
  }
  public static Builder success(){
   return new Builder("success");
  }
  public static Builder error(String message){
   Builder builder = new Builder("error");
   builder.responseResult.setError(message);
   return builder;
  }
  public Builder append(String key,Object data){
   this.dataMap.put(key,data);
   return this;
  }
  /**
   * 設置列表數(shù)據(jù)
   * @param datas 數(shù)據(jù)
   * @return
   */
  public Builder setListData(List<?> datas){
   this.dataMap.put("result",datas);
   this.dataMap.put("total",datas.size());
   return this;
  }
  public Builder setData(Object data){
   this.dataMap.clear();
   this.responseResult.setData(data);
   return this;
  }
  boolean wrapData = false;
  /**
   * 將數(shù)據(jù)包裹在data中
   * @param wrapData
   * @return
   */
  public Builder wrap(boolean wrapData){
   this.wrapData = wrapData;
   return this;
  }
  public String build(){
   JSONObject jsonObject = new JSONObject();
   jsonObject.put("state",this.responseResult.getState());
   if(this.responseResult.getState().equals("error")){
    jsonObject.put("error",this.responseResult.getError());
   }
   if(this.responseResult.getData()!=null){
    jsonObject.put("data", JSON.toJSON(this.responseResult.getData()));
   }else if(dataMap.size()>0){
    if(wrapData) {
     JSONObject data = new JSONObject();
     dataMap.forEach((key, value) -> {
      data.put(key, value);
     });
     jsonObject.put("data", data);
    }else{
     dataMap.forEach((key, value) -> {
      jsonObject.put(key, value);
     });
    }
   }
   return jsonObject.toJSONString();
  }

 }
 private String state;
 private Object data;
 private String error;
 public String getError() {
  return error;
 }
 public void setError(String error) {
  this.error = error;
 }
 public ResponseResult(){}

 public ResponseResult(String rc){
  this.state = rc;
 }
 /**
  * 成功時返回
  * @param rc
  * @param result
  */
 public ResponseResult(String rc, Object result){
  this.state = rc;
  this.data = result;
 }
 public String getState() {
  return state;
 }
 public void setState(String state) {
  this.state = state;
 }
 public Object getData() {
  return data;
 }
 public void setData(Object data) {
  this.data = data;
 }
}

調用時可以優(yōu)雅一點

 @RequestMapping(value = {"/user/login","/pc/user/login"}, produces = "application/json;charset=utf-8", method = {RequestMethod.GET, RequestMethod.POST})
 @ResponseBody
 public String login(String userName,String password,Integer platform) {
  User user = this.authService.login(userName,password);
  if(user!=null){
   // 登陸
   String token = authService.updateToken(user,platform);
   return ResponseResult.Builder
      .success()
     .append("accessToken",token)
     .append("userId",user.getId())
     .build();
  }
  return ResponseResult.Builder.error("用戶不存在或密碼錯誤").build();
 } 
 protected String error(String message){
  return ResponseResult.Builder.error(message).build();
 }
 protected String success(){
  return ResponseResult.Builder
    .success()
    .build();
 }
 protected String successDataList(List<?> data){
  return ResponseResult.Builder
    .success()
    .wrap(true) // data包裹
    .setListData(data)
    .build();
 }

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。

相關文章

  • java 遞歸查詢所有子節(jié)點id的方法實現(xiàn)

    java 遞歸查詢所有子節(jié)點id的方法實現(xiàn)

    在多層次的數(shù)據(jù)結構中,經(jīng)常需要查詢一個節(jié)點下的所有子節(jié)點,本文主要介紹了java 遞歸查詢所有子節(jié)點id的方法實現(xiàn),具有一定的參考價值,感興趣的可以了解一下
    2024-03-03
  • java實現(xiàn)Api接口加密通信方式

    java實現(xiàn)Api接口加密通信方式

    這篇文章主要介紹了java實現(xiàn)Api接口加密通信方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • springboot整合redis配置詳細示例代碼

    springboot整合redis配置詳細示例代碼

    Redis是一種高性能的鍵值存儲數(shù)據(jù)庫,而Spring Boot是一個簡化了開發(fā)過程的Java框架,將兩者結合可以輕松地在Spring Boot項目中使用Redis來實現(xiàn)數(shù)據(jù)緩存、會話管理和分布式鎖等功能,這篇文章主要給大家介紹了關于springboot整合redis配置的相關資料,需要的朋友可以參考下
    2023-11-11
  • SpringBoot啟用GZIP壓縮的代碼工程

    SpringBoot啟用GZIP壓縮的代碼工程

    經(jīng)常我們都會與服務端進行大數(shù)據(jù)量的文本傳輸,例如?JSON?就是常見的一種格式,通過?REST?API?接口進行?GET?和?POST?請求,可能會有大量的文本格式數(shù)據(jù)提交、返回,壓縮和解壓在提升網(wǎng)絡帶寬的同時,會帶來?CPU?資源的損耗,本文介紹了SpringBoot啟用GZIP壓縮的代碼工程
    2024-08-08
  • java注解實現(xiàn)websocket服務的兩種方式

    java注解實現(xiàn)websocket服務的兩種方式

    Java WebSocket是一種基于TCP協(xié)議的雙向全雙工消息傳輸技術,它允許服務器和客戶端之間實時通信,具有低延遲和高效率的特點,下面這篇文章主要給大家介紹了關于java注解實現(xiàn)websocket服務的兩種方式,需要的朋友可以參考下
    2024-08-08
  • springboot 實現(xiàn)mqtt物聯(lián)網(wǎng)的示例代碼

    springboot 實現(xiàn)mqtt物聯(lián)網(wǎng)的示例代碼

    這篇文章主要介紹了springboot 實現(xiàn)mqtt物聯(lián)網(wǎng),本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-03-03
  • Java中值類型和引用類型詳解

    Java中值類型和引用類型詳解

    大家好,本篇文章主要講的是Java中值類型和引用類型詳解,感興趣的同學趕快來看一看吧,對你有幫助的話記得收藏一下,方便下次瀏覽
    2022-01-01
  • Java實現(xiàn)通過時間獲取8位驗證碼

    Java實現(xiàn)通過時間獲取8位驗證碼

    這篇文章主要為大家詳細介紹了Java如何通過時間獲取8位驗證碼(每兩個小時生成一個),文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下
    2023-11-11
  • Mybatis的sql語句執(zhí)行異常后打印到日志問題

    Mybatis的sql語句執(zhí)行異常后打印到日志問題

    文章介紹了一種Mybatis異常日志打印方案,主要通過Mybatis攔截器獲取執(zhí)行的sql語句,并利用ThreadLocal存儲,以避免多線程下的sql語句覆蓋問題,當異常發(fā)生時,從ThreadLocal中取出sql語句并打印到單獨的日志文件中,方便數(shù)據(jù)恢復,該方案經(jīng)過壓力測試
    2024-10-10
  • Java安全后端返回文件流方式

    Java安全后端返回文件流方式

    這篇文章主要介紹了Java安全后端返回文件流方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-07-07

最新評論