SpringBoot:整合控制层

RESTful服务是“前后端分离”架构中的主要功能:后端应用对外暴露RESTful服务;前端应用则通过RESTful服务与后端应用交互。RESTful服务的数据格式既可是JSON的,也可是XML的。

开发基于 JSON 的 RESTful 服务非常简单, 只要使用@RestController注解修饰控制器类,或者使用@ResponseBody修饰处理方法即可。(@RestController和@Controller的区别就在于,@RestController会自 动为每个处理方法都添加@ResponseBody注解)

Spring Boot内置了如下三种JSON库的支持:Jackson、Gson和JSON-B。正如从前面所看到的,如果没有任何特别的配置,Spring Boot默认 选择Jackson作为JSON库。实际上,Jackson的自动配置由spring-boot-sta rter-json.jar提供,只要Spring Boot检测到系统类加载路径中有Jackson依 赖库,Spring Boot就会自动创建基于Jackson的ObjectMapper。

1,整合JSON

1.1,默认方式

JSON是目前主流的前后端数据传输方式,SpringMVC中使用消息转换器HttpMessageConverter对JSON转换提供了很好的支持,在SpringBoot中更进一步,对相关配置做了更进一步的简化。如果没有任何特别的配置,Spring Boot默认选择Jackson作为JSON库。实际上,Jackson的自动配置由spring-boot-starter-json.jar提供,只要Spring Boot检测到系统类加载路径中有Jackson依 赖库,Spring Boot就会自动创建基于Jackson的ObjectMapper。

package com.springboot.ysy.Pojo;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;

import java.util.Date;

public class Book {
    private String name;
    private String author;
    @JsonIgnore     //转换为JSON的时候忽略此项
    private Float price;
    @JsonFormat(pattern = "yyyy-MM-dd")     //设置转换JSON后的格式
    private Date publicationDate;

    //省略get和set方法
}
package com.springboot.ysy.Controller;

import com.springboot.ysy.Pojo.Book;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;

@RestController
class BookController {
    @RequestMapping("/book")
    public Book book(){
        Book book = new Book();
        book.setAuthor("罗贯中");
        book.setName("三国演义");
        book.setPrice(30f);
        book.setPublicationDate(new Date());
        return book;
    }
}

1.2,Gson

Gson是Google开源JSON解析框架,使用Gson,需要先移除默认的jackson-databin,然后加入Gson依赖。

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>com.fasterxml.jackson.core</groupId>
                    <artifactId>jackson-databind</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson </artifactId>
        </dependency>
    </dependencies>
package com.springboot.ysy.Pojo;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;

import java.util.Date;

public class Book {
    private String name;
    private String author;
    protected Float price;
    private Date publicationDate;
//省略get和set
}

需要提供Gson的自动转换类GsonHttpMessageConvertersConfiguration。

package com.springboot.ysy.Controller;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.GsonHttpMessageConverter;
import java.lang.reflect.Modifier;
@Configuration
public class GsonConfig {
    @Bean
    GsonHttpMessageConverter gsonHttpMessageConverter() {
        GsonHttpMessageConverter converter = new GsonHttpMessageConverter();
        GsonBuilder builder = new GsonBuilder();
        builder.setDateFormat("yyyy-MM-dd");    //自定义日期类型
        builder.excludeFieldsWithModifiers(Modifier.PROTECTED);     //设置Gson解析时Protected字段被过滤掉
        Gson gson = builder.create();
        converter.setGson(gson);
        return converter;
    }
}

1.3,fastjson

fastjson是阿里巴巴的一个开源JSON解析框架,是目前JSON解析速度最快的开源框架,该框架也可以集成到Spring Boot中。不同于Gson,fastjson继承完成后不能立马使用,需要开发者提供响应的HttpMessageConverter后才可使用。

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>com.fasterxml.jackson.core</groupId>
                    <artifactId>jackson-databind</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.47</version>
        </dependency>
    </dependencies>

 另外,还需要配置响应编码,否则返回的JSON中文会乱码,在application.properties中添加:

server.servlet.encoding.force-response=true
package com.springboot.ysy.Controller;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.nio.charset.Charset;
@Configuration
public class MyFastJsonConfig {
    @Bean
    FastJsonHttpMessageConverter fastJsonHttpMessageConverter() {
        FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
        FastJsonConfig config = new FastJsonConfig();
        config.setDateFormat("yyyy-MM-dd");
        config.setCharset(Charset.forName("UTF-8"));
        config.setSerializerFeatures(
                SerializerFeature.WriteClassName,
                SerializerFeature.WriteMapNullValue,
                SerializerFeature.PrettyFormat,
                SerializerFeature.WriteNullListAsEmpty,
                SerializerFeature.WriteNullStringAsEmpty
        );
        converter.setFastJsonConfig(config);
        return converter;
    }
}

2,整合视图(Thymeleaf)

添加依赖

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

