Restlet 为我发出的每个 POST 请求返回 HTTP 415
Restlet is returning HTTP 415 for every POST request I make
我每次尝试 post 数据到我的服务器时都会收到此错误:
服务器日志:
Starting the internal [HTTP/1.1] server on port 9192
Starting facilitymanager.api.rest.FacilityManagerAPIRestWrapper application
2015-06-22 13:18:11 127.0.0.1 - - 9192 POST /devices/rename - 415 554 45 64 http://localhost:9192 Java/1.7.0_79 -
Stopping the internal server
但是在服务处理程序中,我声明我将处理 JSON 消息,如您在此处所见:
public static final class RenameDevice extends ServerResource {
@Post("application/json")
public String doPost() throws InterruptedException, ConstraintViolationException, InvalidChoiceException, JSONException {
configureRestForm(this);
final String deviceId = getRequest().getAttributes().get("device_id").toString();
final String newName = getRequest().getAttributes().get("new_name").toString();
return renameDevice(deviceId, newName).toString(4);
}
}
/**
* Enables incoming connections from different servers.
*
* @param serverResource
* @return
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
private static Series<Header> configureRestForm(ServerResource serverResource) {
Series<Header> responseHeaders = (Series<Header>) serverResource.getResponse().getAttributes()
.get("org.restlet.http.headers");
if (responseHeaders == null) {
responseHeaders = new Series(Header.class);
serverResource.getResponse().getAttributes().put("org.restlet.http.headers", responseHeaders);
}
responseHeaders.add("Access-Control-Allow-Origin", "*");
responseHeaders.add("Access-Control-Allow-Methods", "GET, POST, PUT, OPTIONS");
responseHeaders.add("Access-Control-Allow-Headers", "Content-Type");
responseHeaders.add("Access-Control-Allow-Credentials", "false");
responseHeaders.add("Access-Control-Max-Age", "60");
return responseHeaders;
}
我在这里错过了什么?
谢谢!
编辑: 这是关于请求的完整日志:
Processing request to: "http://localhost:9192/devices/rename"
Call score for the "org.restlet.routing.VirtualHost@54594d1d" host: 1.0
Default virtual host selected
Base URI: "http://localhost:9192". Remaining part: "/devices/rename"
Call score for the "" URI pattern: 0.5
Selected route: "" -> facilitymanager.api.rest.FacilityManagerAPIRestWrapper@d75d3d7
Starting facilitymanager.api.rest.FacilityManagerAPIRestWrapper application
No characters were matched
Call score for the "/devices/list" URI pattern: 0.0
Call score for the "/groups/rename" URI pattern: 0.0
Call score for the "/devices/rename" URI pattern: 1.0
Selected route: "/devices/rename" -> Finder for RenameDevice
15 characters were matched
New base URI: "http://localhost:9192/devices/rename". No remaining part to match
Delegating the call to the target Restlet
Total score of variant "[text/html]"= 0.25
Total score of variant "[application/xhtml+xml]"= 5.0E-4
Converter selected for StatusInfo: StatusInfoHtmlConverter
2015-06-22 13:28:31 127.0.0.1 - - 9192 POST /devices/rename - 415 554 45 67 http://localhost:9192 Java/1.7.0_79 -
POST /devices/rename HTTP/1.1 [415 Unsupported Media Type] ()
KeepAlive stream used: http://localhost:9192/devices/rename
sun.net.www.MessageHeader@2bf4dee76 pairs: {null: HTTP/1.1 415 Unsupported Media Type}{Content-type: text/html; charset=UTF-8}{Content-length: 554}{Server: Restlet-Framework/3.0m1}{Accept-ranges: bytes}{Date: Mon, 22 Jun 2015 12:28:31 GMT}
要获得完整的日志,必须在打开 restlet/component 服务器之前在任何地方调用这行代码:
// Create a new Component.
component = new Component();
// Add a new HTTP server listening on default port.
component.getServers().add(Protocol.HTTP, SERVER_PORT);
Engine.setLogLevel(Level.ALL); /// <----- HERE
component.start();
我找到问题了!问题是标记的@Post 方法 必须接收参数.
所以方法应该是这样的:
@Post("application/json")
public String doPost(Representation entity) throws InterruptedException, ConstraintViolationException,
InvalidChoiceException, JSONException, IOException {
configureRestForm(this);
final Reader r = entity.getReader();
StringBuffer sb = new StringBuffer();
int c;
// Reads the JSON from the input stream
while ((c = r.read()) != -1) {
sb.append((char) c);
}
System.out.println(sb.toString()); // Shows the JSON received
}
}
表示实体 参数为您提供了检测正在接收的媒体类型的方法。但是因为我有像 @Post("application/json") 这样的标签,所以我不需要再次验证它。
假设我只使用 "@Post" 而不是 "@Post("application/json")" ,我必须以这种方式验证媒体类型(或多种类型):
@Post
public Representation doPost(Representation entity)
throws ResourceException {
if (entity.getMediaType().isCompatible(MediaType.APPLICATION_JSON)) {
// ...
}
// ...
}
带有@Post注解的方法不需要接收参数,除非您打算从您的请求中接收负载。
如果要过滤传入表示的媒体类型,请使用"json"快捷方式,如下
@Post("json")
这将阻止您测试表示的媒体类型。
此处提供所有可用快捷方式的列表。他们中的大多数都很容易记住。使用快捷方式(或 "extension" 例如文件扩展名)的主要原因是 "xml" 与多种媒体类型相关(application/xml、text/xml)。
如果您想获取表示的全部内容,只需调用 "getText()" 方法,而不是使用 getReader() 并使用它。
如果你想支持CORS,我建议你使用CorsService(在Restlet Framework 2.3版本中可用。
请注意,存在从请求或响应中获取 headers 的快捷方式,只需调用 "getHeaders()" 方法即可。
请注意,存在获取从 URL 中获取的属性的快捷方式,只需调用“getAttribute(String)”方法即可。
这是源代码的更新版本:
public class TestApplication 扩展应用程序 {
public final static class TestPostResource extends ServerResource {
@Post
public String doPost(Representation entity) throws Exception {
final String deviceId = getAttribute("device_id");
final String newName = getAttribute("new_name");
System.out.println(entity.getText());
System.out.println(getRequest().getHeaders());
System.out.println(getResponse().getHeaders());
return deviceId + "/" + newName;
}
}
public static void main(String[] args) throws Exception {
Component c = new Component();
c.getServers().add(Protocol.HTTP, 8183);
c.getDefaultHost().attach(new TestApplication());
CorsService corsService = new CorsService();
corsService.setAllowedOrigins(new HashSet<String>(Arrays.asList("*")));
corsService.setAllowedCredentials(true);
corsService.setSkippingResourceForCorsOptions(true);
c.getServices().add(corsService);
c.start();
}
@Override
public Restlet createInboundRoot() {
Router router = new Router(getContext());
router.attach("/testpost/{device_id}/{new_name}", TestPostResource.class);
return router;
}
}
我每次尝试 post 数据到我的服务器时都会收到此错误:
服务器日志:
Starting the internal [HTTP/1.1] server on port 9192
Starting facilitymanager.api.rest.FacilityManagerAPIRestWrapper application
2015-06-22 13:18:11 127.0.0.1 - - 9192 POST /devices/rename - 415 554 45 64 http://localhost:9192 Java/1.7.0_79 -
Stopping the internal server
但是在服务处理程序中,我声明我将处理 JSON 消息,如您在此处所见:
public static final class RenameDevice extends ServerResource {
@Post("application/json")
public String doPost() throws InterruptedException, ConstraintViolationException, InvalidChoiceException, JSONException {
configureRestForm(this);
final String deviceId = getRequest().getAttributes().get("device_id").toString();
final String newName = getRequest().getAttributes().get("new_name").toString();
return renameDevice(deviceId, newName).toString(4);
}
}
/**
* Enables incoming connections from different servers.
*
* @param serverResource
* @return
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
private static Series<Header> configureRestForm(ServerResource serverResource) {
Series<Header> responseHeaders = (Series<Header>) serverResource.getResponse().getAttributes()
.get("org.restlet.http.headers");
if (responseHeaders == null) {
responseHeaders = new Series(Header.class);
serverResource.getResponse().getAttributes().put("org.restlet.http.headers", responseHeaders);
}
responseHeaders.add("Access-Control-Allow-Origin", "*");
responseHeaders.add("Access-Control-Allow-Methods", "GET, POST, PUT, OPTIONS");
responseHeaders.add("Access-Control-Allow-Headers", "Content-Type");
responseHeaders.add("Access-Control-Allow-Credentials", "false");
responseHeaders.add("Access-Control-Max-Age", "60");
return responseHeaders;
}
我在这里错过了什么?
谢谢!
编辑: 这是关于请求的完整日志:
Processing request to: "http://localhost:9192/devices/rename"
Call score for the "org.restlet.routing.VirtualHost@54594d1d" host: 1.0
Default virtual host selected
Base URI: "http://localhost:9192". Remaining part: "/devices/rename"
Call score for the "" URI pattern: 0.5
Selected route: "" -> facilitymanager.api.rest.FacilityManagerAPIRestWrapper@d75d3d7
Starting facilitymanager.api.rest.FacilityManagerAPIRestWrapper application
No characters were matched
Call score for the "/devices/list" URI pattern: 0.0
Call score for the "/groups/rename" URI pattern: 0.0
Call score for the "/devices/rename" URI pattern: 1.0
Selected route: "/devices/rename" -> Finder for RenameDevice
15 characters were matched
New base URI: "http://localhost:9192/devices/rename". No remaining part to match
Delegating the call to the target Restlet
Total score of variant "[text/html]"= 0.25
Total score of variant "[application/xhtml+xml]"= 5.0E-4
Converter selected for StatusInfo: StatusInfoHtmlConverter
2015-06-22 13:28:31 127.0.0.1 - - 9192 POST /devices/rename - 415 554 45 67 http://localhost:9192 Java/1.7.0_79 -
POST /devices/rename HTTP/1.1 [415 Unsupported Media Type] ()
KeepAlive stream used: http://localhost:9192/devices/rename
sun.net.www.MessageHeader@2bf4dee76 pairs: {null: HTTP/1.1 415 Unsupported Media Type}{Content-type: text/html; charset=UTF-8}{Content-length: 554}{Server: Restlet-Framework/3.0m1}{Accept-ranges: bytes}{Date: Mon, 22 Jun 2015 12:28:31 GMT}
要获得完整的日志,必须在打开 restlet/component 服务器之前在任何地方调用这行代码:
// Create a new Component.
component = new Component();
// Add a new HTTP server listening on default port.
component.getServers().add(Protocol.HTTP, SERVER_PORT);
Engine.setLogLevel(Level.ALL); /// <----- HERE
component.start();
我找到问题了!问题是标记的@Post 方法 必须接收参数.
所以方法应该是这样的:
@Post("application/json")
public String doPost(Representation entity) throws InterruptedException, ConstraintViolationException,
InvalidChoiceException, JSONException, IOException {
configureRestForm(this);
final Reader r = entity.getReader();
StringBuffer sb = new StringBuffer();
int c;
// Reads the JSON from the input stream
while ((c = r.read()) != -1) {
sb.append((char) c);
}
System.out.println(sb.toString()); // Shows the JSON received
}
}
表示实体 参数为您提供了检测正在接收的媒体类型的方法。但是因为我有像 @Post("application/json") 这样的标签,所以我不需要再次验证它。
假设我只使用 "@Post" 而不是 "@Post("application/json")" ,我必须以这种方式验证媒体类型(或多种类型):
@Post
public Representation doPost(Representation entity)
throws ResourceException {
if (entity.getMediaType().isCompatible(MediaType.APPLICATION_JSON)) {
// ...
}
// ...
}
带有@Post注解的方法不需要接收参数,除非您打算从您的请求中接收负载。
如果要过滤传入表示的媒体类型,请使用"json"快捷方式,如下
@Post("json")
这将阻止您测试表示的媒体类型。
此处提供所有可用快捷方式的列表。他们中的大多数都很容易记住。使用快捷方式(或 "extension" 例如文件扩展名)的主要原因是 "xml" 与多种媒体类型相关(application/xml、text/xml)。
如果您想获取表示的全部内容,只需调用 "getText()" 方法,而不是使用 getReader() 并使用它。
如果你想支持CORS,我建议你使用CorsService(在Restlet Framework 2.3版本中可用。
请注意,存在从请求或响应中获取 headers 的快捷方式,只需调用 "getHeaders()" 方法即可。
请注意,存在获取从 URL 中获取的属性的快捷方式,只需调用“getAttribute(String)”方法即可。
这是源代码的更新版本:
public class TestApplication 扩展应用程序 {
public final static class TestPostResource extends ServerResource {
@Post
public String doPost(Representation entity) throws Exception {
final String deviceId = getAttribute("device_id");
final String newName = getAttribute("new_name");
System.out.println(entity.getText());
System.out.println(getRequest().getHeaders());
System.out.println(getResponse().getHeaders());
return deviceId + "/" + newName;
}
}
public static void main(String[] args) throws Exception {
Component c = new Component();
c.getServers().add(Protocol.HTTP, 8183);
c.getDefaultHost().attach(new TestApplication());
CorsService corsService = new CorsService();
corsService.setAllowedOrigins(new HashSet<String>(Arrays.asList("*")));
corsService.setAllowedCredentials(true);
corsService.setSkippingResourceForCorsOptions(true);
c.getServices().add(corsService);
c.start();
}
@Override
public Restlet createInboundRoot() {
Router router = new Router(getContext());
router.attach("/testpost/{device_id}/{new_name}", TestPostResource.class);
return router;
}
}