美文网首页
SpringBoot实现REST

SpringBoot实现REST

作者: j4fan | 来源:发表于2017-10-25 17:27 被阅读406次

如题,本文讲述如何使用Springboot实现restapi,这里感谢开源社区的作者@简单的土豆,和他的源码https://github.com/Fan4J/spring-boot-api-project-seed.git

1基本配置

项目结构.png

基本配置包括分环境的profile配置, log4j2配置,mybatis的配置,maven依赖的添加,统一结果封装,结果生成器等等。
这里作者使用了mybatis通用mapper

public interface Mapper<T>
        extends
        BaseMapper<T>,
        ConditionMapper<T>,
        IdsMapper<T>,
        InsertListMapper<T> {
}

通用service接口

public interface Service<T> {
    void save(T model);//持久化
    void save(List<T> models);//批量持久化
    void deleteById(Long id);//通过主鍵刪除
    void deleteByIds(String ids);//批量刪除 eg:ids -> “1,2,3,4”
    void update(T model);//更新
    T findById(Long id);//通过ID查找
    T findBy(String fieldName, Object value) throws TooManyResultsException; //通过Model中某个成员变量名称(非数据表中column的名称)查找,value需符合unique约束
    List<T> findByIds(String ids);//通过多个ID查找//eg:ids -> “1,2,3,4”
    List<T> findByCondition(Condition condition);//根据条件查找
    List<T> findAll();//获取所有
}

然后实现abstractService,用通用mapper实现了service,后面的service,只需继承abstractService即可

public abstract class AbstractService<T> implements Service<T> {

    @Autowired
    protected Mapper<T> mapper;

    private Class<T> modelClass;    // 当前泛型真实类型的Class

    public AbstractService() {
        ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass();
        modelClass = (Class<T>) pt.getActualTypeArguments()[0];
    }

    public void save(T model) {
        mapper.insertSelective(model);
    }

    public void save(List<T> models) {
        mapper.insertList(models);
    }

    public void deleteById(Long id) {
        mapper.deleteByPrimaryKey(id);
    }

    public void deleteByIds(String ids) {
        mapper.deleteByIds(ids);
    }

    public void update(T model) {
        mapper.updateByPrimaryKeySelective(model);
    }

    public T findById(Long id) {
        return mapper.selectByPrimaryKey(id);
    }

    @Override
    public T findBy(String fieldName, Object value) throws TooManyResultsException {
        try {
            T model = modelClass.newInstance();
            Field field = modelClass.getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(model, value);
            return mapper.selectOne(model);
        } catch (ReflectiveOperationException e) {
            throw new ServiceException(e.getMessage(), e);
        }
    }

    public List<T> findByIds(String ids) {
        return mapper.selectByIds(ids);
    }

    public List<T> findByCondition(Condition condition) {
        return mapper.selectByCondition(condition);
    }

    public List<T> findAll() {
        return mapper.selectAll();
    }
}

2.下面看看Controller

先上代码

@RestController
public class FetchConfigController {

    @Autowired
    UserInfoService userInfoService;

    @PostMapping(value = "/add", consumes = "application/json", produces = "application/json")
    public Result add(@RequestBody UserInfo userInfo) {
        userInfoService.save(userInfo);
        return ResultCodeGenerator.genSuccessResult();
    }

    @PostMapping("/delete")
    public Result delete(@RequestParam Long id) {
        userInfoService.deleteById(id);
        return ResultCodeGenerator.genSuccessResult();
    }

    @PostMapping("/update")
    public Result update(@RequestBody UserInfo userInfo) {
        userInfoService.update(userInfo);
        return ResultCodeGenerator.genSuccessResult();
    }

    @PostMapping("/detail")
    public Result detail(@RequestParam Long id) {
        UserInfo userInfo = userInfoService.findById(id);
        return ResultCodeGenerator.genSuccessResult(userInfo);
    }

    @PostMapping("/list")
    public Result list(@RequestParam(defaultValue = "0") Integer page, @RequestParam(defaultValue = "0") Integer size) {
        PageHelper.startPage(page, size);
        List<UserInfo> list = userInfoService.findAll();
        PageInfo pageInfo = new PageInfo(list);
        return ResultCodeGenerator.genSuccessResult(pageInfo);
    }

}

1@RestController,可以看到它是@Controller/@ResponseBody的结合体,@ResponseBody这个注解在RESTAPI 中很有意义,它不是将返回资源定位到resouces下面的html/css/js生成视图,而是直接以写入输出流返回给客户端。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
    String value() default "";
}

这里不得不提到HttpMessageConverter,这个转换器顾名思义,是将httprequest输入流读成对象,或者字符串,再返回的时候将对象转换成HttpResponse输出流

public interface HttpMessageConverter<T> {
    boolean canRead(Class<?> var1, MediaType var2);

    boolean canWrite(Class<?> var1, MediaType var2);

    List<MediaType> getSupportedMediaTypes();

    T read(Class<? extends T> var1, HttpInputMessage var2) throws IOException, HttpMessageNotReadableException;

    void write(T var1, MediaType var2, HttpOutputMessage var3) throws IOException, HttpMessageNotWritableException;
}

可以看到它是个泛型的接口,主要的两个方法是read/write canread/write,Spring已经实现了abstractHttpMessageConverter,自己可以继承这个abstract的class,拿来使用。converter用canread/write检查http流的类型,然后返回是true,就用read/write方法去读写。一般都是用别人写好的converter,例如阿里的fastjson的converter,可以用于处理application/json,例如MappingJackson2XmlHttpMessageConverter用于处理application/xml。这些内容可以配在configure文件中

