zoukankan      html  css  js  c++  java
  • JAX-RS REST 服务结果的自动封装

    如转发请注明: 原文luyiisme博客

    当使用遵循 JAX-RS 标准的框架开发REST 服务时,我们倾向于定义个(含有JAX-RS)注解接口。 服务器端负责实现该接口,而客户端是该接口的代理进行远程调用,从而简化开发。比如,下面是个具体的 REST API 接口的例子:

    @Path("/movies")
    public interface ServicesInterface {
    
        @GET
        @Path("/getinfo")
        @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
        Movie movieByImdbId(@QueryParam("imdbId") String imdbId);
    

    Movie 对象是个典型的资源。

    但是如果客户端不支持JAX-RS(比如,不是JAVA语言),尤其浏览器端请求的情况下,一般都需要有固定的JSON/XML的数据结构,比如,响应体的数据结构:

    //失败
    {"error":{"message":"xxxx","code":500},"success":false}
    //成功
    {"data":{"name":"jack"},"success":true}
    

    但这个需求,就会使得我们给方法返回类型加个封装类:

    ...
      Result<Movie> movieByImdbId(@QueryParam("imdbId") String imdbId);
    

    这样可以满足需求,但如果你不希望封装类型侵入的到接口方法中呢(处于简洁或者不想做“打包”工作)? 下面提供一个解决方案。

    1.如果通过JAX-RS 客户端访问结果无封装,而其他客户端访问默认对结果进行封装;

    • 首先,在服务器端(服务实现端)声明一个加封装类型的拦截器:
    @Provider
    public class WrapResultJaxrsFilter implements ContainerRequestFilter, WriterInterceptor, ContainerResponseFilter {
    
        @Override
        public void filter(ContainerRequestContext requestContext) throws IOException {
            String wrValue = requestContext.getHeaderString("Not-Wrap-Result");
            if (StringUtils.isEmpty(wrValue)) {
                //need wrap result
                requestContext.setProperty("wrap-result", "T");
            }
        }
    
        @Override
        public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
            //省略 @ResultWrapper 的自定义注解处理逻辑;
    
            Object originalObj = context.getEntity();
            if (context.getProperty("wrap-result") != null && !(originalObj instanceof Result)) {
                context.setEntity(Result.ok().data(originalObj));
                context.setType(Result.ok().getClass());
            }
            context.proceed();
        }
    
        /**
         * wrapper http status 204 to 404
         */
        @Override
        public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
          if (requestContext.getProperty("wrap-result") != null&&responseContext.getStatus() == 204 && !responseContext.hasEntity()) {
                responseContext.setStatus(404);
                responseContext.setEntity(Result.error().code(404).message("Resource Not Found!"));
                responseContext.getHeaders().add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
            }
        }
    }
    

    第一个方法是实现 “ContainerRequestFilter” 接口,用来判断请求头里是否暗示不要对响应结果进行封装;

    第二个方法是实现 “WriterInterceptor”接口,针对需要封装的请求对结构进行封装处理。这里需要注意的是对返回类型已经是封装类(比如:异常处理器的响应可能已经是封装类型)时要忽略掉。

    第三个方法是实现“ContainerResponseFilter”,它的目的是专门处理方法返回类型是 void,或者某个资源类型返回是 null 的情况,这种情况下JAX-RS 框架一般会返回状态204,表示请求处理成功但没有响应内容。 我们对这种情况也重新处理改为响应为 404。

    • 然后,对JAX-RS使用方客户端使用一个filter,暗示不要对结果进行封装处理:
    @Provider
    public class UnwrapResultJaxrsFilter implements ClientRequestFilter {
        @Override
        public void filter(ClientRequestContext clientRequestContext) throws IOException {
            clientRequestContext.getHeaders().add("Not-Wrap-Result", "T");
        }
    }
    

    通过上述方法,我们可以开发代码更简单点,远离恼人的结构封装了,将封装逻辑作为一个全局的可协商行为,封装逻辑方便变更。虽然封装一般不建议轻易变更,会考虑对其他已有用户的支持和兼容,但可以让不同用户客户端通过 header 暗示封装的(类型)版本做到差异化对待。

    2.服务器端,如果个别请求是明确需要(或者不需要)结果封装怎么办?

    最简单的方法,可以在服务方法体内容,将 Request 的"wrap-result"情况,或者是指定为 True;

    @Context HttpRequest resteasyHttpRequest;
    
    public Movie movieByImdbId( String imdbId){
      resteasyHttpRequest.remove("wrap-result");
    }
    

    当然也可以自定义个注解"@ResultWrapper(false)",不要写上述代码,如下:

    @GET
    @Path("/getinfo")
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    @ResultWrapper(false)
    Movie movieByImdbId(@QueryParam("imdbId") String imdbId);
    

    著作权声明

    首次发布于此,转载请保留以上链接

  • 相关阅读:
    实现随机颜色
    为网站实现一个验证码
    vue.js帐号,密码,邮箱和移动手机号码正则验证
    从网址中截去主机名和参数
    vue.js判断网址参数是否有效
    创建windows service
    vue.js axios call api example
    vue.js mouse over change the image
    jQuery接收url的参数
    ms sql server排序
  • 原文地址:https://www.cnblogs.com/luyi/p/7101656.html
Copyright © 2011-2022 走看看