配置控制器

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;
import java.util.ArrayList;
import java.util.List;
@Controller
public class BookController {
    @GetMapping("/books")
    public ModelAndView books() {
        List<Book> books = new ArrayList<>();
        Book b1 = new Book();
        b1.setId(1);
        b1.setAuthor("罗贯中");
        b1.setName("三国演义");
        Book b2 = new Book();
        b2.setId(2);
        b2.setAuthor("曹雪芹");
        b2.setName("红楼梦");
        books.add(b1);
        books.add(b2);
        ModelAndView mv = new ModelAndView();
        mv.addObject("books", books);
        mv.setViewName("books");
        return mv;
    }
}

创建返回视图

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>图书列表</title>
</head>
<body>
<table border="1">
    <tr>
        <td>图书编号</td>
        <td>图书名称</td>
        <td>图书作者</td>
    </tr>
    <tr th:each="book:${books}">
        <td th:text="${book.id}"></td>
        <td th:text="${book.name}"></td>
        <td th:text="${book.author}"></td>
    </tr>
</table>
</body>
</html>

3,CORS跨域问题

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="jquery3.3.1.js"></script>
</head>
<body>
<div id="contentDiv"></div>
<div id="deleteResult"></div>
<input type="button" value="提交数据" onclick="postData()"><br>
<input type="button" value="删除数据" onclick="deleteData()"><br>
<script>
    function deleteData() {
        $.ajax({
            url:'http://localhost:8080/book/99',
            type:'delete',
            success:function (msg) {
                $("#deleteResult").html(msg);
            }
        })
    }
    function postData() {
        $.ajax({
            url:'http://localhost:8080/book/add',
            type:'post',
            data:{name:'三国演义'},
            success:function (msg) {
                $("#contentDiv").html(msg);
            }
        })
    }
</script>
</body>
</html>
package com.example.demo;

import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/book")
public class BookController {
    @PostMapping("/add")
    @CrossOrigin(value = "*")   //一劳永逸,哈哈哈
    public String addBook(String name){
        return "receive:"+name;
    }
    @DeleteMapping("/{id}")
    @CrossOrigin(value = "http://localhost:8080",maxAge = 1800,allowedHeaders = "*")
    public String deleteBook(@PathVariable Long id){
        return String.valueOf(id);
    }
}

 @CrossOrigin:value表示允许来自http://localhost:8080域的请求是支持跨域的。

maxAge:表示探测请求的有效期。对于DELETE,PUT请求或者自定义信息的请求,在执行过程中先发送探测请求,探测请求不用每次都发送,配置有效期,在有效期过了之后才会发送探测请求。

alloweHeaders:表示允许的请求头,*表示所有的请求头都被允许。

4,文件上传

Java中的文件上传一共涉及到两个组件,

一个是CommonsMultipartResolver,另一个是StandardServletMultipartResolver,

其中CommonsMultipartResolver使用commons-fileupload来处理multipart请求,而StandardServletMultipartResolver则是基于Servlet3.0来处理multipart请求的。因此若使用StandardServletMultipartResolver,则不需要添加额外的jar包。

Tomcat7.0开始就支持Servlet3.0了,而Spring Boot2.0.4内嵌的Tomcat为Tomcat8.5.32,因此可以直接使用StandardServletMultipartResolver。如果开发者没有提供MultipartResolver,那么默认采用的MultipartResolver就是StandardServletMultipartResolver。因此,在Spring Boot中上传文件甚至可以做到零配置。

4.1,单文件上传

package com.springboot.ysy.Controller;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;

