Java,for,Web学习笔记(八一):RESTful(1)设置Rest,Contextx

发布时间:2022-06-22 19:35:08   来源:党团工作    点击:   
字号:

 Java for Web 学习笔记(八一):RESTful (1 )设置 Rest Context Wei:我想了想,还是小步快跑,控制每篇学习笔记的篇幅。

 我们将给出 Rest Context 和 web Context 共存的例子。Service 是方在 Root Context 的,Controller 则是位于下一级的 Rest Context 或者 web context,需要能够识别扫描,最简单的,我们可以将它们分别放在不同的 package 中,通过@ComponentScan 中的 basePackages,扫描不同的位置来实现。这种方式能适应大部分的场景。但在小例子中,我们采用另一种方式,通过定义不同的标记来进行区分这个 controller 是属于 rest 的还是 web 的。

 定 义继承@Controller 的标记:@WebController 和@RestEndpoint 我们定义@WebController,用来标记 web context 下的 controller。标记在之前的validator 中已经学过。

 @Target({ElementType.TYPE})

 @Retention(RetentionPolicy.RUNTIME)

 @Documented

 @Controller

 //继承@Controller

 public @interface WebController {

  //和其他的 spring 的标记一样,提供一个可以覆盖的方法,用于设置 bean 名字。

 String value() default "";

 }

 定义@RestEndpoint,用于 Restful 网络接口 @Target({ElementType.TYPE})

 @Retention(RetentionPolicy.RUNTIME)

 @Documented

 @Controller

 public @interface RestEndpoint {

 String value() default "";

 }

 实现 REST 上下文的配置 REST 用于机器对机器,其上下文环境更为简单,不需要那么多的 message converters,小例子只需要对 json 或者 xml 进行解析和封装,不需要ViewResolver,RequestToViewNameResolver。

 //【1】扫描带有@RestEndpoint 标记

 @Configuration

 @EnableWebMvc

 @ComponentScan(

 basePackages = "cn.wei.chapter17.site",

 useDefaultFilters = false,

 includeFilters = @ComponentScan.Filter({RestEndpoint.class}))

 public class RestServletContextConfiguration extends WebMvcConfigurerAdapter{

 // 之前已经在强大的生态中讲过对于 Json 和 XML,并不需要手动设置 codec 的转换器,Spring 能够自动在 lib 中找到合适的。这里沿用书中例子

 @Inject ObjectMapper objectMapper;

 @Inject Marshaller marshaller;

 @Inject Unmarshaller unmarshaller;

 //【2】验证在 RESTful 接口很重要,要加上

 @Inject SpringValidatorAdapter validator;

  //【3】消息格式支持 Json 和 XML 两种

 //(3.1) 根据 Content-Type 加上相关的转换器作。

 @Override

 public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {

 converters.add(new SourceHttpMessageConverter<>());

  MarshallingHttpMessageConverter xmlConverter = new MarshallingHttpMessageConverter();

 xmlConverter.setSupportedMediaTypes(Arrays.asList( new MediaType("application", "xml"),

  new MediaType("text", "xml")));

 xmlConverter.setMarshaller(this.marshaller);

 xmlConverter.setUnmarshaller(this.unmarshaller);

 converters.add(xmlConverter);

  MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();

 jsonConverter.setSupportedMediaTypes(Arrays.asList(new MediaType("application", "json"),

  new MediaType("text", "json")));

 jsonConverter.setObjectMapper(this.objectMapper);

 converters.add(jsonConverter);

 }

  //(3.2) 对媒体格式的协商。如果请求中 Accept: application/xml,则采用 xml 的方式,若支持 json 和 xml,其先后顺序具有优先级别;若无相关信息,采用缺省的 json

 @Override

 public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {

 configurer.favorPathExtension(false).favorParameter(false)

 .ignoreAcceptHeader(false)

 .defaultContentType(MediaType.APPLICATION_JSON); //缺

 省采用 json 方式

 }

  //(2.1) 设置 validator

 @Override

 public Validator getValidator() {

 return this.validator;

 }

  //(2.2) 设置 locale 解析器,可对 Restful 接口的返回信息进行本地化,通常是错误信息。REST 没有 session,根据 Accept-Language 获取

  @Bean

 public LocaleResolver localeResolver(){

 return new AcceptHeaderLocaleResolver();

 }

  }

 WebServletContextConfiguration 和以前学习的没有什么区别,只是在扫描是针对@WebController。

 依次启动不同的上下文 我们将在 Bootstrap 类中一次启动 Root 上下文,Web 上下文,和 Rest 上下文

 public class Bootstrap implements WebApplicationInitializer{

  @Override

 public void onStartup(ServletContext container) throws ServletException {

 container.getServletRegistration("default").addMapping("/resource/*");

  //(1)启动 root 上下文

 AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();

 rootContext.register(RootContextConfiguration.class);

 container.addListener(new ContextLoaderListener(rootContext));

  //(2)启动 web 上下文

 AnnotationConfigWebApplicationContext webContext = new AnnotationConfigWebApplicationContext();

 webContext.register(WebServletContextConfiguration.class);

 ServletRegistration.Dynamic dispatcher = container.addServlet(

 "springWebDispatcher", new DispatcherServlet(webContext));

 dispatcher.setLoadOnStartup(1);

 dispatcher.setMultipartConfig(new MultipartConfigElement(null, 20_971_520L, 41_943_040L, 512_000));

 dispatcher.addMapping("/");

  //(3)启动 Rest 上下文,spring 将采用最佳匹配的方式,和 web 上下文的"/"不矛盾。

 AnnotationConfigWebApplicationContext restContext = new AnnotationConfigWebApplicationContext();

 restContext.register(RestServletContextConfiguration.class);

 // 小例子测试 OPTIONS,需要支持 OPTIONS,将此开关打开。FrameworkServlet 中此值为 false,但在 spring 4.3,已经内置为 true。我们使用了 4.3.9.RELEASE,实际上无需手动设置为 true。

 // ➤ false 为采用自动处理,将会回复 200OK,body 长度为 0;

 // ➤ true 将转到我们的代码处理,如果 url 没有匹配,会回复 404。

 // DispatcherServlet restServlet = new DispatcherServlet(restContext);

 // restServlet.setDispatchOptionsRequest(true);

 // dispatcher = container.addServlet("springRestDispatcher", restServlet);

 dispatcher = container.addServlet("springRestDispatcher", new DispatcherServlet(restContext));

 dispatcher.setLoadOnStartup(2);

 dispatcher.addMapping("/services/Rest/*");

 //我们希望对无效的 url 进行自定义的 404 回复(返回一个 json 或者 xml 说明),而非缺省方式(缺省返回一个页面),需要将 NoHandlerFoundException 抛出,在代码中处理,而非默认的 servlet 自行处理。

 dispatcher.setInitParameter("throwExceptionIfNoHandlerFound", "true");

  }

 }

  相关链接:

 我的 Professional Java for Web Applications 相关文章