springboot+jersey+swagger,swagger界面接口自定义排序,全局异常处理
1、springboot 2.6.3 + jersey
依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jersey</artifactId> </dependency> <!-- 使用JAXB 在Jersey中将对象转换为XML --> <dependency> <groupId>org.glassfish.jersey.media</groupId> <artifactId>jersey-media-jaxb</artifactId> </dependency>
jersey 配置类
/** * 配置 jersey rest api 资源路径, Spring Boot建议在使用 * ResourceConfig添加资源类的时候,不要使用packages方法去自动扫描,建议还是使用register添加。 * * 注解ApplicationPath,默认为/* * */ @Component @ApplicationPath("/jersey") public class JerseyConfig extends ResourceConfig { public JerseyConfig() { //packages("com.oy"); // 扫描 com.oy 包,使其识别 JAX-RS 注解 register(UserController.class); } }
模型类
package com.oy.model; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(name = "user") // 将该类转化成XML时,说明这个是XML的根节点 public class User { private String name; private String email; public User() {} public User(String name, String email) { this.name = name; this.email = email; } @XmlAttribute // 属性 public String getName() { return name; } public void setName(String name) { this.name = name; } @XmlElement // 节点 public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } }
资源类
@Component @Path("/users") public class UserController { /** * http://localhost:8080/jersey/users/json */ @GET @Path("/json") @Produces(MediaType.APPLICATION_JSON) public User getJson() { List<User> users = new ArrayList<>(); User user = new User("xxx", "xxx@163.com"); users.add(user); return users.get(0); } /** * 使用 JAXB 在 Jersey 中将对象转换为 XML */ @GET @Path("/xml") @Produces(MediaType.APPLICATION_XML) public User getXml() { List<User> users = new ArrayList<>(); User user = new User("xxx", "xxx@163.com"); users.add(user); return users.get(0); } // 返回多节点xml @GET @Path("/getUsers") @Produces(MediaType.APPLICATION_XML) public List<User> getAllUser() { List<User> users = new ArrayList<User>(); users.add(new User("001", "HuiJia")); users.add(new User("002", "Andy")); users.add(new User("003", "BoWen")); return users; } // 返回单节点xml @GET @Produces(MediaType.APPLICATION_XML) @Path("/getUser") public User getUser() { User user = new User("004", "Lucy"); return user; } }
问题:jersey:MessageBodyWriter not found for media type=application/xml
<!-- 使用JAXB 在Jersey中将对象转换为XML --> <dependency> <groupId>org.glassfish.jersey.media</groupId> <artifactId>jersey-media-jaxb</artifactId> </dependency>
2、Jersey&Jackson返回json对象时隐藏null字段
https://www.jianshu.com/p/1cc610e9e93d
3、jersey 集成 swagger,swagger界面接口自定义排序
/resources/static.apidocs 目录的资源文件可以从gitee下载(搜springboot jersey swagger)
依赖
<dependency> <groupId>io.swagger</groupId> <artifactId>swagger-jersey2-jaxrs</artifactId> <version>1.6.5</version> </dependency>
实现接口自定义排序:通过自定义BaseApiListingResource的子类,当第一次生成Swagger对象后,对swagger对象的tags进行排序,从而实现接口自定义排序
public class MySwaggerApiListingResource extends BaseApiListingResource { protected Response getListingJsonResponse( Application app, ServletContext servletContext, ServletConfig servletConfig, HttpHeaders headers, UriInfo uriInfo) { Swagger swagger = process(app, servletContext, servletConfig, headers, uriInfo); /* 对 tags 进行排序 */ List<Tag> tags = swagger.getTags(); // 获取添加@Api注解的类 SwaggerContextService ctxService = new SwaggerContextService() .withServletConfig(servletConfig) .withBasePath(getBasePath(uriInfo)); Scanner scanner = ctxService.getScanner(); Set<Class<?>> classes = scanner.classes(); // 所有扫描的类 final Map<String, Integer> map = new TreeMap<>(); // if (classes != null && !classes.isEmpty()) { for (Class<?> aClass : classes) { Api annotation = AnnotationUtils.findAnnotation(aClass, Api.class); if (annotation == null) continue; map.put(annotation.value(), annotation.position()); } } tags.sort((e1, e2) -> { Integer order1 = map.get(e1.getName()); Integer order2 = map.get(e2.getName()); if (order1 != null && order2 != null) { return order1.compareTo(order2); } return 0; }); if (swagger != null) { return Response.ok().entity(swagger).type(MediaType.APPLICATION_JSON).build(); } return Response.status(404).build(); } private static String getBasePath(UriInfo uriInfo) { if (uriInfo != null) { return uriInfo.getBaseUri().getPath(); } else { return "/"; } } }
SwaggerApiListingResource
@Path("/swagger.{type:json|yaml}") public class SwaggerApiListingResource extends MySwaggerApiListingResource { @Context ServletContext context; @GET @Produces({MediaType.APPLICATION_JSON, "application/yaml"}) @ApiOperation(value = "The swagger definition in either JSON or YAML", hidden = true) public Response getListing( @Context Application app, @Context ServletConfig sc, @Context HttpHeaders headers, @Context UriInfo uriInfo, @PathParam("type") String type) { if (StringUtils.isNotBlank(type) && type.trim().equalsIgnoreCase("yaml")) { return getListingYamlResponse(app, context, sc, headers, uriInfo); } else { return getListingJsonResponse(app, context, sc, headers, uriInfo); } } }
JerseyConfig
/** * 配置 jersey rest api 资源路径, SpringBoot 建议使用 ResourceConfig 添加资源类的时候, * 不要使用 packages() 方法去自动扫描,建议还是使用 register() 添加。 * <p> * 注解ApplicationPath,默认为/* */ @Component @ApplicationPath("/api") public class JerseyConfig extends ResourceConfig { public JerseyConfig() { // 扫描包,使其识别 JAX-RS 注解 //packages("com.oy.api"); // 注册站点服务 注册顺序与界面排列顺序无关, 根据@Api注解的position排序, 并且@Api注解必须配置value值 register(UserService.class); register(CityService.class); // 注册过滤器 //register(XxxFilter.class); } @PostConstruct private void init() { configureSwagger(); } /** * 访问Swagger主页: http://localhost:8080/apidocs/index.html */ private void configureSwagger() { // Available at localhost:port/swagger.json //this.register(ApiListingResource.class); this.register(SwaggerApiListingResource.class); // 使用自定义的swagger.json访问服务类 this.register(SwaggerSerializers.class); register(AcceptHeaderApiListingResource.class); BeanConfig config = new BeanConfig(); config.setConfigId("1001"); config.setTitle("xxx Open API"); config.setVersion("v1.0.0"); config.setContact("xxx"); config.setSchemes(new String[]{"http", "https"}); config.setBasePath("/api"); config.setResourcePackage("com.oy.api"); config.setPrettyPrint(true); config.setScan(true); } }
4、springboot-jersey全局异常处理
在jersey中注册了一个全局异常处理器 GlobalExceptionHandler
import com.oy.result.ApiResult; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Response; import javax.ws.rs.ext.ExceptionMapper; import javax.ws.rs.ext.Provider; @Provider public class GlobalExceptionHandler implements ExceptionMapper<Throwable> { public Response toResponse(Throwable ex) { if (ex instanceof WebApplicationException) { WebApplicationException exception = (WebApplicationException) ex; Response response = exception.getResponse(); String msg = response.getStatusInfo().toString(); ApiResult<Object> apiResult = ApiResult.fail(response.getStatus(), msg); return Response.status(response.getStatus()).entity(apiResult).type("application/json;charset=utf-8").build(); } String msg = ex.getMessage(); if (msg == null || !msg.isEmpty()) msg = ex.getClass().getName(); ApiResult<Object> apiResult = ApiResult.fail(500, msg); return Response.status(500).entity(apiResult).type("application/json;charset=utf-8").build(); } }
但是发现,一般的异常是会走GlobalExceptionHandler的,包括404错误等都会走GlobalExceptionHandler。但是某些情况不会走GlobalExceptionHandler:比如请求传递错误格式的json数据时。
请求正常传递json数据时
请求传递错误格式的json数据时
原因是jersey的ExceptionMapperFactory默认创建了三个错误处理器,如下图,导致当发生JsonParseException异常时,优先走了jersey默认的处理器,而没有走GlobalExceptionHandler。
解决:给JsonParseException,JsonMappingException,ValidationException定义处理器
JerseyConfig中注册
// 注册异常处理器 register(JsonMappingExceptionHandler.class, 1); register(JsonParseExceptionHandler.class, 1); register(ValidationExceptionHandler.class, 1); register(GlobalExceptionHandler.class);
JsonParseExceptionHandler
import com.fasterxml.jackson.core.JsonParseException; import com.oy.result.ApiResult; import javax.annotation.Priority; import javax.ws.rs.core.Response; import javax.ws.rs.ext.ExceptionMapper; import javax.ws.rs.ext.Provider; @Provider @Priority(1) public class JsonParseExceptionHandler implements ExceptionMapper<JsonParseException> { @Override public Response toResponse(JsonParseException ex) { ApiResult<Object> apiResult = ApiResult.fail(400, ex.getMessage()); return Response.status(400).entity(apiResult).type("application/json;charset=utf-8").build(); } }
posted on 2022-02-22 18:18 wenbin_ouyang 阅读(818) 评论(0) 编辑 收藏 举报