前言
最近在开发一些http server类型程序,通过spring boot构建一些web程序,这些web程序之间通过http进行数据访问、共享,如下图,
假设现在client发起一次保存数据的请求到server,server可能会返回如下类似的数据
1
2
3
4
|
{ "status" : 1 , "message" : "xxxxxx" } |
然后client通过解析json获得status来判断当前的请求操作是否成功,开发过程中通过都是这么做的,但是这样在restful设计中不怎么好,其实这个status字段的表达完全可以通过http status
来表示,类似404、500、502这种都有明确的定义并且相互理解、沟通起来也方便。
文章主要记录一下我是如何在spring boot中实现自定反馈状态码的,以及我找到的三种实现方式。
第一种,使用**@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,会出现下面结果,
1
2
3
4
5
6
7
|
{ "timestamp" : 1497850427325 , "status" : 500 , "error" : "Internal Server Error" , "message" : "server error" , "path" : "/user" } |
这里我一开始觉得很奇怪,为什么我的getUser方法中没有错误,结果还是出现了500错误?原因就是@ResponseStatus
注解的问题,我后面猜测它会强制的将映射转化成500的状态码。这种应用场景我想不太明白在什么地方会用到。
在类中使用方式,
1
2
3
4
|
@ResponseStatus (code=HttpStatus.INTERNAL_SERVER_ERROR,reason= "111" ) public class ServerException extends Exception { } |
这种使用方式就是将自定义异常和状态码结合在一起,合理使用自定义异常机制可以最大化的提高程序的健壮性,下面看如何使用,
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" ; } |
这段代码的意思是当userName字段为null的时候会抛出ServerException异常,但是ServerException类被标记了@ResponseStatus
注解,因此会直接报500错误,如果觉得500不适合还可以定义其它的错误代码。
这种方式看着已经很好了,可以按照逻辑自定义反馈码,程序够健壮。这种方式也有不好地方,如果反馈码太多需要定义太多的异常类,并且错误内容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" ); } |
这种方式可以很好的实现同时满足自定义反馈码+消息内容,一般的实现方式也都是这样。但是这样也不是太好,
- 在括号内创建了一个response内置变量,这样显得不够美观,反而有些多余。
-
在方法中调用了源生的方法来设置反馈码和消息体,并且如果需要返回json格式数据还需要设置
response.setContentType("application/json");
和response.setCharacterEncoding("UTF-8");
,这样做有些多余,重复的工作太多,虽然可以进行封装。 -
最严重的问题这个方法必须是void类型,否则就会和
@ResponseBody
出现冲突,其次就是不能利用@ResponseBody
自动封装json的特性,在spring mvc框架中如果在方法上加上@ResponseBody
是可以对返回值自动进行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查看返回结果,如下,
1
2
3
|
{ "name" : "zhangsan" } |
可以直接将map对象帮我转化成json对象,并且可以获得自定义状态码,很好,很强大。
这种方式很和我意,
- 不需要多于的HttpServletResponse,看着很干净。
- 可以充分利用@ResponseBody注解,直接将我的返回值帮我转化成json对象。
- 在设置返回值的时候同时还可以设置http反馈码,HttpStatus是springframework提供的一个枚举类,里面封装了所有的http反馈码,方便使用命名统一,不会有任何歧义。
相比于前面两种,这种方式很对我胃口。
仔细看了ResponseEntity的说明,发现spring mvc其它很多地方也都有使用,如下,下面内容摘自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反馈码在设计优良的restful api中起到关键作用,http反馈码是业内统一、共识的,建议在尽量不要通过解析json来获得status判断操作结果。
总结
以上就是这篇文章的全部内容了,希望本文的内容对给各位iOS开发者们能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对服务器之家的支持。
原文链接:http://xiaoqiangge.com/aritcle/1497859085696.html