| 知乎专栏 |
Restful 控制器
URL 参数,只能传递简单数据类型,参数可以在 web 服务器日志中看到,方便调试。可以在 Web 服务器中拦截,能用于 7层负载均衡转发策略中。还能做数据统计分析。
form data 数据提交的优势是简单,key-value 对应,一般情况下,web服务器不做特殊设置,是看不到数据的,数据类型支持数组等简单类型。
json 方案是应对复杂的数据结构,方便进行反序列化操作,除了常规数据类型 int/float/string/bool 还支持 map,list 以及用户定义的 class
@GetMapping("iterable")
public Iterable<PicturePsychoanalysis> get() {
Iterable<PicturePsychoanalysis> picturePsychoanalysis = picturePsychoanalysisService.findByAnalysisIsNull();
return picturePsychoanalysis;
}
@PostMapping("/{device}/question/voice")
public String questionVoice(@PathVariable String device, @RequestParam("file") MultipartFile file, @RequestParam("session") String session) {
if (file.isEmpty()) {
logger.error("上传失败,请选择文件");
}
String fileName = file.getOriginalFilename();
String filePath = "/tmp/".concat(session.concat(".mp3"));
File saveAs = new File(filePath);
try {
file.transferTo(saveAs);
logger.info("上传成功 " + fileName);
} catch (IOException e) {
logger.error(e.toString(), e);
return "上传失败";
}
return "上传成功";
}
@RequestMapping("/get/{id}")
public Member getStatistics(@PathVariable long id) {
Member statistics = memberRepostitory.findOne(id);
if (statistics == null) {
statistics = new Member();
}
return statistics;
}
MediaType.APPLICATION_JSON_VALUE 执行结果反馈json数据
@RestController
@RequestMapping("/api/persons")
public class MainController {
@RequestMapping(
value = "/detail/{id}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE
)
public ResponseEntity<Persons> getUserDetail(@PathVariable Long id) {
Persons user = personsRepository.findById(id);
return new ResponseEntity<>(user, HttpStatus.OK);
}
}
@RequestMapping(value = "/create", method = RequestMethod.POST, produces = { "application/xml", "application/json" })
public ResponseEntity<Member> create(@RequestBody Member member) {
memberRepository.save(member);
return new ResponseEntity<Member>(member, HttpStatus.OK);
}
package api.config;
import java.io.IOException;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
@Configuration
public class JacksonConfig {
@Bean
@Primary
@ConditionalOnMissingBean(ObjectMapper.class)
public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
ObjectMapper objectMapper = builder.createXmlMapper(false).build();
objectMapper.getSerializerProvider().setNullValueSerializer(new JsonSerializer<Object>() {
@Override
public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
jsonGenerator.writeString("");
}
});
return objectMapper;
}
}
restful 将同时支持 json 和 xml 数据传递
package com.example.api.restful;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.example.api.domain.RecentRead;
import com.example.api.repository.RecentReadRepostitory;
@RestController
@RequestMapping("/restful/article")
public class ArticleRestController {
@Autowired
private RecentReadRepostitory recentReadRepostitory;
@RequestMapping(value = "/recent/read/add/{memberId}/{articleId}", method = RequestMethod.GET, produces = { "application/xml", "application/json" })
public ResponseEntity<RecentRead> recentAdd(@PathVariable long memberId, @PathVariable long articleId) {
RecentRead recentRead = new RecentRead();
recentRead.setMemberId(memberId);
recentRead.setArticleId(articleId);
recentReadRepostitory.save(recentRead);
return new ResponseEntity<RecentRead>(recentRead, HttpStatus.OK);
}
@RequestMapping(value="/recent/read/list/{id}", produces = { "application/xml", "application/json" })
public List<RecentRead> recentList(@PathVariable long id) {
int page = 0;
int limit = 20;
List<RecentRead> recentRead = recentReadRepostitory.findByMemberId(id, new PageRequest(page, limit));
return recentRead;
}
}
开发中发现很多人不适应新的接口方式,有时候只能妥协,这些顽固不化的人需要这样的数据库格式
{
"status":true,
"reason":"登录成功",
"code":1,
"data":{
"id":2,
"name":null,
"sex":null,
"age":0,
"wechat":null,
"mobile":"13113668890",
"picture":null,
"ipAddress":"0:0:0:0:0:0:0:1"
}
}
返回数据必须放在 data 字典中, 而我通常是采用 http status code 来返回状态,返回结果是对象。实现上面的需求我们需要加入一个data成员变量,因为我们不清楚最终要返回什么对象。所以声明为 java.lang.Object
package com.example.api.pojo;
import java.io.Serializable;
public class RestfulResponse implements Serializable {
/**
*
*/
private static final long serialVersionUID = -4045645995352698349L;
private boolean status;
private String reason;
private int code;
private Object data;
public RestfulResponse(boolean status, int code, String reason, Object data) {
this.status = status;
this.code = code;
this.reason = reason;
this.data = data;
}
public boolean isStatus() {
return status;
}
public void setStatus(boolean status) {
this.status = status;
}
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
@Override
public String toString() {
return "RestfulResponse [status=" + status + ", reason=" + reason + ", code=" + code + ", data=" + data + "]";
}
}
Service
public RestfulResponse bindWechat(String mobile, String wechat) {
Member member = memberRepository.findByMobile(mobile);
member.setWechat(wechat);
memberRepository.save(member);
return new RestfulResponse(true, 1, "微信绑定成功", member);
}
Controller
@RequestMapping("/login/sms/{mobile}/{code}")
public RestfulResponse sms(@PathVariable String mobile, @PathVariable String wechat) {
return memberService.bindWechat(mobile, wechat);
}
spring.servlet.multipart.max-file-size=128KB spring.servlet.multipart.max-request-size=128KB spring.http.multipart.enabled=false
RestController
package api.restful;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import api.pojo.RestfulResponse;
@RestController
@RequestMapping("/upload")
public class UploadRestController {
private final static String FOLDER = "/tmp";
public UploadRestController() {
}
@PostMapping("/single")
public RestfulResponse upload(@RequestParam("file") MultipartFile file, RedirectAttributes redirectAttributes) {
if (file.isEmpty()) {
return new RestfulResponse(false, 0, "Please select a file to upload", "");
}
try {
byte[] bytes = file.getBytes();
Path path = Paths.get(FOLDER + "/" + file.getOriginalFilename());
Files.write(path, bytes);
return new RestfulResponse(true, 0, "", path.toString());
} catch (Exception e) {
return new RestfulResponse(false, 0, e.getMessage(), null);
}
}
@PostMapping(value = "/group")
public RestfulResponse group(@RequestParam("files") MultipartFile[] files) {
List<String> filelist = new ArrayList<String>();
try {
for (MultipartFile file : files) {
File tmpfile = new File(FOLDER + "/" + file.getOriginalFilename());
file.transferTo(tmpfile);
filelist.add(tmpfile.getPath());
}
return new RestfulResponse(true, 0, null, filelist);
} catch (Exception e) {
return new RestfulResponse(false, 0, e.getMessage(), null);
}
}
}
由于上传文件名可能存在空格等特殊字符,这里使用UUID替代文件名
@PostMapping(value = "/file")
public RestfulResponse file(@RequestParam("file") MultipartFile[] files) {
List<Object> filelist = new ArrayList<Object>();
try {
for (MultipartFile file : files) {
UUID uuid = UUID.randomUUID();
String filename = String.format("%s/%s.%s", folder, uuid.toString(), this.getExtensionName(filename));
File tmpfile = new File(filename);
String filepath = tmpfile.getPath();
System.out.println(filepath);
file.transferTo(tmpfile);
filelist.add(tmpfile.toString());
}
return new RestfulResponse(true, 0, null, filelist);
} catch (Exception e) {
return new RestfulResponse(false, 0, e.getMessage(), null);
}
}
private String getExtensionName(String filename) {
if ((filename != null) && (filename.length() > 0)) {
int dot = filename.lastIndexOf('.');
if ((dot > -1) && (dot < (filename.length() - 1))) {
return filename.substring(dot + 1);
}
}
return filename;
}
获取文件名及后缀信息
MultipartFile file = new MultipartFile();
String file = file.getOriginalFilename()
获取文件名
MultipartFile file = new MultipartFile();
String fileName = file.getOriginalFilename().substring(0,file.getOriginalFilename().lastIndexOf("."))
获取文件后缀
MultipartFile file = new MultipartFile();
String fileSuffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."))
获取文件类型
MultipartFile file = new MultipartFile();
String fileType = file.getContentType()
获取文件大小
MultipartFile file = new MultipartFile();
String fileSize = file.getSize()
package cn.netkiller.controller;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.nio.charset.StandardCharsets;
@RestController
@Slf4j
@RequestMapping("/download/{appId}/{device}")
public class DownloadController {
@SneakyThrows
@GetMapping("file")
public ResponseEntity<InputStreamResource> download(@RequestParam("filepath") String filepath) {
FileSystemResource file = new FileSystemResource("/tmp/document/" + filepath);
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Disposition", "attachment; filename=" + new String(file.getFilename().getBytes(StandardCharsets.UTF_8), "ISO8859-1"));
return ResponseEntity.ok()
.headers(headers)
.contentLength(file.contentLength())
.contentType(MediaType.parseMediaType("application/octet-stream"))
.body(new InputStreamResource(file.getInputStream()));
}
}
package cn.netkiller.test;
import lombok.SneakyThrows;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
public class Test {
@SneakyThrows
public static void main(String[] args) {
Path path = Paths.get("/netkiller/neo/example.svg");
try {
String mimeType = Files.probeContentType(path);
if (mimeType != null) {
System.out.println("Media Type: " + mimeType); // 输出: image/png
} else {
System.out.println("无法识别的媒体类型");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
下面是一个导出 CSV 文件的例子
@GetMapping("/export")
public void export(HttpServletResponse response) throws IOException {
response.setContentType("application/csv");
// response.setContentType("application/csv;charset=gb18030");
response.setHeader("Content-Disposition", "attachment; filename=\"file.csv\"");
BufferedWriter writer = new BufferedWriter(response.getWriter());
// 需要写入 utf8bom 头否则会出现中文乱码
// byte[] uft8bom = { (byte) 0xef, (byte) 0xbb, (byte) 0xbf };
String bom = new String(new byte[] { (byte) 0xEF, (byte) 0xBB, (byte) 0xBF });
writer.write(bom);
writer.write("A,B,C");
writer.newLine();
tableRepository.findAll().forEach(table -> {
try {
String tmp = String.format("%s,%s,%s", table.getId(), table.getMethod(), table.getMoney());
writer.write(tmp);
writer.newLine();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
});
writer.flush();
writer.close();
}
HTTP RFC 7807 规范:https://www.rfc-editor.org/rfc/rfc7807。这个规范里定义了HTTP API的“问题细节”(Problem Details)内容。用它来携带HTTP错误返回信息,避免自定义新的错误返回格式。
HTTP/1.1 403 Forbidden
Content-Type: application/problem+json
Content-Language: en
{
"status": 403,
"type": "https://bankname.com/common-problems/low-balance",
"title": "You not have enough balance",
"detail": "Your current balance is 30 and you are transterring 50",
"instance": "/account-transfer-service"
}
type: 问题的类型; title: 问题类型描述; status: HTTP状态码; detail: 问题实例描述; instance: URI的内容应该用来描述问题实例,但不是必须的。
@GetMapping("/ProblemDetail/v1/{id}")
public ResponseEntity config(@PathVariable("id") Long id) {
if (id < 100) {
return ResponseEntity.ok(new Member(id, "netkiller"));
} else {
ProblemDetail pd = ProblemDetail.forStatusAndDetail(HttpStatus.NOT_FOUND, "Member id '" + id + "' does no exist");
pd.setType(URI.create("https://www.netkiller.cn/errors/not-found"));
pd.setTitle("Record Not Found");
pd.setProperty("hostname", "www.netkiller.cn");
return ResponseEntity.status(404).body(pd);
}
}
@GetMapping(path = "/ProblemDetail/v2/{id}")
public ResponseEntity getEmployeeById_V3(@PathVariable("id") Long id) {
try {
//somthing threw this exception
throw new NullPointerException("Something was expected but it was null");
} catch (NullPointerException npe) {
ProblemDetail pd = ProblemDetail
.forStatusAndDetail(HttpStatus.INTERNAL_SERVER_ERROR,
"Null Pointer Exception");
pd.setType(URI.create("https://www.netkiller.cn/errors/npe"));
pd.setTitle("Null Pointer Exception");
pd.setProperty("hostname", "www.netkiller.cn");
throw new ErrorResponseException(HttpStatus.NOT_FOUND, pd, npe);
}
}
@PostMapping(path = "/foo", params = {"id", "name=John"})
public ResponseEntity<String> handlePostRequest() {
// 处理请求
return ResponseEntity.ok("Success");
}
package netkiller.json;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import javax.json.*;
public final class Writer {
public static void main(String[] args) {
// TODO Auto-generated method stub
JsonObjectBuilder jsonBuilder = Json.createObjectBuilder();
JsonObjectBuilder addressBuilder = Json.createObjectBuilder();
JsonArrayBuilder phoneNumBuilder = Json.createArrayBuilder();
phoneNumBuilder.add("12355566688").add("0755-2222-3333");
addressBuilder.add("street", "Longhua").add("city", "Shenzhen").add("zipcode", 518000);
jsonBuilder.add("nickname", "netkiller").add("name", "Neo").add("department", "IT").add("role", "Admin");
jsonBuilder.add("phone", phoneNumBuilder);
jsonBuilder.add("address", addressBuilder);
JsonObject jsonObject = jsonBuilder.build();
System.out.println(jsonObject);
try {
// write to file
File file = new File("json.txt");
if (!file.exists()) {
file.createNewFile();
}
OutputStream os = null;
os = new FileOutputStream(file);
JsonWriter jsonWriter = Json.createWriter(os);
jsonWriter.writeObject(jsonObject);
jsonWriter.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
运行后输出
{"nickname":"netkiller","name":"Neo","department":"IT","role":"Admin","phone":["12355566688","0755-2222-3333"],"address":{"street":"Longhua","city":"Shenzhen","zipcode":"518000"}}
package netkiller.json;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonObject;
import javax.json.JsonReader;
import javax.json.JsonValue;
public final class Reader {
public static final String JSON_FILE="json.txt";
public static void main(String[] args) throws IOException {
InputStream fis = new FileInputStream(JSON_FILE);
//create JsonReader object
JsonReader jsonReader = Json.createReader(fis);
//get JsonObject from JsonReader
JsonObject jsonObject = jsonReader.readObject();
//we can close IO resource and JsonReader now
jsonReader.close();
fis.close();
System.out.printf("nickname: %s \n", jsonObject.getString("nickname"));
System.out.printf("name: %s \n", jsonObject.getString("name"));
System.out.printf("department: %s \n",
jsonObject.getString("department"));
System.out.printf("role: %s \n", jsonObject.getString("role"));
JsonArray jsonArray = jsonObject.getJsonArray("phone");
//long[] numbers = new long[jsonArray.size()];
int index = 0;
for(JsonValue value : jsonArray){
//numbers[index++] = Long.parseLong(value.toString());
System.out.printf("phone[%d]: %s \n", index++, value.toString());
}
//reading inner object from json object
JsonObject innerJsonObject = jsonObject.getJsonObject("address");
System.out.printf("address: %s, %s, %d \n",
innerJsonObject.getString("street"),
innerJsonObject.getString("city"),
innerJsonObject.getInt("zipcode"));
}
}
运行结果
nickname: netkiller name: Neo department: IT role: Admin phone[0]: +8612355566688 phone[1]: 0755-2222-3333 address: Longhua, Shenzhen, 518000
package netkiller.json;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.Charset;
import javax.json.*;
public class HttpUrl {
public static void main(String[] args) {
// TODO Auto-generated method stub
String URL = "http://www.example.com/json/2/20/0.html";
// system.out.println("Requeted URL:" + URL);
StringBuilder sb = new StringBuilder();
URLConnection urlConn = null;
InputStreamReader in = null;
try {
URL url = new URL(URL);
urlConn = url.openConnection();
if (urlConn != null)
urlConn.setReadTimeout(60 * 1000);
if (urlConn != null && urlConn.getInputStream() != null) {
in = new InputStreamReader(urlConn.getInputStream(), Charset.defaultCharset());
BufferedReader bufferedReader = new BufferedReader(in);
if (bufferedReader != null) {
int cp;
while ((cp = bufferedReader.read()) != -1) {
sb.append((char) cp);
}
bufferedReader.close();
}
}
in.close();
String jsonString = sb.toString();
//System.out.println(jsonString);
JsonReader reader = Json.createReader(new StringReader(jsonString));
JsonObject jsonObject = reader.readObject();
reader.close();
// System.out.println(jsonObject.size());
for (int i = 0; i < jsonObject.size() - 2; i++) {
JsonObject rowObject = jsonObject.getJsonObject(Integer.toString(i));
// System.out.println(rowObject.toString());
System.out.printf("%s\t%s\t%s\n", rowObject.getJsonString("id"), rowObject.getJsonString("title"),
rowObject.getJsonString("ctime"));
}
} catch (Exception e) {
throw new RuntimeException("Exception while calling URL:" + URL, e);
}
}
}
#序列化时间格式 spring.jackson.date-format=yyyy-MM-dd HH:mm:ss spring.mvc.date-format=yyyy-MM-dd HH:mm:ss #mvc序列化时候时区选择 spring.jackson.time-zone=GMT+8
默认 json 中的时间格式是这样的
"createDate":"2018-09-11T07:34:20.106+0000","updateDate":"2018-09-11T07:34:20.106+0000"
@JsonFormat 可以格式化 json 返回的时间格式。
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
格式化后
"createDate":"2018-09-11 07:42:44","updateDate":"2018-09-11 07:42:44"
解决时区问题,MongoDb 默认使用UTC,显示时间相差8小时
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") private Date createdDate = new Date();
public class Test {
@JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MMM-dd HH:mm:ss z", timezone="EST")
@JsonProperty("pubDate")
private Date recentBookPubDate;
}
public class Test {
@JsonFormat(shape=JsonFormat.Shape.NUMBER)
@JsonProperty("birthDate")
private Date birthDate;
}
{
"birthDate" : 1528702883858
}
package cn.netkiller.json;
public class Member {
private String name;
public Member() {
// TODO Auto-generated constructor stub
}
public Member(String name) {
// TODO Auto-generated constructor stub
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Member [name=" + name + "]";
}
}
package cn.netkiller.json;
import java.io.IOException;
import org.springframework.boot.jackson.JsonComponent;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.node.TextNode;
@JsonComponent
public class Json {
public Json() {
// TODO Auto-generated constructor stub
}
public static class MemberJsonSerializer extends JsonSerializer<Member> {
@Override
public void serialize(Member value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
// TODO Auto-generated method stub
gen.writeStartObject();
gen.writeStringField("member", value.toString());
gen.writeEndObject();
}
}
public static class MemberJsonDeserializer extends JsonDeserializer<Member> {
@Override
public Member deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
// TODO Auto-generated method stub
TreeNode treeNode = p.getCodec().readTree(p);
TextNode member = (TextNode) treeNode.get("member");
return new Member(member.asText());
}
}
}
package cn.netkiller.json.controller;
import cn.netkiller.json.Member;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
*
* @author neo
*/
@RestController
public class SimpleController {
@Autowired
public ObjectMapper objectMapper;
@GetMapping("/")
public String home() throws JsonMappingException, JsonProcessingException {
String json = "{\"name\":\"netkiller\"}";
Member member = objectMapper.readValue(json, Member.class);
System.out.println(member.getName());
return member.getName();
}
}
ObjectMapper mapper = new ObjectMapper();
User user = new User();
//Object to JSON in file
mapper.writeValue(new File("c:\\user.json"), user);
//Object to JSON in String
String jsonInString = mapper.writeValueAsString(user);
//Convert object to JSON string and pretty print
String jsonInString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(user);
Notification notification = new Notification(status, time, summary + info); ObjectMapper objectMapper = new ObjectMapper(); String json = objectMapper.writeValueAsString(notification);
ObjectMapper mapper = new ObjectMapper();
String jsonInString = "{'name' : 'mkyong'}";
//JSON from file to Object
User user = mapper.readValue(new File("c:\\user.json"), User.class);
//JSON from String to Object
User user = mapper.readValue(jsonInString, User.class);
package cn.netkiller;
import java.util.Date;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class ObjectToJSON {
public static void main(String[] args) throws JsonProcessingException {
Test test = new Test("Neo", 123, new Date());
ObjectMapper mapper = new ObjectMapper();
String jsonData = mapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(test);
System.out.println(jsonData);
}
}
package cn.netkiller;
import java.io.IOException;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JSONToObject {
public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException {
String jsonData =
"{"
+"\"name\" : \"Neo\","
+"\"age\" : 12,"
+"\"birthDate\" : \"2019-Jun-11 07:00:46 CST\""
+"}";
ObjectMapper mapper = new ObjectMapper();
Test test = mapper.readValue(jsonData, Test.class);
System.out.println(test.getName()+" | "+test.getBirthDate()+" | "+ test.getAge());
}
}
HashMap<String, HashMap<String, String>> data = new HashMap<String, HashMap<String, String>>();
Gson gson = new Gson();
data = gson.fromJson(jsonString, data.getClass());
Gson gson = new Gson();
Map<String, String> data = new LinkedHashMap<String, String>() {{
put("text", text);
put("scene", "talk");
}};
String json = gson.toJson(data, LinkedHashMap.class);
注意:String json = gson.toJson(data); 这样转换不成功,返回 null,必须指定 class 才能成功 String json = gson.toJson(data, LinkedHashMap.class);
避免接口无序执行,被同时多次执行,同一时间只能有一个请求,请求完毕之后才能进行下一次请求。
@GetMapping("/lock/{id}")
public String lock1(@PathVariable("id") String id) throws InterruptedException {
synchronized (id.intern()) {
log.info(Thread.currentThread().getName() + " 上锁");
Thread.sleep(10000);
log.info(Thread.currentThread().getName() + " 解锁");
}
return Thread.currentThread().getName();
}
使用 ConcurrentHashMap 数据共享
private final Map<String, Object> share = new ConcurrentHashMap<>();
@GetMapping("/share/{id}")
public Map<String, Object> shareTest(@PathVariable("id") String id) throws InterruptedException {
// share.computeIfAbsent(id, k -> new Object());
share.computeIfAbsent(id, key -> {
return new Date();
});
synchronized (share) {
log.info(Thread.currentThread().getName() + " 上锁");
Thread.sleep(1000);
log.info(Thread.currentThread().getName() + " 解锁");
}
return share;
}
SseEmitter 实现,可以对比一下两者的区别
@GetMapping(path="/events", produces=MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter handle() {
SseEmitter emitter = new SseEmitter();
// Save the emitter somewhere..
return emitter;
}
// In some other thread
emitter.send("Hello once");
// and again later on
emitter.send("Hello again");
// and done at some point
emitter.complete();
@GetMapping(value = "emitter", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter emitter() {
SseEmitter sseEmitter = new SseEmitter(300_000L);
sseEmitter.onTimeout(() -> System.out.println("超时"));
sseEmitter.onCompletion(() -> System.out.println("完成"));
try {
sseEmitter.send(SseEmitter.event().id("1").name("test").data("Test"));
} catch (IOException e) {
throw new RuntimeException(e);
}
sseEmitter.complete();
return sseEmitter;
}
@GetMapping("/mvc/sse")
public SseEmitter streamSseMvc() {
SseEmitter emitter = new SseEmitter();
ExecutorService sseMvcExecutor = Executors.newSingleThreadExecutor();
sseMvcExecutor.execute(() -> {
try {
for (int i = 0; true; i++) {
SseEventBuilder event = SseEmitter.event()
.data("SSE MVC - " + LocalTime.now().toString())
.id(String.valueOf(i))
.name("sse event - mvc");
emitter.send(event);
Thread.sleep(1000);
}
} catch (Exception ex) {
emitter.completeWithError(ex);
}
});
return emitter;
}
@RequestMapping("/sse")
public SseEmitter sse() {
SseEmitter emitter = new SseEmitter();
new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
emitter.send(SseEmitter.event()
.id(String.valueOf(i))
.name("message")
.data("Message " + i));
Thread.sleep(1000);
}
emitter.complete();
} catch (Exception e) {
emitter.completeWithError(e);
}
}).start();
return emitter;
}
@RequestMapping("/async")
public ResponseBodyEmitter async() {
ResponseBodyEmitter emitter = new ResponseBodyEmitter();
new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
emitter.send("Message " + i);
Thread.sleep(1000);
}
emitter.complete();
} catch (Exception e) {
emitter.completeWithError(e);
}
}).start();
return emitter;
}
@GetMapping("/events")
public ResponseBodyEmitter handle() {
ResponseBodyEmitter emitter = new ResponseBodyEmitter();
// Save the emitter somewhere..
return emitter;
}
// In some other thread
emitter.send("Hello once");
// and again later on
emitter.send("Hello again");
// and done at some point
emitter.complete();
@GetMapping(value = "sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public ResponseBodyEmitter sse() {
ResponseBodyEmitter responseBodyEmitter = new ResponseBodyEmitter();
CompletableFuture.runAsync(() -> {
List.of("text", "audio", "video", "image").forEach((value) -> {
try {
responseBodyEmitter.send(SseEmitter.event().id(usingRandom(5)).name(value).data(usingRandom(128).concat("\n").concat(usingRandom(128))).comment(usingRandom(128)).build());
} catch (IOException e) {
log.error(e.toString());
responseBodyEmitter.completeWithError(e);
}
});
responseBodyEmitter.complete();
});
return responseBodyEmitter;
}
@RequestMapping("/stream")
public StreamingResponseBody stream(HttpServletResponse response) {
response.setContentType("text/plain");
return outputStream -> {
for (int i = 0; i < 10; i++) {
outputStream.write(("Message " + i + "\n").getBytes());
outputStream.flush();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
};
}
StreamingResponseBody 输出 Raw Data
@GetMapping("/download")
public StreamingResponseBody handle() {
return new StreamingResponseBody() {
@Override
public void writeTo(OutputStream outputStream) throws IOException {
// write...
}
};
}
@GetMapping(value = "sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public ResponseBodyEmitter sse() {
ResponseBodyEmitter responseBodyEmitter = new ResponseBodyEmitter();
CompletableFuture.runAsync(() -> {
List.of("text", "audio", "video", "image").forEach((value) -> {
try {
responseBodyEmitter.send(SseEmitter.event().id(usingRandom(5)).name(value).data(usingRandom(128).concat("\n").concat(usingRandom(128))).comment(usingRandom(128)).build());
} catch (IOException e) {
log.error(e.toString());
responseBodyEmitter.completeWithError(e);
}
});
responseBodyEmitter.complete();
});
return responseBodyEmitter;
}
neo@MacBook-Pro-M2 ~/w/watch (main)> curl -H "Accept: text/event-stream" http://dev.netkiller.cn:8081/test/sse id:GZ1O2 event:text data:amftmGZcWyhniUbDganQLbttIXe0qAoWbEaMwtzqmW7S0a3odWpkGD4hgBC9sOmljSWEArmwpTv1ZY637tz2m8IdnSHzy5OjWKWwdXdG4GfvqTfpSavk3RWt09aS68zM data:DojMvAJhZk7hgoyrQFcx1oXRN78xpxJ6I4k9r7kMfJUBkozpSOsSLmxD2z39ISZkY8rur2mEzDNhzVZAunYC37D5bRW4PNZ0hD7PsAsYXQVxQmOQSODLVUJbJQ5YlmRI :tZ5Q0DsH5hFQDHOoviwpWtX96pGyaPREqtlHdr9HuX8FMTJCBkcEK6yJZyjEIjlhiZ7kZKTWaGslvwOda4pIrGFlA1hWtMj5Bntik8CMSuhGkB3nAXbTJ8eJMTe9CHde id:kpKRH event:audio data:fRNjEj0daux6WaISv3ZrtnWeGFtlG0Q5bvKTRyjmkSBAEbm2enA9Jsxx4H4DzcdlJDQKsG9Md9f1Rh5YlpikbNWRXAiT1Kw9fMgFdWZvRIDuaRBIxwIZ6T3a5NhWwV9c data:OzNJYLBpvRAYNALKKxOmkGuvTLYVscvZS6UYcBX1hKOv0Zx7yKOXgq1vt8LKcluyNNYIoeQsOCbERcEyOLdL8UYqQujeMCXRspa5Gl5SskarbKMlfhobRuydrmaoA3jW :Hye2fybk1dHvLXo5nJBdHXJBRfno6C4N6YFvRo0ch2FilKCuAV7VgoOrQj1Hx0TETFbxRziCNsfHU6GVJ9oy5wdAERVIrGnlEh3K2L2jcG5oU8HbADcHWyZ4mPjja0VA id:I3Hq6 event:video data:IIRcsgPMaxVIkF69JtKz2bHRYfZWF9ydc1kBdpEt4w8J4xezdsDixTb2Jv9ue6F3l8wSGubdw8A77szA65M7VrzKr9NsuRlG5YXzyhioCHDP21vWUttByXAfavRFUaQf data:hOZB3iFwK5159tHAh5Ul2plVreIjHm7WlJGZ0dHoYWLsg6Ft8To3Qtv1Gt1p0TCFLyrASr9COyEup5q9tjVsLUSr75ZWeuDoPNjMfNOH4jBrf1PVtKfT7HNgo8fxt64c :AhbH0VI78vukAUNieNMRoET7lQiY94UQZbSNaM2UqP9R0RYcxPoTE9g0wpyhz3qJbuxoYkWL5sw2bm7jv3nITL3tqSo64IZZRpkpbcD5OBvtqoFe08euNEB34aEFbOXt id:pqmcf event:image data:HKPvvsa2Gut3Pji3DbPXnmWcVGkBHkrIRjaNeaBq0kTl2o0Xo2H7mwYAmZ40lJxlb3cEdvcu2ddYe8mZR9pBOXyl16Sk48UFfRLvirXESw4REb3VWdZVaLCgTeRSgpOC data:Ef2EmTusV8xNTdLrcb3ndKOD23Sz1v8iw98YMqd9TGIQppNvXHmrTv4JYzVOIr7NwOqpID81OyjmGhNkqpG4Ho1FMigw939xHuaY9d6tZHSFqVY3wHjMCFqsNuao4gOW :HhyY8h0Yez0pIKMYNavtTBuAFX8iOuRlGpAntNak4H09mQbHZ5O7Sc5huOjcgL43BQs0V57UTKI2cpSptroxJTRQPQmJwrHGbWzHHqjjB85p4AsSBzF5mWit2eEmDKWu
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
// 被访问的接口:/hello
@GetMapping("/hello")
public String hello() {
return "Hello, Spring Boot!";
}
// 发起访问的接口:/query,通过 forward 访问 /hello
@GetMapping("/query")
public String query() {
// 方式1:直接返回 forward 路径(推荐)
return "forward:/hello";
// 方式2:通过 HttpServletRequest 转发(等效)
/*
return request.getRequestDispatcher("/hello").forward(request, response);
*/
}
}