@RestController
public class FileUploadController {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd/");
    @PostMapping("/upload")
    public String upload(MultipartFile uploadFile, HttpServletRequest req){
        //规划上传文件的保存路径为项目运行目录下的uploadFile文件夹,并在文件中通过日期对所上传文件归类保存
        String realPath = req.getSession().getServletContext().getRealPath("/uploadFile/");
        String format = sdf.format(new Date());
        File folder = new File(realPath+format);
        if(!folder.isDirectory()){
            folder.mkdirs();
        }
        //重命名文件
        String oldName = uploadFile.getOriginalFilename();
        String newName = UUID.randomUUID().toString()+oldName.substring(oldName.lastIndexOf("."),oldName.length());
        try {
            //文件保存
            uploadFile.transferTo(new File(folder,newName));
            String filePath = req.getScheme()+"://"+req.getServerName()+":"+req.getServerPort()+"/uploadFile/"+format+newName;
            return filePath;
        }catch (IOException e){
            e.printStackTrace();
        }
        return "上传失败";
    }
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/upload" method="post" enctype="multipart/form-data">
        <input type="file" name="uploadFile" value="请选择文件"/>
        <input type="submit" value="上传"/>
    </form>
</body>
</html>

如果还需要对文件上传的细节进行配置,在application.properties中进行配置。

#是否开启文件上传支持,默认为true
spring.servlet.multipart.enabled=true
#文件写入磁盘的阀值,默认为0
spring.servlet.multipart.file-size-threshold=0
#上传文件的临时保存位置
spring.servlet.multipart.location=E:\\temp
#上传的单个文件的最大大小,默认为1MB
spring.servlet.multipart.max-file-size=1MB
#多个文件上传时的总大小,默认10MB
spring.servlet.multipart.max-request-size=10MB
#表示文件是否延迟解析,默认为false
spring.servlet.multipart.resolve-lazily=false

4.2,多文件上传

多文件上传和单文件上传基本一致,首先修改HTML文件,然后修改控制器。将上传一个改成,循环上传。如果上传文件超过application.properties设定的文件大小,发出异常,然后被上面程序捕获。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/upload" method="post" enctype="multipart/form-data">
        <input type="file" name="uploadFiles" value="请选择文件" multiple/>
        <input type="submit" value="上传"/>
    </form>
</body>
</html>
package com.springboot.ysy.Controller;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;

@RestController
public class FileUploadController {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd/");
    @PostMapping("/uploads")
    public String upload(MultipartFile[] uploadFiles, HttpServletRequest req){
        for (MultipartFile uploadFile : uploadFiles) {
            String realPath = req.getSession().getServletContext().getRealPath("/uploadFile/");
            System.out.println(realPath);
            String format = sdf.format(new Date());
            File folder = new File(realPath + format);
            if (!folder.isDirectory()) {
                folder.mkdirs();
            }
            String oldName = uploadFile.getOriginalFilename();
            String newName = UUID.randomUUID().toString() + oldName.substring(oldName.lastIndexOf("."), oldName.length());
            try {
                uploadFile.transferTo(new File(folder, newName));
                String filePath = req.getScheme() + "://" + req.getServerName() + ":" + req.getServerPort() + "/uploadFile/" + format + newName;
                System.out.println(filePath);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return "上传失败!";
    }
}

5,@ControllerAdvice

@ControllerAdvice就是@Controller的增强版。@ControllerAdvice主要用来处理全局数据,一般搭配@ExceptionHandler、@ModelAttribute以及@InitBuinder使用。

5.1,全局异常处理

@ControllerAdvice结合@ExceptionHandler定义全局异常捕获机制。

package com.springboot.ysy.Controller;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@ControllerAdvice
public class uploadException {
    @ExceptionHandler(MaxUploadSizeExceededException.class)
    public void uploadException(MaxUploadSizeExceededException e, HttpServletResponse resp) throws IOException {
        resp.setContentType("text/html;charset=utf-8");
        PrintWriter out = resp.getWriter();
        out.write("上传文件大小超出限制");
        out.flush();
        out.close();
    }
}

如果想返回视图,采用以下方法:在templates目录下创建error.html,添加thymeleaf依赖。 

package com.springboot.ysy;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.servlet.ModelAndView;
import java.io.IOException;

@ControllerAdvice
public class uploadException {
    @ExceptionHandler(MaxUploadSizeExceededException.class)
    public ModelAndView uploadException(MaxUploadSizeExceededException e) throws IOException {
       ModelAndView mv = new ModelAndView();
       mv.addObject("msg","上传文件超出限制");
       mv.setViewName("error");
       return mv;
    }
}
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <div th:text="${msg}"></div>
</body>
</html>

5.2,添加全局数据

package com.example.demo;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ModelAttribute;
import java.util.HashMap;
import java.util.Map;

@ControllerAdvice
public class GlobalConfig {
    @ModelAttribute(value="info")
    public Map<String ,String >userInfo(){
        HashMap<String,String> map = new HashMap<>();
        map.put("username","罗贯中");
        map.put("gender","男");
        return map;
    }
}

在全局配置中添加userInfo方法,返回一个map。该方法有一个注解@ModelAttribute,其中的value属性表示这条返回数据的key,而方法的返回值是返回数据的value;

此时在任意请求的Controller中,通过方法参数中的Model都可以获取info的数据。

package com.example.demo;

import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

@RestController
public class helloController {
    @GetMapping("/hello")
    public void hello(Model model){
        Map<String,Object> map = model.asMap();
        Set<String> keySet = map.keySet();
        Iterator<String> iterator = keySet.iterator();
        while(iterator.hasNext()){
            String key = iterator.next();
            Object value = map.get(key);
            System.out.println(key+">>>>>>"+value);
        }
    }
}

6,自定义错误页面

6.1,静态页面

创建resource/static/error,然后创建404.html,500.html(内容自定)。

6.2,动态页面

创建resource/templates/error,然后创建4xx.html,5xx.html。

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<table border="2">
    <tr>
        <td>timestamp</td>
        <td th:text="${timestamp}"></td>
    </tr>
    <tr>
        <td>status</td>
        <td th:text="${status}"></td>
    </tr>
    <tr>
        <td>error</td>
        <td th:text="${error}"></td>
    </tr>
    <tr>
        <td>error</td>
        <td th:text="${#messages}"></td>
    </tr>
    <tr>
        <td>path</td>
        <td th:text="${path}"></td>
    </tr>
</table>
</body>
</html>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值