springboot--實戰--大事件--用戶接口開發

開發模式&環境搭建

開發模式:

前后端分離開發

前端程序員寫前端頁面,后端程序員寫后端的接口,前端工程發送請求來訪問后臺,后臺處理完請求后要給前端相應對應的數據。

還需要一套標準來約束即接口文檔,在接口文檔中會對每一個接口的訪問路徑,請求方式以及請求參數還有響應數據進行明確的說明。

一般這樣的接口文檔就需要提供好,有了接口文檔就有了開發的標準。前端程序員參照接口文檔進行開發,后端程序員也參考同一份接口文檔開發,最終開發好的項目就不會出現接口出錯的情況了。

環境搭建

準備數據庫表

創建springboot工程,引入對應的依賴(web,MyBatis,MySQL驅動)

在配置文件中引入MyBatis的配置信息

創建包結構,并準備實體類

注冊接口

用戶

  • 注冊

查看用戶表及其實體類:

image-20250518180858771

在數據庫表中user_pic應該為圖片,但為什么數據類型為varchar,因為將來將圖片放在第三方服務器上,這里存放的是一個URL地址。

開發接口流程:

首先我們要明確一下需求,需要知道該功能將來用戶是如何使用的,

接下來需要去閱讀接口文檔明確該接口的輸入以及輸出

接著要進行思路分析,分析將來如何寫代碼邏輯,再接著就是開發寫代碼,代碼寫完之后需要測試接口的正確性

image-20250518182141970

在寫注冊接口時,依然是按照三層架構的方式書寫:

Controller層:

@Controller
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;@PostMapping("/register")public Result register(String username,String password){//查詢用戶User user = userService.findByUsername(username);if(user != null){return Result.error("用戶已存在");}else {userService.Register(username,password);return Result.success();}}
}

Service層:

//根據用戶名查詢用戶User findByUsername(String username);
//注冊void Register(User user);

實現類:

