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相关文章

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值