概念
一種軟件架構風格、設計風格,而不是標准,只是提供了一組設計原則和約束條件。它主要用於客戶端和服務器交互類的軟件。基於這個風格設計的軟件可以更簡潔,更有層次,更易於實現緩存等機制。
URL定義
資源:互聯網所有的事物都可以被抽象為資源
資源操作:使用POST、DELETE、PUT、GET,使用不同方法對資源進行操作。
分別對應 添加、 刪除、修改、查詢。
傳統方式操作資源
http://127.0.0.1/item/queryUser.action?id=1 查詢,GET
http://127.0.0.1/item/saveUser.action 新增,POST
http://127.0.0.1/item/updateUser.action 更新,POST
http://127.0.0.1/item/deleteUser.action?id=1 刪除,GET或POST
請求方式
可以通過 GET、 POST、 PUT、 PATCH、 DELETE 等方式對服務端的資源進行操作。其中,GET 用於查詢資源,POST 用於創建資源,PUT 用於更新服務端的資源的全部信息,PATCH 用於更新服務端的資源的部分信息,DELETE 用於刪除服務端的資源。
這里使用“用戶”的案例進行回顧通過 GET、 POST、 PUT、 PATCH、 DELETE 等方式對服務端的資源進行操作。
SpringBoot測試
測試代碼:
@RequestMapping(value = "/user",method = RequestMethod.GET)
public String getUser(){
return "GET-張三";
}
@RequestMapping(value = "/user",method = RequestMethod.POST)
public String saveUser(){
return "POST-張三";
}
@RequestMapping(value = "/user",method = RequestMethod.PUT)
public String putUser(){
return "PUT-張三";
}
@RequestMapping(value = "/user",method = RequestMethod.DELETE)
public String deleteUser(){
return "DELETE-張三";
}
首先html中只能發送GET后者POST,如果要實現REST風格的話,需要把html修改成下面的這種
<form action="/user" method="get">
<input value="REST-GET 提交" type="submit"/>
</form>
<form action="/user" method="post">
<input value="REST-POST 提交" type="submit"/>
</form>
<form action="/user" method="post">
<input name="_method" type="hidden" value="delete"/>
<input name="_m" type="hidden" value="delete"/>
<input value="REST-DELETE 提交" type="submit"/>
</form>
<form action="/user" method="post">
<input name="_method" type="hidden" value="PUT"/>
<input value="REST-PUT 提交" type="submit"/>
</form>
但是光改成這樣,不能生效,因為在自動配置類中,默認是關閉的,需要手動開啟。
spring:
mvc:
hiddenmethod:
filter:
enabled: true
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
HttpServletRequest requestToUse = request;
//這里先判斷請求是否是POST,而且沒有異常
if ("POST".equals(request.getMethod()) && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null) {
//methodParam 就是_method
String paramValue = request.getParameter(this.methodParam);
if (StringUtils.hasLength(paramValue)) {
String method = paramValue.toUpperCase(Locale.ENGLISH);
//兼容以下請求;**PUT**.**DELETE**.**PATCH**
if (ALLOWED_METHODS.contains(method)) {
//原生request(post),包裝模式requesWrapper重寫了getMethod方法,返回的是傳入的值
//過濾器鏈放行的時候用wrapper。以后的方法調用getMethod是調用****requesWrapper的。
requestToUse = new HttpMethodRequestWrapper(request, method);
}
}
}
filterChain.doFilter(requestToUse, response);
}
源碼
畫了一個簡易的流程圖,方便結合下面的代碼一起看。
- 首先請求都會經過mvc的DispatcherServlet的doDispatch方法。
其中這里的request方法,里面包含了各種請求信息。
- 然后進去到這個getHandler方法。
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//這里的是,List<HandlerMapping>,具體見下圖
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
//這里會for循環變量各個請求映射器,看那個可以處理請求。
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
- 然后進去到 AbstractHandlerMapping的getHandler方法中。
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 這里就是返回的HandlerMethod,具體信息如下圖
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (logger.isTraceEnabled()) {
logger.trace("Mapped to " + handler);
}
else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
logger.debug("Mapped to " + executionChain.getHandler());
}
if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
CorsConfiguration config = getCorsConfiguration(handler, request);
if (getCorsConfigurationSource() != null) {
CorsConfiguration globalConfig = getCorsConfigurationSource().getCorsConfiguration(request);
config = (globalConfig != null ? globalConfig.combine(config) : config);
}
if (config != null) {
config.validateAllowCredentials();
}
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
@Override
protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
//lookupPath 就是請求的路徑 /user
String lookupPath = initLookupPath(request);
//這里獲取一個讀鎖
this.mappingRegistry.acquireReadLock();
try {
HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
return (handlerMethod != null ? handlerMethod.createWithResolvedBean() : null);
}
finally {
//釋放鎖
this.mappingRegistry.releaseReadLock();
}
}
@Nullable
protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
//注,這里的Match里面保存了Mapping和HandlerMethod
List<Match> matches = new ArrayList<>();
//這里根據請求的路徑,找到能匹配的路徑,見下圖可以找到4個
List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
if (directPathMatches != null) {
addMatchingMappings(directPathMatches, matches, request);
}
if (matches.isEmpty()) {
addMatchingMappings(this.mappingRegistry.getRegistrations().keySet(), matches, request);
}
if (!matches.isEmpty()) {
Match bestMatch = matches.get(0);
if (matches.size() > 1) {
Comparator<Match> comparator = new MatchComparator(getMappingComparator(request));
matches.sort(comparator);
bestMatch = matches.get(0);
if (logger.isTraceEnabled()) {
logger.trace(matches.size() + " matching mappings: " + matches);
}
if (CorsUtils.isPreFlightRequest(request)) {
return PREFLIGHT_AMBIGUOUS_MATCH;
}
Match secondBestMatch = matches.get(1);
if (comparator.compare(bestMatch, secondBestMatch) == 0) {
Method m1 = bestMatch.handlerMethod.getMethod();
Method m2 = secondBestMatch.handlerMethod.getMethod();
String uri = request.getRequestURI();
throw new IllegalStateException(
"Ambiguous handler methods mapped for '" + uri + "': {" + m1 + ", " + m2 + "}");
}
}
request.setAttribute(BEST_MATCHING_HANDLER_ATTRIBUTE, bestMatch.handlerMethod);
handleMatch(bestMatch.mapping, lookupPath, request);
return bestMatch.handlerMethod;
}
else {
return handleNoMatch(this.mappingRegistry.getRegistrations().keySet(), lookupPath, request);
}
}
private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
for (T mapping : mappings) {
//這里對那4個請求進行判斷。
T match = getMatchingMapping(mapping, request);
if (match != null) {
matches.add(new Match(match,
this.mappingRegistry.getRegistrations().get(mapping).getHandlerMethod()));
}
}
}
然后找到匹配的請求后,會封裝成一個Match,具體信息如下:
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
//這步,會把方法放到攔截器當中。
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
//這里會遍歷所有的攔截器
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if (mappedInterceptor.matches(request)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
chain.addInterceptor(interceptor);
}
}
return chain;
}
這里先分析到請求進來,然后找到合適的處理器映射器,再到綁定攔截器,下面的流程后面在分析。