果圖
1、整體的框架用了表格標簽,可以讓內容排列整齊
2、列表標簽<ul><li>
3、表單標簽
<input type="radio">單選
<input type="checkbox">多選
<input type="text">文本填寫
<textarea>文本框
<select>里面一定要有一個<option>下拉選項
屬性:checked="checked" 默認
在這篇文章中,我們將使用Spring Boot實現一個基本的郵箱注冊賬戶以及驗證的過程。
我們的目標是添加一個完整的注冊過程,允許用戶注冊,驗證,并持久化用戶數據。
首先,我們需要一個DTO來囊括用戶的注冊信息。這個對象應該包含我們在注冊和驗證過程中所需要的基本信息。
例2.1 UserDto的定義
package com.savagegarden.web.dto;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
public class UserDto {
@NotBlank
private String username;
@NotBlank
private String password;
@NotBlank
private String repeatedPassword;
@NotBlank
private String email;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username=username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password=password;
}
public String getRepeatedPassword() {
return repeatedPassword;
}
public void setRepeatedPassword(String repeatedPassword) {
this.repeatedPassword=repeatedPassword;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email=email;
}
}
請注意我們在DTO對象的字段上使用了標準的javax.validation注解——@NotBlank。
@NotBlank、@NotEmpty、@NotNull的區別
@NotNull: 適用于CharSequence, Collection, Map 和 Array 對象,不能是null,但可以是空集(size=0)。
@NotEmpty: 適用于CharSequence, Collection, Map 和 Array 對象,不能是null并且相關對象的size大于0。
@NotBlank: 該注解只能作用于String類型。String非null且去除兩端空白字符后的長度(trimmed length)大于0。
在下面的章節里,我們還將自定義注解來驗證電子郵件地址的格式以及確認二次密碼。
登錄頁面上的注冊鏈接將用戶帶到注冊頁面:
例3.1 RegistrationController的定義
package com.savagegarden.web.controller;
import com.savagegarden.web.dto.UserDto;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class RegistrationController {
@GetMapping("/user/registration")
public String showRegistrationForm(Model model) {
model.addAttribute("user", new UserDto());
return "registration";
}
}
當RegistrationController收到請求/user/registration時,它創建了新的UserDto對象,將其綁定在Model上,并返回了注冊頁面registration.html。
Model 對象負責在控制器Controller和展現數據的視圖View之間傳遞數據。
實際上,放到 Model 屬性中的數據將會復制到 Servlet Response 的屬性中,這樣視圖就能在這里找到它們了。
從廣義上來說,Model 指的是 MVC框架 中的 M,即 Model(模型)。從狹義上講,Model 就是個 key-value 集合。
接下來,讓我們看看控制器在注冊新賬戶時將執行的驗證:
對于簡單的檢查,我們將使用@NotBlank來驗證DTO對象。
為了觸發驗證過程,我們將在Controller中用@Valid注解來驗證對象。
例4.1 registerUserAccount
public ModelAndView registerUserAccount(@ModelAttribute("user") @Valid UserDto userDto,
HttpServletRequest request, Errors errors) {
//...
}
下一步,讓我們驗證電子郵件地址,以保證它的格式是正確的。我們將為此建立一個自定義驗證器,以及一個自定義驗證注解--IsEmailValid。
下面是電子郵件驗證注解IsEmailValid和自定義驗證器EmailValidator:
為什么不使用Hibernate內置的@Email?
因為Hibernate中的@Email會驗證通過XXX@XXX之類的郵箱,其實這是不符合規定的。
感興趣的讀者朋友可以移步此處Hibernate validator: @Email accepts ask@stackoverflow as valid?。
例4.2.1 IsEmailVaild注解的定義
package com.savagegarden.validation;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
@Target({ TYPE, FIELD, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy=EmailValidator.class)
@Documented
public @interface IsEmailVaild {
String message() default "Invalid Email";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
@Target的作用是說明了該注解所修飾的對象范圍
@Retention的作用是說明了被它所注解的注解保留多久
@Constraint的作用是說明自定義注解的方法
@Documented的作用是說明了被這個注解修飾的注解可以被例如javadoc此類的工具文檔化
關于如何自定義一個Java Annotation,感興趣的朋友可以看看我的另一篇文章。
例4.2.2 EmailValidator的定義
package com.savagegarden.validation;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class EmailValidator implements ConstraintValidator<IsEmailVaild, String> {
private static final String EMAIL_PATTERN="^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@" + "[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";
private static final Pattern PATTERN=Pattern.compile(EMAIL_PATTERN);
@Override
public void initialize(IsEmailVaild constraintAnnotation) {
}
@Override
public boolean isValid(final String username, final ConstraintValidatorContext context) {
return (validateEmail(username));
}
private boolean validateEmail(final String email) {
Matcher matcher=PATTERN.matcher(email);
return matcher.matches();
}
}
現在讓我們在我們的UserDto實現上使用新注解。
@NotBlank
@IsEmailVaild
private String email;
我們還需要一個自定義注解和驗證器,以確保UserDto中的password和repeatedPassword字段相匹配。
例4.3.1 IsPasswordMatching注解的定義
package com.savagegarden.validation;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
@Target({ TYPE, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy=PasswordMatchingValidator.class)
@Documented
public @interface IsPasswordMatching {
String message() default "Passwords don't match";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
請注意,@Target注解表明這是一個Type級別的注解。這是因為我們需要整個UserDto對象來執行驗證。
例4.3.2 PasswordMatchingValidator的定義
package com.savagegarden.validation;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import com.savagegarden.web.dto.UserDto;
public class PasswordMatchingValidator implements ConstraintValidator<IsPasswordMatching, Object> {
@Override
public void initialize(final IsPasswordMatching constraintAnnotation) {
//
}
@Override
public boolean isValid(final Object obj, final ConstraintValidatorContext context) {
final UserDto user=(UserDto) obj;
return user.getPassword().equals(user.getRepeatedPassword());
}
}
現在,將@IsPasswordMatching注解應用到我們的UserDto對象。
@IsPasswordMatching
public class UserDto {
//...
}
我們要實現的第四個檢查是驗證該電子郵件帳戶在數據庫中是否已經存在。
這是在表單被驗證后進行的,我們把這項驗證放在了UserService。
例4.4.1 UserService
package com.savagegarden.service.impl;
import com.savagegarden.error.user.UserExistException;
import com.savagegarden.persistence.dao.UserRepository;
import com.savagegarden.persistence.model.User;
import com.savagegarden.service.IUserService;
import com.savagegarden.web.dto.UserDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
@Service
@Transactional
public class UserService implements IUserService {
@Autowired
private UserRepository userRepository;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public User registerNewUserAccount(UserDto userDto) throws UserExistException {
if (hasEmailExisted(userDto.getEmail())) {
throw new UserExistException("The email has already existed: "
+ userDto.getEmail());
}
User user=new User();
user.setUsername(userDto.getUsername());
user.setPassword(passwordEncoder.encode(userDto.getPassword()));
user.setEmail(userDto.getEmail());
return userRepository.save(user);
}
private boolean hasEmailExisted(String email) {
return userRepository.findByEmail(email) !=null;
}
}
使用@Transactional開啟事務注解,至于為什么@Transactional加在Service層而不是DAO層?
如果我們的事務注解@Transactional加在DAO層,那么只要做增刪改,就要提交一次事務,那么事務的特性就發揮不出來,尤其是事務的一致性。當出現并發問題的時候,用戶從數據庫查到的數據都會有所偏差。
一般的時候,我們的Service層可以調用多個DAO層,我們只需要在Service層加一個事務注解@Transactional,這樣我們就可以一個事務處理多個請求,事務的特性也會充分地發揮出來。
UserService依靠UserRepository類來檢查數據庫中是否已存在擁有相同郵箱的用戶賬戶。當然在本文中我們不會涉及到UserRepository的實現。
然后我們繼續實現RegistrationController中的持久化邏輯。
@PostMapping("/user/registration")
public ModelAndView registerUserAccount(
@ModelAttribute("user") @Valid UserDto userDto,
HttpServletRequest request,
Errors errors) {
try {
User registered=userService.registerNewUserAccount(userDto);
} catch (UserExistException uaeEx) {
ModelAndView mav=new ModelAndView();
mav.addObject("message", "An account for that username/email already exists.");
return mav;
}
return new ModelAndView("successRegister", "user", userDto);
}
在上面的代碼中我們可以發現:
1、我們創建了ModelAndView對象,該對象既可以保存數據也可以返回一個View。
常見的ModelAndView的三種用法
(1) new ModelAndView(String viewName, String attributeName, Object attributeValue);
(2) mav.setViewName(String viewName);
mav.addObejct(String attributeName, Object attributeValue);
(3) new ModelAndView(String viewName);
2、在注冊的過程中如果產生任何報錯,將會返回到注冊頁面。
在本節內容中,我們將實現一個自定義的UserDetailsService,從持久層檢查登錄的憑證。
讓我們從自定義UserDetailsService開始。
例6.1.1 MyUserDetailsService
@Service
@Transactional
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
User user=userRepository.findByEmail(email);
if (user==null) {
throw new UsernameNotFoundException("No user found with username: " + email);
}
boolean enabled=true;
boolean accountNonExpired=true;
boolean credentialsNonExpired=true;
boolean accountNonLocked=true;
return new org.springframework.security.core.userdetails.User(
user.getEmail(), user.getPassword().toLowerCase(), enabled, accountNonExpired,
credentialsNonExpired, accountNonLocked, getAuthorities(user.getRoles()));
}
private static List<GrantedAuthority> getAuthorities (List<String> roles) {
List<GrantedAuthority> authorities=new ArrayList<>();
for (String role : roles) {
authorities.add(new SimpleGrantedAuthority(role));
}
return authorities;
}
}
然后,為了真正地能夠開啟自定義的MyUserDetailsService,我們還需要在SecurityConfig配置文件中加入以下代碼:
@Override
protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider());
}
限于篇幅,我們就不在這里詳細展開SecurityConfig配置文件。至此我們完成了一個由Spring Boot實現的基本的用戶注冊過程。
作者:翊君
鏈接:https://juejin.cn/post/7051279571341017101
來源:稀土掘金
讀提示:本文純干貨,無任何廢話,SDS申請步驟需反復閱讀,建議現在收藏本文章,避免日后迷路!
根據IRCC描述,SDS(Student Direct Stream)是申請加拿大學習簽證快速通道,主要針對包括中國在內的一些發展中國家的學生,要求本人遞交申請時需要在國內,且錄取學校在DLI列表中(Designated learning institutions官方認可的學校)。SDS獲批率為75%-90%,申請費用150CAD。
以下條件必須全部滿足:
(1).申請人必須在中國境內遞交申請
(2).持有DLI列表中院校的offer
(3).繳納首年學費證明
(4).GIC證明(至少1萬CAD GIC存款證明)
(5).無犯罪記錄證明
(6).最近的中學或大學成績單
(7).滿足語言成績(雅思6.0分、托福ibt 83分、CAEL60分、PTE Academic 60分以及法語TEF和TCF考試成績)
點擊查看官網原文IRCC Student Direct Stream
*以下所有材料均提供pdf電子版
加拿大DLI院校出具的正式offer,提交PDF電子版。點擊查詢DLI學校列表。
如果錄取院校不在列表中,則不能申請SDS快速通道,只能申請普通學簽。
第一年學費的繳納證明可選以下方式之一,提交英文PDF電子版。
如果學校未要求預先繳納學費,可以發郵件聯系學校說明辦理SDS簽證需要提前繳納首年學費,大部分學校都會接受提前繳納學費,并提供繳費證明。通常繳費信息都會在學校網站登錄學生賬戶后進行查看和下載。
GIC(Guaranteed Investment Certificate)銀行開具1萬+的一年期存款證明,SDS支持多家銀行的GIC證明,推薦加拿大五大行GIC。一方面是因為入境加拿大后需要開本地銀行卡和信用卡,融入本地生活積累信用。另一方面是因為這些銀行在加拿大網點眾多,辦理業務非常方便。
根據個人經驗,推薦辦理加拿大五大行的CIBC(加拿大帝國商業銀行)的GIC。CIBC辦理GIC的費用和其它銀行相當,但是處理速度很快,快點的話7天就能辦好。CIBC在加拿大可以預約中文柜員和在線客服,網點分布非常廣泛,涵蓋絕大多數偏遠地區。
這是我寫的GIC辦理流程,點擊查看(GIC辦理流程)。
申請加拿大學簽需要進行體檢,體檢中心可以點擊此處查看IRCC認可的中國地區體檢中心,我是在北京,選擇的崇文門體檢中心,費用是1670。體檢結束后體檢中心會打印幾張蓋章的回執,將回執掃描為電子版PDF。
這是我寫的體檢流程,點擊查看(體檢流程)。
如果是高中畢業申請本科/大專,需要提交高中成績單。
如果是專科、本科或碩博畢業,需要提交所有大學期間的成績單,不需要高中成績單。
成績單需提供中英雙版,如果沒有英文版,可以TB或PDD搜索蓋章翻譯。蓋章翻譯后合并為一個PDF文件。
建議一并提供畢業證學位證的中英文版,推薦提供WES電子報告。WES申請:World Education Services: International Credential Evaluation for U.S.World Education Services: International Credential Evaluation for U.S.
上傳符合SDS最低要求的英語測試成績單
最低要求:雅思6.0分、托福ibt 83分、CAEL60分、PTE Academic 60分以及法語TEF和TCF考試成績
以雅思為例,上傳雅思成績單帶個人照片頁的pdf掃描電子版。
在戶籍所在地或工作地派出所開具近10年或18歲后的無犯罪記錄證明,開具后需辦理公證。然后掃描無犯罪記錄證明原件和公正件,合并成PDF電子版。
大部分地區都能網上申請辦理,以北京天津為例,可以在京通小程序、津心辦APP在線申請,最快當天就能獲取電子版無犯罪記錄證明。
提交時需要提交本人公民身份材料,強烈建議辦理出生公證、戶口本公證和身份證公證。如果沒有《出生醫學證明》需聯系戶籍所在地開具出生證明,均需公證處公證。將原件掃描和公正書掃描件合并成一個pdf文件提交。
如果公證辦理有任何疑問,可以找我索要我當時的官方辦理機構,費用低、要求低。
涉及畢業后畢業工簽或后期申請加拿大永居,或者涉及配偶工簽或父母未來辦理探親簽等情況,辦理以上公證后可以省去后期再跨國辦理的麻煩,也為未來其它申請辦理鋪好路。
掃描個人新舊護照掃描件,需掃描首頁以及所有蓋章或簽證頁,合并成pdf電子版,無需翻譯。
可以選擇周邊打印店或快照店拍照,拍照時告知加拿大簽證照,一般都會知道簽證照拍攝要求。
照片尺寸至少35x45mm,純白背景,近6個月內拍照。
以下材料如有建議盡可能一并提供:(需蓋章翻譯/公證)
增加資金證明可以提供:存款證明、工資流水、納稅證明、社保證明、房產證明
提升回國約束力可以提供:結婚證公證、在職證明
個人申請加拿大簽證首先需要在IRCC官網注冊申請個人賬號,用于管理自己的申請,官網申請入口:https://www.canada.ca/en/immigration-refugees-citizenship/services/application/account.html
注冊GCKey請參考我另一篇文章。
點贊+關注,查看后續...
*請認真填寫需求信息,我們會在24小時內與您取得聯系。