前言
最近在開發(fā)一些http server類型程序,通過spring boot構(gòu)建一些web程序,這些web程序之間通過http進(jìn)行數(shù)據(jù)訪問、共享,如下圖,
假設(shè)現(xiàn)在client發(fā)起一次保存數(shù)據(jù)的請求到server,server可能會返回如下類似的數(shù)據(jù)
1
2
3
4
|
{ "status" : 1 , "message" : "xxxxxx" } |
然后client通過解析json獲得status來判斷當(dāng)前的請求操作是否成功,開發(fā)過程中通過都是這么做的,但是這樣在restful設(shè)計中不怎么好,其實這個status字段的表達(dá)完全可以通過http status
來表示,類似404、500、502這種都有明確的定義并且相互理解、溝通起來也方便。
文章主要記錄一下我是如何在spring boot中實現(xiàn)自定反饋狀態(tài)碼的,以及我找到的三種實現(xiàn)方式。
第一種,使用**@ResponseStatus** 。這是一個注解,可以作用在方法和類上面,如下使用,
在方法上使用方式,
1
2
3
4
5
|
@RequestMapping (value = "/user" , method = RequestMethod.GET) @ResponseStatus (code=HttpStatus.INTERNAL_SERVER_ERROR,reason= "server error" ) public String getUser(){ return "im zhangsan" ; } |
啟動web程序,通過postman訪問http://127.0.0.1:8100/user,會出現(xiàn)下面結(jié)果,
1
2
3
4
5
6
7
|
{ "timestamp" : 1497850427325 , "status" : 500 , "error" : "Internal Server Error" , "message" : "server error" , "path" : "/user" } |
這里我一開始覺得很奇怪,為什么我的getUser方法中沒有錯誤,結(jié)果還是出現(xiàn)了500錯誤?原因就是@ResponseStatus
注解的問題,我后面猜測它會強(qiáng)制的將映射轉(zhuǎn)化成500的狀態(tài)碼。這種應(yīng)用場景我想不太明白在什么地方會用到。
在類中使用方式,
1
2
3
4
|
@ResponseStatus (code=HttpStatus.INTERNAL_SERVER_ERROR,reason= "111" ) public class ServerException extends Exception { } |
這種使用方式就是將自定義異常和狀態(tài)碼結(jié)合在一起,合理使用自定義異常機(jī)制可以最大化的提高程序的健壯性,下面看如何使用,
1
2
3
4
5
6
7
|
@RequestMapping (value = "/user" , method = RequestMethod.GET) public String getUser( @RequestParam String userName) throws ServerException{ if (StringUtils.isEmpty(userName)){ throw new ServerException(); } return "im zhangsan" ; } |
這段代碼的意思是當(dāng)userName字段為null的時候會拋出ServerException異常,但是ServerException類被標(biāo)記了@ResponseStatus
注解,因此會直接報500錯誤,如果覺得500不適合還可以定義其它的錯誤代碼。
這種方式看著已經(jīng)很好了,可以按照邏輯自定義反饋碼,程序夠健壯。這種方式也有不好地方,如果反饋碼太多需要定義太多的異常類,并且錯誤內(nèi)容reason還是不能手動定義。
到這里,我基本上放棄了@ResponseStatus
的使用了。
第二種,使用HttpServletResponse,HttpServletResponse是javax.servlet下的一個接口,如下使用,
1
2
3
4
5
|
@RequestMapping (value = "/user" , method = RequestMethod.GET) public void getUser(HttpServletResponse response) throws IOException{ response.setStatus( 500 ); response.getWriter().append( "server error" ); } |
這種方式可以很好的實現(xiàn)同時滿足自定義反饋碼+消息內(nèi)容,一般的實現(xiàn)方式也都是這樣。但是這樣也不是太好,
- 在括號內(nèi)創(chuàng)建了一個response內(nèi)置變量,這樣顯得不夠美觀,反而有些多余。
-
在方法中調(diào)用了源生的方法來設(shè)置反饋碼和消息體,并且如果需要返回json格式數(shù)據(jù)還需要設(shè)置
response.setContentType("application/json");
和response.setCharacterEncoding("UTF-8");
,這樣做有些多余,重復(fù)的工作太多,雖然可以進(jìn)行封裝。 -
最嚴(yán)重的問題這個方法必須是void類型,否則就會和
@ResponseBody
出現(xiàn)沖突,其次就是不能利用@ResponseBody
自動封裝json的特性,在spring mvc框架中如果在方法上加上@ResponseBody
是可以對返回值自動進(jìn)行json封裝的。
再找找其他的,如果沒有找到,估計也只能接受這個不完美的東西了。
后來在翻閱spring boot文檔的時候找到了ResponseEntity這么一個東西,這就是我要說的第三種方式。
第三種,使用ResponseEntity
不多說,直接上代碼,
1
2
3
4
5
6
|
@RequestMapping (value = "/user" , method = RequestMethod.GET) public ResponseEntity<Map<String,Object>> getUser() throws IOException{ Map<String,Object> map = new HashMap<String,Object>(); map.put( "name" , "zhangsan" ); return new ResponseEntity<Map<String,Object>>(map,HttpStatus.OK); } |
通過postman查看返回結(jié)果,如下,
1
2
3
|
{ "name" : "zhangsan" } |
可以直接將map對象幫我轉(zhuǎn)化成json對象,并且可以獲得自定義狀態(tài)碼,很好,很強(qiáng)大。
這種方式很和我意,
- 不需要多于的HttpServletResponse,看著很干凈。
- 可以充分利用@ResponseBody注解,直接將我的返回值幫我轉(zhuǎn)化成json對象。
- 在設(shè)置返回值的時候同時還可以設(shè)置http反饋碼,HttpStatus是springframework提供的一個枚舉類,里面封裝了所有的http反饋碼,方便使用命名統(tǒng)一,不會有任何歧義。
相比于前面兩種,這種方式很對我胃口。
仔細(xì)看了ResponseEntity的說明,發(fā)現(xiàn)spring mvc其它很多地方也都有使用,如下,下面內(nèi)容摘自org.springframework.http.ResponseEntity
文件注釋,
In RestTemplate, this class is returned by getForEntity() and exchange() :
1
2
3
4
|
ResponseEntity<String> entity = template.getForEntity( "http://example.com" , String. class ); String body = entity.getBody(); MediaType contentType = entity.getHeaders().getContentType(); HttpStatus statusCode = entity.getStatusCode(); |
Can also be used in Spring MVC, as the return value from a @Controller method:
1
2
3
4
5
6
7
8
|
@RequestMapping ( "/handle" ) public ResponseEntity<String> handle() { URI location = ...; HttpHeaders responseHeaders = new HttpHeaders(); responseHeaders.setLocation(location); responseHeaders.set( "MyResponseHeader" , "MyValue" ); return new ResponseEntity<String>( "Hello World" , responseHeaders, HttpStatus.CREATED); } |
這就是上面說過的。
Or, by using a builder accessible via static methods:
1
2
3
4
5
|
@RequestMapping ( "/handle" ) public ResponseEntity<String> handle() { URI location = ...; return ResponseEntity.created(location).header( "MyResponseHeader" , "MyValue" ).body( "Hello World" ); } |
自定義http反饋碼在設(shè)計優(yōu)良的restful api中起到關(guān)鍵作用,http反饋碼是業(yè)內(nèi)統(tǒng)一、共識的,建議在盡量不要通過解析json來獲得status判斷操作結(jié)果。
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對給各位iOS開發(fā)者們能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對服務(wù)器之家的支持。
原文鏈接:http://xiaoqiangge.com/aritcle/1497859085696.html