|
Technorati 标记: Java,Spring,Rest,Mongo
使用Spring MVC发布Restful资源不难,读写Mongodb也不难,但是能不能让两者无缝地对接起来呢?也就是说,让Spring MVC来做retrieve/stringify的工作,我们的方法就像这样:
public DBObject createModel(@RequestBody DBObject model)
按照Spring文档的说法,当一个@RequestMapping的方法没有@ResponesBody时,Spring会尝试渲染View;如果方法同时也带了@ResponseBody,Spring会查找当前已经注册的HttpMessageConverter,通过这些Converter得到希望的结果。所以,我们要做的第一步就是实现DBObject的Converter:
package com.narcissu5.util;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.mongodb.util.JSON;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Narcissu5 on 2014/4/5.
*/
public class DBObjectMessageConverter implements HttpMessageConverter {
@Override
public boolean canRead(Class aClass, MediaType mediaType) {
return DBObject.class.isAssignableFrom(aClass);
}
@Override
public boolean canWrite(Class aClass, MediaType mediaType) {
return DBObject.class.isAssignableFrom(aClass);
}
@Override
public List getSupportedMediaTypes() {
List supports = new ArrayList();
supports.add(MediaType.APPLICATION_JSON);
return supports;
}
@Override
public DBObject read(Class
但是。。。。不起作用,还是Stackoverflow靠谱,实际上应该这样注册:
register-defaults表示同时也注册默认的Converter,spring内置了几种Converter:
- ByteArrayHttpMessageConverter converts byte arrays.
- StringHttpMessageConverter converts strings.
- FormHttpMessageConverter converts form data to/from a MultiValueMap.
- SourceHttpMessageConverter converts to/from a javax.xml.transform.Source.
然后是Controller,因为有Converter的帮助,我们不用再费心处理json转换的工作了:
package com.narcissu5.mvc;
import com.mongodb.*;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.net.UnknownHostException;
/**
* Created by Narcissu5 on 2014/4/6.
*/
@Controller
@RequestMapping(value = "/models")
public class ModelController {
@ResponseBody
@RequestMapping(method = RequestMethod.POST)
public DBObject createModel(@RequestBody DBObject model) throws UnknownHostException {
MongoClient client = new MongoClient("localhost");
DBCollection colModels = client.getDB("MyDB").getCollection("models");
colModels.insert(model);
return model;
}
@ResponseBody
@RequestMapping(method = RequestMethod.PUT)
public DBObject updateModel(@RequestBody DBObject model) throws UnknownHostException {
MongoClient client = new MongoClient("localhost");
DBCollection colModels = client.getDB("MyDB").getCollection("models");
colModels.save(model);
return model;
}
@ResponseBody
@RequestMapping(method = RequestMethod.GET)
public DBObject queryModel() throws UnknownHostException {
MongoClient client = new MongoClient("localhost");
DBCollection colModels = client.getDB("MyDB").getCollection("models");
DBCursor cursor = colModels.find();
BasicDBList all = new BasicDBList();
try {
while (cursor.hasNext()) {
all.add(cursor.next());
}
} finally {
cursor.close();
}
return all;
}
@ResponseBody
@RequestMapping(method = RequestMethod.PATCH)
public DBObject getModel(@RequestBody DBObject query) throws UnknownHostException {
MongoClient client = new MongoClient("localhost");
DBCollection colModels = client.getDB("MyDB").getCollection("models");
return colModels.findOne(query);
}
@RequestMapping(method = RequestMethod.DELETE)
public void deleteModel(@RequestBody DBObject std, HttpServletResponse resp) throws UnknownHostException {
MongoClient client = new MongoClient("localhost");
DBCollection colModels = client.getDB("MyDB").getCollection("models");
WriteResult result = colModels.remove(std);
if (result.getN() > 0) {
resp.setStatus(HttpServletResponse.SC_OK);
} else {
resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
}
}
}
注意这里的查找和删除不像一般的Restful使用/models/{id}这样的路径,这是因为在mongodb中。默认的id是对象而非整型,而且官方也不鼓励使用别的主键类型,因此我们通过消息体将包含主键的对象提交上来,相应的查找的方法也从GET换成了PATCH。
最后是测试,使用Powershell:
function Assert($expression,$message){
Write-Host $message -NoNewline
if($expression){
Write-Host "`tSuccess" -ForegroundColor Green
} else {
Write-Host "`tFail" -ForegroundColor Red
}
}
$url = "http://localhost:8080/models"
#POST /models
$exception = $false
$body = ConvertTo-Json @{name=[GUID]::NewGuid().ToString()}
try{
$resp = Invoke-WebRequest -Body $body -ContentType "application/json" -Method Post -Uri $url #-Headers $headers
} catch {
$exception = $true
}
Assert (-not $exception -and ($resp.StatusCode -eq 200)) "POST /models"
#PUT models
$exception = $false
$ret = ConvertFrom-Json ([System.Text.Encoding]::UTF8.GetString($resp.Content))
$ret.name = [DateTime]::Now.ToString()
$body = ConvertTo-Json $ret
try{
$resp = Invoke-WebRequest -Body $body -ContentType "application/json" -Method PUT -Uri $url
} catch {
$exception = $true
}
Assert (-not $exception -and ($resp.StatusCode -eq 200)) "PUT /models"
#GET /models
$exception = $false
try{
$resp = Invoke-WebRequest -Method GET -Uri $url
} catch {
$exception = $true
}
$ret = ConvertFrom-Json ([System.Text.Encoding]::UTF8.GetString($resp.Content))
Assert (-not $exception -and ($ret.Count -gt 0) -and ($resp.StatusCode -eq 200)) "GET /models"
#GET /models
$exception = $false
$body = ConvertTo-Json (@{_id=$ret[0]._id})
try{
$resp = Invoke-WebRequest -Body $body -Method PATCH -Uri $url
} catch {
$exception = $true
}
Assert (-not $exception -and ($resp.StatusCode -eq 200)) "PATCH /models"
#DELETE /models
$exception = $false
try{
$resp = Invoke-WebRequest -Body $body -Method DELETE -Uri $url
} catch {
$exception = $true
}
Assert (-not $exception -and ($resp.StatusCode -eq 200)) "DELETE /models"
打完收功=W= |
|
|