@Configuration
public class WebMvcConfigurer extends WebMvcConfigurerAdapter {

    private static Logger logger = LogManager.getLogger();

    //当前激活的配置文件
    @Value("${spring.profiles.active}")
    private String env;

    //使用阿里 FastJson 作为JSON MessageConverter
    //使用MappingJackson2XmlHttpMessageConverter进行xml的转换
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        FastJsonHttpMessageConverter4 converter = new FastJsonHttpMessageConverter4();
        FastJsonConfig config = new FastJsonConfig();
        config.setSerializerFeatures(SerializerFeature.WriteMapNullValue,//保留空的字段
                SerializerFeature.WriteNullStringAsEmpty,//String null -> ""
                SerializerFeature.WriteNullNumberAsZero);//Number null -> 0
        converter.setFastJsonConfig(config);
        converter.setDefaultCharset(Charset.forName("UTF-8"));
        MappingJackson2XmlHttpMessageConverter converter1 = new MappingJackson2XmlHttpMessageConverter();
        converters.add(converter1);
        converters.add(converter);
    }

我这里添加了两个converter,分别用来处理json和xml.
2.@PostMapping其实是@RequestMapping(method = {RequestMethod.POST})的缩写
3.@RequestParam其实还可以加入default和require的设置
4.@RequestBody直接把inputstream中的json通过converter读成对象进行处理,和@responsebody对应将对象写成json放入outputstream

3.研究下Spring自带的RestTemplate

通常我们进行http请求是用apache的httpclient,而且功能比较强大,这里spring也集成了自己的rest请求方法

private static ClientHttpRequestFactory getSimpleClientHttpRequestFactory() {
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        factory.setReadTimeout(60000);//ms
        factory.setConnectTimeout(30000);//ms
        return factory;
 }

RestTemplate直接new就可以使用了,构造方法里可以注入一个factory用于统一设置一些参数

private static RestTemplate getRestTemplate(ClientHttpRequestFactory factory) {
        RestTemplate restTemplate = new RestTemplate(factory);
        List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
        StringHttpMessageConverter stringHttpMessageConverter
                = new StringHttpMessageConverter();
        stringHttpMessageConverter.setWriteAcceptCharset(false);
        stringHttpMessageConverter.setDefaultCharset(Charset.forName("UTF-8"));
        messageConverters.add(stringHttpMessageConverter);
        messageConverters.add(new FormHttpMessageConverter());
        messageConverters.add(new FastJsonHttpMessageConverter4());
        restTemplate.setMessageConverters(messageConverters);
        return restTemplate;
    }

template里面统一可以注入converter,这里我加入了3个converter,这里注意StringmessageConverter里面默认的字符是ISO-8859-1,所以new一个新的converter并且设置编码防止出错,剩下的就是简单粗暴的直接使用咯

        RestTemplate restTemplate = getRestTemplate(getSimpleClientHttpRequestFactory());
        String url = "http://localhost:7777/dcs/list";
//        HttpHeaders headers = new HttpHeaders();
//        headers.setContentType(MediaType.ALL.APPLICATION_JSON);
//        UserInfo userInfo = new UserInfo().setUserId((long) 1231).
//                setUserName("test").setAge(13).setEmail("fafa@jancy.com");
//        HttpEntity request = new HttpEntity(userInfo, headers);
//        Result result = restTemplate.postForObject(url, request, Result.class);
//        System.out.println(JSON.toJSONString(result));
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        MultiValueMap<String, Integer> map = new LinkedMultiValueMap<>();
        map.add("page", 1);
        map.add("size", 5);
        HttpEntity<MultiValueMap<String, Integer>> request = new HttpEntity<>(map, headers);
        Result result = restTemplate.postForObject(url, request, Result.class);
        System.out.println(JSON.toJSONString(result));

根据api的不同,既可以直接传入只要把HttpEntity包装好即可。
除了写在main函数中,也可以用Spring的注解加载factory/restemplate

@Configuration
public class RestTemplateConfig {
    @Bean
    public RestTemplate restTemplate(ClientHttpRequestFactory factory){
        RestTemplate restTemplate = new RestTemplate(factory);
        List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
        StringHttpMessageConverter stringHttpMessageConverter
            = new StringHttpMessageConverter();
        stringHttpMessageConverter.setWriteAcceptCharset(false);
        stringHttpMessageConverter.setDefaultCharset(Charsets.UTF_8);
        messageConverters.add(stringHttpMessageConverter);
        messageConverters.add(new FormHttpMessageConverter());
        messageConverters.add(new FastJsonHttpMessageConverter());
        restTemplate.setMessageConverters(messageConverters);
        return restTemplate;
    }

    @Bean
    public ClientHttpRequestFactory simpleClientHttpRequestFactory(){
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        factory.setReadTimeout(60000);//ms
        factory.setConnectTimeout(30000);//ms
        return factory;
    }
}

在其他代码里只用@autowired即可使用

4.总结

以上就是如何用Springboot构建restApi,其实还可以和数据库做很多交互,还有签名认证,session管理等东西,都到后面完善吧。源码地址在最开始就贴了,是个开源的项目,HttpMessageConverter,如何得到json、xml的返回,都需要操作操作。继续努力吧
个人博客 欢迎访问~

相关文章

网友评论

      本文标题:SpringBoot实现REST

      本文链接:https://www.haomeiwen.com/subject/mfiqpxtx.html