@Service
public class UserServiceImpl implements UserService {@Autowiredprivate UserMapper userMapper;@Overridepublic User findByUsername(String username) {return userMapper.findByUsername(username);}@Overridepublic void Register(String username,String password) {//加密password = MD5Util.MD5Lower(password);userMapper.addUser(username,password);}
}

Mapper層:

@Mapper
public interface UserMapper {@Select("select * from user where username = #{username}")User findByUsername(String username);@Insert("insert into user(username,password,create_time,update_time) values(#{username},#{password},now(),now())")void addUser(String username, String password);
}

執行:發現錯誤,無法掃描到mapperBean對象,檢查得知,沒有添加MyBatis-spring的starter,導入之后可正常啟動。

接口測試:

image-20250526141928823

檢查數據庫數據

image-20250526141950512

測試成功。

注冊接口參數校驗

我們在接口文檔中對username和password有明確的說明,這兩個參數必須是5-16位的非空字符,那接口就必須保證,如果前端傳遞的參數不符合規則,就不能夠完成注冊。而在我們之前的代碼中并沒有對username與password的校驗,就會導致無論什么樣的數據都會被無差別傳入數據庫。

所以我們需要對參數做校驗

?@RestController@RequestMapping("/user")public class UserController {@Resourceprivate UserService userService;@PostMapping("/register")public Result register(String username,String password){//檢驗用戶名與密碼必須滿足5到16位且不能為空if(username.length() < 5 || username.length() > 16 || password.length() < 5 || password.length() > 16){return Result.error("用戶名或密碼長度不符合要求");}else {User user = userService.findByUsername(username);if(user != null){return Result.error("用戶已存在");}else {userService.Register(username,password);return Result.success();}}?}}

接口測試得:

image-20250526163823949

證明代碼執行成功。

但是該業務代碼太過繁瑣,且在只有兩個參數的情況下,就已經如此,在訪問量高的情況下,更不用想。

解決方案:

Spring Validation

spring 提供的一個參數校驗框架,是用預定義的注解完成參數校驗。

案例要求:使用spring Validation 對注冊接口的參數進行合法性校驗。

使用步驟:

  • 引入Spring Validation 起步依賴

  • 在參數前面添加@Pattern注解

  • 在Controller類上添加@Validated

代碼展示:

pom.xml

?<!-- ? ? ? ?validation依賴--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency>

UserController

?
@RestController@RequestMapping("/user")@Validatedpublic class UserController {@Resourceprivate UserService userService;@PostMapping("/register")public Result register(@Pattern(regexp = "^\\S{5,16}$") String username, @Pattern(regexp = "^\\S{5,16}$") String password){//檢驗用戶名與密碼必須滿足5到16位且不能為空if(username.length() < 5 || username.length() > 16 || password.length() < 5 || password.length() > 16){return Result.error("用戶名或密碼長度不符合要求");}else {User user = userService.findByUsername(username);if(user != null){return Result.error("用戶已存在");}else {userService.Register(username,password);return Result.success();}}?}}

測試接口結果:

image-20250526170051144

發現500異常,在idea內部也報錯

使用全局異常處理器進行參數校驗失敗異常處理,

代碼展示:

?//  全局異常處理類@RestControllerAdvicepublic class GlobalExceptionHandler {// 捕獲所有異常@ExceptionHandler(Exception.class)// 返回值類型為Result,因為全局異常處理類返回值類型為Resultpublic Result handleException(Exception e){e.printStackTrace();// 三元運算符 :如果e.getMessage()不為空,則返回e.getMessage(),否則返回"服務器異常"return Result.error(StringUtils.hasLength(e.getMessage())? e.getMessage() : "服務器異常");}}
??

效果展示:

image-20250526171306285

小結:

Spring Validation

  • 導入 validation 起步依賴

  • 在參數上添加@Pattern注解,指定校驗規則

  • 在Controller類上添加@Validated注解

  • 在全局異常處理器中處理參數校驗注解失敗的異常

登錄

登錄需求:

用戶在登錄界面輸入用戶名還有密碼,點擊登錄按鈕,訪問后臺的登錄接口,如果登錄成功,要跳轉到首頁,登錄失敗則會給出對應的提示。

根據接口文檔

分析實現思路

首先在UserController中添加方法。

?@PostMapping("/login")public Result<String> login(@Pattern(regexp = "^\\S{5,16}$") String username, @Pattern(regexp = "^\\S{5,16}$") String password){//根據用戶名查詢用戶User user = userService.findByUsername(username);//判斷用戶是否存在if (user == null) {return Result.error("用戶不存在");}//  判斷密碼是否正確if (MD5Util.MD5Lower(password).equals(user.getPassword())){return Result.success("登錄成功 JWT 令牌");}return Result.error("密碼錯誤");?}}

測試成功

image-20250526175754823

登錄認證

在實際項目中,會有許多接口,在提供服務之前需要對用戶的登錄狀態進行檢查,檢查的過程被稱為登錄認證。

而在當前項目中并沒有登錄認證,因此我們要為項目添加登錄驗證。

如果要實現登錄認證,要借助于令牌的技術,即我們在登錄頁面登錄成功之后,需要生成一個JWT令牌,再將令牌響應給瀏覽器,瀏覽器在訪問其它接口或資源是都需要攜帶該令牌,則服務器能夠得到該令牌,還需要去驗證令牌的合法性,如果令牌合法,則正常提供服務,如果不是,則不提供服務。

令牌就是一段字符串

  • 承載業務數據,減少后續請求查詢數據庫的次數(系統需要得知本次操作是哪個用戶操作的,方便以后回收,如果每次都需要到數據庫去查詢用戶信息,則會極大影響性能,因此可以將用戶信息放在令牌中,而瀏覽器每次訪問接口都會攜帶令牌,就可以直接從令牌中獲取,由此減少了數據庫的訪問次數,提高了性能)

  • 防止篡改,保證信息的合法性與有效性。

而在實際開發中,我們所用到的常用令牌規范為JWT令牌規范

JWT
簡介

全稱:JSON Web Token(官網地址:JSON Web Tokens - jwt.io)

定義了一種簡潔的、自包含的格式,用于通信雙方以JSON數據格式安全的傳輸信息。

組成:

  • 第一部分:Header(頭),記錄令牌類型、簽名算法等。例如{"alg":"HS256","type":"JWT"},算法用于防篡改

  • 第二部分:Payload(有效載荷),攜帶一些自定義信息,默認信息。例如 {"id":"1","username":"Tom"} 。而外在表示為一段無規律的64個可打印字符

原因:借助base64編碼方式(Base64:是一種基于64個可打印字符(A-Z a-z 0-9 + /)來表示二進制數據的編碼方式)來完成,將json字符串變為64個可打印字符,

優點:提高token的實用性:假如JSON字符串中含有空格、中文等特殊字符,cookie就不能支持,因此在JWT中將JS字符串通過base64編碼轉換。

注意事項:Base64僅僅是編碼方式,不是加密方式,且編碼方式是公開的,因此安全性不高,所以在Payload中不要存放私密數據。

(Base64特點:通用,在任意場景下都被支持)

  • 第三部分:Signature(簽名),防止token被篡改,確保安全性。將header,payload,借助于密鑰以及加密算法通過加密得來的。

加密算法是通過第一部分的alg指定,而密鑰可以在程序中單獨的配置。

主要作用:防篡改,保證token的安全性,即使篡改了前兩部分,也無法篡改第三部分,因為是加密后的字符串,之后JWT解析token令牌時,會通過解密第三部分數字簽名從而得到頭部與載荷,在拿解密的內容與用戶傳遞的內容作對比,如果不一樣,則說明數據被篡改過,就會禁止訪問。

注意事項:JWT就是生成token的一種規則,包含了token的生成,校驗,是一種約定俗成。

生成令牌

JWT是令牌的規范,我們可以自己手動書寫,也可以借助生成JWT令牌的工具。

生成步驟:

導入依賴:

<!--        JWT依賴--><dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>4.4.0</version></dependency>

生成令牌(大多數實際開發直接使用工具包生成即可,在這里手動編譯是加強記憶):

public class JwtTest {@Testpublic void test() {Map<String, Object> claims = new HashMap<>();claims.put("userId",1);claims.put("username","lyc");//生成JWT的代碼String token = JWT.create().withClaim("user", claims)//添加載荷.withExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 12))//設置過期時間.sign(Algorithm.HMAC256("lyc"));//指定簽名算法System.out.println(token);}
}

image-20250526185457526

驗證令牌:

@Test
public void testParse(){//定義字符串,模擬從數據庫中取出的JWTString token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" +".eyJ1c2VyIjp7InVzZXJJZCI6MSwidXNlcm5hbWUiOiJseWMifSwiZXhwIjoxNzQ4MzAwMDI4fQ" +".f5LzYorp0U4RBiKt7DzBgYDm_SRanjHPpb1zCMYJszQ";JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("lyc")).build();DecodedJWT decodedJWT = jwtVerifier.verify(token);//解析JWT,生成一個DecodedJWT對象decodedJWT.getClaims().forEach((k,v)->{System.out.println(k + ":" + v.asMap());});}

image-20250526190115278

  • 如果篡改了頭部與載荷,就會拋出異常

  • 如果修改了密鑰,就會拋出異常

  • 如果過期了,就會拋出異常

image-20250526190206145

注意事項:

  • JWT校驗使用的簽名密鑰,必須和生成JWT令牌時使用的密鑰是配套的。

  • 如果JWT令牌解析校驗時報錯,則說明令牌被篡改或失效,令牌非法。

登錄驗證實現

操作流程:在USerController中生成JWT令牌,在其他接口中去驗證該令牌

先編譯一個JWT工具類

??
public class JWTUtil {private static final String SIGNATURE = "lyc";/*** 生成token* @param map //傳入payload* @return 返回token*/public static String getToken(Map<String,Object> map){JWTCreator.Builder builder = JWT.create();builder.withClaim("claims",map);Calendar instance = Calendar.getInstance();instance.add(Calendar.HOUR,12);builder.withExpiresAt(instance.getTime());return builder.sign(Algorithm.HMAC256(SIGNATURE));}//接收token,驗證token,返回業務數據public static Map<String,Object> verifyToken(String token){DecodedJWT verify = JWT.require(Algorithm.HMAC256(SIGNATURE)).build().verify(token);return verify.getClaims().get("claims").asMap();}??}

在userController中的login接口里,新建JWT令牌,

@PostMapping("/login")
public Result<String> login(@Pattern(regexp = "^\\S{5,16}$") String username, @Pattern(regexp = "^\\S{5,16}$") String password){//根據用戶名查詢用戶User user = userService.findByUsername(username);//判斷用戶是否存在if (user == null) {return Result.error("用戶不存在");}//  判斷密碼是否正確if (MD5Util.MD5Lower(password).equals(user.getPassword())){// 生成JWT令牌Map<String, Object> claims = new HashMap<>();claims.put("username", username);claims.put("password", password);String token = JWTUtil.getToken(claims);return Result.success(token);}return Result.error("密碼錯誤");}

測試,看接口是否能接收到token

image-20250526192252523

在ArticleController中驗證令牌

代碼如下:

@RestController
@RequestMapping("/article")
public class ArticleController {@RequestMapping("/list")public Result<String> list(@RequestHeader(name="Authorization") String token, HttpServletResponse response){try {Map<String, Object> claims = JWTUtil.verifyToken(token);return Result.success("文章列表");} catch (Exception e) {response.setStatus(401);return Result.error("未登錄");}}
}

測試結果

image-20250526195028811

在我們實際項目中會有很多的接口需要驗證用戶的登錄狀態,我們不能在每一個接口中書寫業務代碼,所以我們應該使用攔截器來進行驗證令牌,讓所有請求都經過攔截器,在攔截器里統一完成令牌驗證,如果生效則通行,失效則攔截。

代碼如下:

JWTInterceptor:

?public class JWTInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//驗證令牌String token = request.getHeader("Authorization");if (token == null || token.isEmpty()) {response.setStatus(401);return false;}try {Map<String, Object> claims = JWTUtil.verifyToken(token);return true;} catch (Exception e) {response.setStatus(401);return false;}}}

MvcConfig(將攔截器注冊進IOC容器中):

@Configuration
public class MvcConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new JWTInterceptor()).excludePathPatterns("/user/login","/user/register");}
}

測試結果:

不攜帶token

image-20250526213824116

攜帶token

image-20250526213834283

注意事項:

  • 使用攔截器統一驗證令牌

  • 登錄與注冊接口需要放行

獲取用戶詳細信息

明確需求:當用戶登陸成功后需要跳轉到首頁,在首頁頂部導航欄需要展示用戶的昵稱,頭像等,以及個人中心得基本資料,更換頭像,重置密碼等都需要先查詢數據庫來獲取用戶的詳細信息,再去更新。

根據接口文檔分析實現思路

我們還是需要在UserController中聲明userInfo方法。在接口文檔中并沒有攜帶參數,但是瀏覽器中攜帶的token里有用戶的username,因此我們可以取出username,在根據用戶名查詢用戶的詳細信息。

代碼展示:

@GetMapping("/userInfo")
public Result<User> userInfo(@RequestHeader(name = "Authorization") String token){//根據用戶名獲取當前用戶信息Map<String, Object> claims = JWTUtil.verifyToken(token);String username = (String)claims.get("username");User user = userService.findByUsername(username);return Result.success(user);
}

效果展示:

image-20250526221629715

發現問題:在返回的數據中將用戶的密碼也返回,在接口文檔中并沒有返回密碼。

解決方案:在實體類上的密碼屬性添加@JsonIgnore,讓springmvc把當前對象轉換成json字符串時,忽略password,最終的json字符串中就沒有password了。

注意事項:一定要檢查導入的依賴為com.fasterxml.jackson.annotation.JsonIgnore

測試效果:

image-20250526222930683

發現問題:在創建數據庫表示規定createTime以及updateTime非空,而在獲取的信息中卻為空。

原因:因為在實體類中的createTime與updateTime命名為駝峰命名,而數據庫中的命名為下劃線命名,這樣就導致MyBatis無法自動識別,需要在配置文件中設置

mybatis:configuration:map-underscore-to-camel-case: true # 開啟駝峰映射

這時開啟駝峰命名與下劃線命名的相互轉換。

測試結果:

image-20250526223517347

優化獲取用戶信息接口

問題引入:

在userInfo接口中解析前端的接收頭的業務代碼在攔截器中已經寫過一遍,不應該在重復造輪子,我們可以復用在攔截器里解析到的結果。

需要知識:ThreadLocal

ThreadLocal(線程本地存儲)是Java中用于解決線程間數據共享問題的機制。

作用:提供線程局部變量,為每個線程提供獨立的數據副本,防止因共享資源造成的競態條件。

競態條件:

多個線程或者進程沒有正確的同步的訪問共享資源的時候,其執行結果可能依賴于一些不可控的執行順序導致一些不可預測的錯誤行為。簡單來講,就是并發安全性問題。

  • ThreadLocal中用來存取數據(set()/get())

  • TreadLocal可以做到線程隔離。

代碼展示:

 ?@Testpublic void threadLocalTest1() {//  創建ThreadLocal對象ThreadLocal threadLocal = new ThreadLocal();//開啟兩個線程new Thread(()->{threadLocal.set("thread1");System.out.println(Thread.currentThread().getName()+":"+threadLocal.get());},"thread1").start();new Thread(()->{threadLocal.set("thread2");System.out.println(Thread.currentThread().getName()+":"+threadLocal.get());},"thread2").start();}}

如何實現?

首先定義ThreadLocal對象,定義之后再去在對應線程中設置value(set()方法),并且將value保存在每個線程中的獨立存儲數據的空間ThreadLocalMap,ThreadLocal對象會去維護一個ThreadLocalMap對象。

對于項目的作用:

假如項目中的Mapper層,Service層,Controller層都需要傳入username參數,如果還有其他業務代碼要用到username,還需要去傳參,那有沒有一種方式,不需要聲明,也可以使用參數。此時就可以使用ThreadLocal來進行優化,可以維護一個全局的ThreadLocal對象,用來存儲這類經常使用的數據,這時就可以在請求到達攔截器之后,調用Threadlocal對象的set()方法存儲變量,在請求到達接口之后,可以直接定義ThreadLocal對象,在調用get方法調用參數即可。且不會造成相同參數名混淆的情況,因為客戶端在訪問服務端時,服務端會為用戶單獨開啟一條線程用來提供服務,而ThreadLocal為每一個線程都提供了獨立的存儲空間,并且做到了數據隔離。同時做到了統一線程數據共享,不同線程數據隔離。

作用總結:減少參數的傳遞,同一個線程之間共享數據,不同線程之間數據隔離。

代碼優化:

新建ThreadLocal工具類:

?package com.lyc.utils;//  線程工具類public class ThreadUtil {//提供ThreadLocal對象private static final ThreadLocal THREAD_LOCAL = new ThreadLocal();//根據建獲取值//泛型 可以直接強轉public static <T> T get(){return (T) THREAD_LOCAL.get();}//存儲鍵值對public static void set(Object obj){THREAD_LOCAL.set(obj);}//清除Thread Local 防止內存泄露public static void remove(){THREAD_LOCAL.remove();}}

再在攔截器中將參數存入ThreadLocal中。

image-20250527165446398

最后在UserController中調用get方法獲取user參數。

   @GetMapping("/userInfo")public Result<User> userInfo(/*@RequestHeader(name = "Authorization") String token*/){//根據用戶名獲取當前用戶信息Map<String, Object> claims = ThreadUtil.get();String username = (String)claims.get("username");User user = userService.findByUsername(username);return Result.success(user);}
}

測試結果:

image-20250527165220206

注意事項:ThreadLocal的生命周期與線程的生命周期一致,因此不使用時就應該將ThreadLocal對象移除,防止內存泄漏

所以在攔截器中重寫afterCompletion方法,該方法是在請求結束后執行,在方法中移除ThreadLocal對象。

?@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {//清除ThreadLocal中存儲的數據ThreadUtil.remove();}

更新用戶基本信息

明確需求:當用戶在個人中心點擊基本資料后,當前頁面會展示用戶的詳細信息,用戶可以再次修改信息并點擊提交修改按鈕,從而讓訪問后臺的接口更新當前用戶的信息,

注意事項:當前用戶的登錄名稱無法修改。

根據接口文檔分析實現思路

首先在UserController中聲明一個新的方法,使用put請求方式提交,所以需要添加注解@PutMapping,瀏覽器向服務端請求時攜帶的信息是用戶想要修改的信息,并放在了請求體中以json格式攜帶,因此可以將這些數據封裝在user對象接收,為了讓框架能夠自動把請求體里面的JSON數據給轉換成一個實體類對象,因此使用@RequestBody注解。

注意事項:在Mapper層中要注意,SQL修改語句中要將updateTime一并修改,且不能修改username。

代碼展示:

USerController:

?@PutMapping("/update")public Result update(@RequestBody User user){userService.update(user);return Result.success();}

UserServiceImpl:

?@Overridepublic void update(User user) {//獲取當前時間user.setUpdateTime(LocalDateTime.now());userMapper.update(user);?}

測試結果,發現錯誤:

org.springframework.web.servlet.resource.NoResourceFoundException: No static resource user/update.

表示 Spring MVC 在嘗試訪問靜態資源 /user/update 時沒有找到對應的文件。

如果 Spring Boot 錯誤地認為 /user/update 是靜態資源,所以關閉靜態資源處理

在配置文件中 配置spring.web.resources.add-mappings=false。

?web:resources:add-mappings: false

再次測試:

image-20250527175611376

檢查數據庫:

image-20250527175658023

優化:參數校驗

在前面只是將user屬性更新,但是并沒有對屬性進行校驗,

首先ID必須傳遞,不能為null,nickname必須傳遞,不能為null,且必須是1-10位非空字符,以及email也是必須傳遞,不能為null,需要滿足郵箱的格式。

可以繼續使用Spring Validation,如果要對實體參數進行校驗,首先要在實體類的成員變量上添加validation提供的注解,對指定的屬性值完成校驗。

使用注解詳情:

注解作用
@NotNull值不能為null
@NotEmpty值不能為null,且內容不為空
@Email滿足郵箱格式

代碼展示:

?public class User {@NotNullprivate Integer id;// 主鍵IDprivate String username;// 用戶名@JsonIgnore//  讓springmvc把當前對象轉換成json字符串時,忽略password,最終的json字符串中就沒有password了。private String password;// 密碼@NotEmpty@Pattern(regexp = "^\\S{1,10}$")private String nickname;// 昵稱@NotEmpty@Emailprivate String email;// 郵箱private String userPic;//  用戶頭像地址private LocalDateTime createTime;// 創建時間private LocalDateTime updateTime;// 修改時間?}

其次需要在Controller層的方法參數上聲明@Validated注解

? ? ?@PutMapping("/update")public Result update(@RequestBody @Validated User user){userService.update(user);return Result.success();}}

測試結果:

image-20250527181103740

查看數據庫:

image-20250527181142127

當不添加ID時:

image-20250527181350337

小結:實體參數校驗:

  • 實體類的成員變量上添加注解(@NotNUll,@NotEmpty,@Email等)

  • 接口方法的實體參數上添加@Validated注解

更新用戶頭像

明確需求:當用戶在個人中心點擊更換頭像,然后會在頁面的主區域展示當前用戶的頭像,此時用戶可以點擊選擇頭像,選擇一張本地的圖片,再次點擊上傳頭像按鈕,訪問后臺的接口完成頭像更新。

根據接口文檔分析實現思路:

請求方式為patch(因為更新用戶頭像僅僅是更新用戶信息里的局部)

請求參數需要一個URL,頭像地址。

首先需要在UserController中新聲明一個方法,在該方式中需要聲明@PatchMapping注解,且需要從前端中拿到請求體中的參數,需要使用@RequestParam注解。

還需要在Service層以及Mapper層都需要修改。

注意事項:修改SQL語句時依舊要修改updateTime。

代碼實現:

UserController

?@PatchMapping("/updateAvatar")public Result updateAvatar(@RequestParam("avatar") String avatarUrl){userService.updateAvatar(avatarUrl);return Result.success();}

serviceImpl

?@Overridepublic void updateAvatar(String avatarUrl) {Map<String,Object> map = ThreadUtil.get();String username = (String) map.get("username");System.out.println("username = " + username);Integer id = userMapper.findByUsername(username).getId();userMapper.updateAvatar(avatarUrl,id);}

注意事項:這里是在登錄時并沒有存儲user的ID,因此只能拿到username,在通過username拿到user,在通過user拿到userid。

測試結果:

image-20250527185949066

查看數據庫:

image-20250527190026979

優化:參數校驗

此時無論前端傳入任何值都會直接傳入,但是用戶頭像要求為URL格式,因此我們可以調用validation提供的參數校驗注解@URL,直接放在參數上面即可。

代碼展示:

?@PatchMapping("/updateAvatar")public Result updateAvatar(@RequestParam("avatar") @URL String avatarUrl){userService.updateAvatar(avatarUrl);return Result.success();}

測試結果:

image-20250527190434115

更新用戶密碼

明確需求:當用戶在個人中心點擊重置密碼時,會在刷新一個表單,在該表單用戶需要填寫三項內容,原密碼、新密碼、確認新密碼,如果沒問題,點擊修改密碼,最終需要訪問后臺的接口完成密碼的更新。

根據接口文檔分析實現思路

請求方式依舊為patch(更新用戶密碼僅僅是更新用戶對象中的一個字段)。

首先在UserController新聲明一個方法,用于完成密碼的更新,在該方法上需要聲明一個map類型的參數用于接手前端提交的JSON參數

注意:在之前更新用戶基本信息的時候,也接收過JSON參數,當時聲明了一個user實體對象來接受,這是因為當時傳遞的JSON中的鍵名剛好和user實體類的屬性名一樣,但此時接受的JSON中的鍵名以實體類屬性名不一致,需要使用map類型的參數來接受,MVC框架會自動的將JSON數據轉換成map。

依舊是要修改Service層以及Mapper層的業務代碼。

代碼展示:

UserController:

?@PatchMapping("/updatePwd")public Result updatePwd(@RequestBody Map<String,String> params){//校驗參數String OldPwd = params.get("old_pwd");String NewPwd = params.get("new_pwd");String RePwd = params.get("re_pwd");//校驗三個參數是否都被傳入if (!StringUtils.hasLength(OldPwd) || !StringUtils.hasLength(NewPwd) || !StringUtils.hasLength(RePwd)){return Result.error("參數不能為空");}//校驗原密碼是否正確Map<String, Object> claims = ThreadUtil.get();String username = (String)claims.get("username");User user = userService.findByUsername(username);System.out.println(user.getPassword());System.out.println(OldPwd);System.out.println(MD5Util.MD5Lower("123456"));System.out.println(MD5Util.MD5Lower(OldPwd));if (!MD5Util.MD5Lower(OldPwd).equals(user.getPassword())){return Result.error("原密碼錯誤");}if (!NewPwd.equals(RePwd)){return Result.error("新密碼不一致");}userService.updatePwd(NewPwd);//調用service層修改密碼return Result.success();}

UserServiceImpl:

?@Overridepublic void updatePwd(String pwd) {Map<String,Object> map = ThreadUtil.get();String username = (String) map.get("username");System.out.println("username = " + username);Integer id = userMapper.findByUsername(username).getId();userMapper.updatePwd(MD5Util.MD5Lower(pwd),id);}

測試結果:

image-20250527195813001

查看數據庫:

image-20250527195958574

注意事項:由于不可知原因,在測試接口時,參數前面被添加了逗號,因此在校驗原密碼時記得加上逗號。

至此關于用戶模塊的接口開發全部完成。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/82636.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/82636.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/82636.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

html使用JS實現賬號密碼登錄的簡單案例

目錄 案例需求 思路 錯誤案例及問題 修改思路 案例提供 所需要的組件 <input>標簽&#xff0c;<button>標簽&#xff0c;<script>標簽 詳情使用參考&#xff1a;HTML 教程 | 菜鳥教程 案例需求 編寫一個程序&#xff0c;最多允許用戶嘗試登錄 3 次。…

小米玄戒O1架構深度解析(一):十核異構設計與緩存層次詳解

前言 這兩天&#xff0c;小米的全新SOC玄戒O1橫空出世&#xff0c;引發了科技數碼圈的一次小地震&#xff0c;那么小米的這顆所謂的自研SOC&#xff0c;內部究竟有著什么不為人知的秘密呢&#xff1f;我們一起一探究竟。 目錄 前言1 架構總覽1.1 基本構成1.2 SLC缺席的原因探…

VSCode如何像Pycharm一樣“““回車快速生成函數注釋文檔?如何設置文檔的樣式?autoDocstring如何設置自定義模板?

文章目錄 ?? 介紹 ???? 演示環境 ???? 讓VSCode擁有PyCharm級注釋生成能力 ???? 實現方案??? 備用方案?? 自定義注釋文檔格式樣式 ???? 切換主流注釋風格? 深度自定義模板??? 類型提示與注釋聯動優化?? 相關鏈接 ???? 介紹 ?? 用PyCharm寫P…

數據庫的事務(Transaction)

在數據庫中&#xff0c;事務&#xff08;Transaction&#xff09; 是保證數據操作一致性和完整性的核心機制。它通過一組原子性的操作單元&#xff0c;確保所有操作要么全部成功&#xff08;提交&#xff09;&#xff0c;要么全部失敗&#xff08;回滾&#xff09;。以下是數據…

2025-05-27 Python深度學習7——損失函數和反向傳播

文章目錄 1 損失函數1.1 L1Loss1.2 MSELoss1.3 CrossEntropyLoss 2 反向傳播 本文環境&#xff1a; Pycharm 2025.1Python 3.12.9Pytorch 2.6.0cu124 1 損失函數 ? 損失函數 (loss function) 是將隨機事件或其有關隨機變量的取值映射為非負實數以表示該隨機事件的"風險&…

python+tkinter實現GUI界面調用即夢AI文生圖片API接口

背景 目前字節跳動公司提供了即夢AI的接口免費試用&#xff0c;但是并發量只有1&#xff0c;不過足夠我們使用了。我這里想做個使用pythontkinter實現的GUI可視化界面客戶端&#xff0c;這樣就不用每次都登錄官方網站去進行文生圖片&#xff0c;當然文生視頻&#xff0c;或者圖…

#git 儲藏庫意外被清空 Error: bad index – Fatal: index file corrupt

問題&#xff1a;通常是由于 Git 的索引文件損壞導致 原因&#xff1a;系統崩潰或斷電、硬盤故障、Git 操作錯誤等 方案&#xff1a;重建索引文件&#xff1a;將當前的索引文件重命名為其他名稱或刪除&#xff0c;比如 index.m&#xff0c;然后命令行重建索引&#xff0c;git…

GitLab 18.0 正式發布,15.0 將不再受技術支持,須升級【二】

GitLab 是一個全球知名的一體化 DevOps 平臺&#xff0c;很多人都通過私有化部署 GitLab 來進行源代碼托管。極狐GitLab 是 GitLab 在中國的發行版&#xff0c;專門為中國程序員服務。可以一鍵式部署極狐GitLab。 學習極狐GitLab 的相關資料&#xff1a; 極狐GitLab 官網極狐…

車載網關策略 --- 車載網關通信故障處理機制深度解析

我是穿拖鞋的漢子,魔都中堅持長期主義的汽車電子工程師。 老規矩,分享一段喜歡的文字,避免自己成為高知識低文化的工程師: 鈍感力的“鈍”,不是木訥、遲鈍,而是直面困境的韌勁和耐力,是面對外界噪音的通透淡然。 生活中有兩種人,一種人格外在意別人的眼光;另一種人無論…

Unity數字人開發筆記

開源工程地址&#xff1a;https://github.com/zhangliwei7758/unity-AI-Chat-Toolkit 先致敬zhangliwei7758&#xff0c;開放這個源碼 一、建立工程 建立Unity工程&#xff08;UnityAiChat&#xff09;拖入Unity-AI-Chat-Toolkit.unitypackage打開chatSample工程&#xff0c;可…

Cherry Studio連接配置MCP服務器

之前寫了一篇關于Cherry Studio的文章&#xff0c;不了解的可以先看一下 AI工具——Cherry Studio&#xff0c;搭建滿血DeepSeek R1的AI對話客戶端【硅基流動DeepSeek API】-CSDN博客 最近Cherry Studio更新了一個新功能&#xff1a;MCP服務器 在 v1.2.9 版本中&#xff0c;…

OpenSSH 服務配置與會話保活完全指南

一、/etc/ssh/sshd_config 配置機制 1. 配置文件基礎 文件作用 OpenSSH 服務器 (sshd) 的主配置文件&#xff0c;控制連接、認證、端口轉發等行為。 加載與生效 修改后需重啟服務&#xff1a;sudo systemctl restart sshd # Systemd 系統 sudo service ssh restart # S…

阿里云國際版注冊郵箱格式詳解

“為什么我的阿里云國際版注冊總提示郵箱無效&#xff1f;” 這是許多初次接觸阿里云國際版&#xff08;Alibaba Cloud International&#xff09;的用戶常遇到的困惑。隨著全球化進程加速&#xff0c;越來越多的企業選擇阿里云國際版部署海外業務&#xff0c;而注冊環節中郵箱…

【IDEA問題】springboot本地啟動應用報錯:程序包不存在;找不到符號

問題&#xff1a; springboot本地啟動應用報錯&#xff1a; 程序包xxx不存在&#xff1b;找不到符號 解決方案&#xff1a; 1.確保用maven重新導入依賴 2.刪除.idea文件夾 3.invalidate caches里&#xff0c;把能選擇的都勾選上&#xff0c;然后清除緩存重啟 4.再在上方工具欄…

FFmpeg 時間戳回繞處理:保障流媒體時間連續性的核心機制

FFmpeg 時間戳回繞處理&#xff1a;保障流媒體時間連續性的核心機制 一、回繞處理函數 /** * Wrap a given time stamp, if there is an indication for an overflow * * param st stream // 傳入一個指向AVStream結構體的指針&#xff0c;代表流信息 * pa…

【b站計算機拓荒者】【2025】微信小程序開發教程 - chapter3 項目實踐 -1 項目功能描述

1 項目功能描述 # 智慧社區-小程序-1 歡迎頁-加載后端&#xff1a;動態變化-2 首頁-輪播圖&#xff1a;動態-公共欄&#xff1a;動態-信息采集&#xff0c;社區活動&#xff0c;人臉檢測&#xff0c;語音識別&#xff0c;心率檢測&#xff0c;積分商城-3 信息采集頁面-采集人數…

5.27 day 30

知識點回顧&#xff1a; 導入官方庫的三種手段導入自定義庫/模塊的方式導入庫/模塊的核心邏輯&#xff1a;找到根目錄&#xff08;python解釋器的目錄和終端的目錄不一致&#xff09; 作業&#xff1a;自己新建幾個不同路徑文件嘗試下如何導入 一、導入官方庫 我們復盤下學習py…

【GitHub Pages】部署指南

vue項目 編輯你的 vite.config.ts 文件&#xff0c;加上 base 路徑&#xff0c;設置為你的 GitHub 倉庫名 import { defineConfig } from vite import vue from vitejs/plugin-vue// 假設你的倉庫是 https://github.com/your-username/my-vue-app export default defineConfi…

遠程控制技術全面解析:找到適合你的最佳方案

背景&#xff1a;遠程控制為何成為企業核心需求&#xff1f; 隨著企業數字化轉型的推進&#xff0c;遠程控制技術已成為異地辦公和運維的關鍵工具。無論是跨國企業需要高效管理全球設備&#xff0c;還是中小型企業追求經濟高效的解決方案&#xff0c;選擇合適的遠程控制技術&a…

觸覺智能RK3506星閃開發板規格書 型號IDO-EVB3506-V1

產品概述 觸覺智能RK3506星閃開發板&#xff0c;型號IDO-EVB3506-V1采用 Rockchip RK3506&#xff08;三核 Cortex-A7單核Cortex-M0, 主頻最高1.5GHz&#xff09;設計的評估開發板&#xff0c;專為家電顯控、顯示HMI、手持終端、工業IOT網關、工業控制、PLC等領域而設計。內置…