我正在使用 Eclipse Californium 开发 CoAP 应用程序,我需要使用 URL 传递参数,就像我们在 restful web 服务中所做的那样。是否可以在 californium coap 实施中做到这一点,如果可以,请告诉我该怎么做。前任:
coap://localhost:5683/foo/{fooID}
简短的回答是,你可以做到。
如 JavaDocs 中所述
- 当请求到达服务器时,{@link ServerMessageDeliverer} 在资源树中搜索目标资源。它通过逐个查找目标 URI 的一个元素并在每个元素上调用方法 {@link #getChild(String)} 沿着资源树向下移动。允许覆盖此方法并返回任意资源。例如,这允许使用通配符为 URI 提供服务,或者将对任何子 URI 的请求委托给同一资源。
因此,基本上您必须覆盖DeliverRequest并且可能在org.eclipse.californium.core.server.ServerMessageDeliverer中覆盖findResource方法,以便返回将处理请求的适当资源。此外,还需要分析 Exchange Request UriPath 作为资源句柄GET/PUT/POST/etc 的一部分以获取路径变量(这可以通过使用CoapExchange.advanced().getRequest().getOptions().getUriPath() )
根据 Californium 的源代码,应该很容易覆盖请求传递者的默认行为。
祝你好运!
您可以按照亚历克斯的说法进行覆盖deliverRequest
,但是我的方法是我不预先注册资源树,而是按资源注册资源而不维护层次结构。
public DynamicMessageDeliverer (List<ProxyRes> resources) {
this.resources = resources;
}
public void deliverRequest (final Exchange exchange) {
Request request = exchange.getRequest ();
List<String> path = request.getOptions ().getUriPath ();
final Resource resource = registerResources (path);
if (resource != null) {
executeResource (exchange, resource);
} else {
exchange.sendResponse (new Response (ResponseCode.NOT_FOUND));
throw new RuntimeException ("Did not find resource " + path.toString() + " requested by " + request.getSource()+":"+request.getSourcePort());
}
}
private void executeResource (final Exchange exchange, final Resource resource) {
// Get the executor and let it process the request
Executor executor = resource.getExecutor ();
if (executor != null) {
exchange.setCustomExecutor ();
executor.execute (new Runnable () {
public void run () {
resource.handleRequest (exchange);
}
});
} else {
resource.handleRequest (exchange);
}
}
private Resource registerResources (List<String> list) {
LinkedList<String> path = new LinkedList<String> (list);
String flatRequestedEndpoint = Arrays.toString (path.toArray ());
LinkedList<String> wildcards = new LinkedList <String> ();
ProxyRes retainedResource = null;
for (ProxyRes proxyRes : resources) {
String[] res = proxyRes.getPath ().replaceFirst ("/", "").split ("/");
int length = res.length;
if (length != path.size ()) {
continue;
}
String flatResEndpoint = Arrays.toString (res);
if (flatResEndpoint.equals (flatRequestedEndpoint)) {
retainedResource = proxyRes;
break;
}
boolean match = true;
for (int i = 0; i < length; i ++) {
String str = res[i];
if (str.equals ("*")) {
wildcards.add (path.get (i));
continue;
}
if (!str.equals (path.get (i))) {
match = false;
break;
}
}
if (!match) {
wildcards.clear ();
continue;
}
retainedResource = proxyRes;
break;
}
if (retainedResource == null) {
return null;
}
((AbstractResource)retainedResource.getCoapRes ()).setWildcard (wildcards);
return retainedResource.getCoapRes ();
}
带有步骤的完整答案代码在这里:Eclipse Californium CoAP wildcard as url path
从我目前所见,创建自定义ServerMessageDeliverer
似乎是更复杂的解决方案。实际上,看起来正确的解决方案是覆盖CoapResource#getChild(String)
,因此它返回您想要与该名称关联的资源。在ServerMessageDeliverer
我看来,这更像是实现某种控制器的方式,该控制器在更复杂的环境中传递或分发请求。
对于 URI 的最后一部分是参数的问题,解决方案可能如下所示:
public class UriParameterResource extends CoapResource {
public UriParameterResource() {
super("foo");
}
@Override
public void handleGET(CoapExchange exchange) {
List<String> uriPath = exchange.getRequestOptions().getUriPath();
// check if there is a sub-resource given, and if so use it for processing
if (uriPath.size() > 1) {
exchange.respond("Process " + uriPath.get(1));
} else {
exchange.respond(ResponseCode.NOT_IMPLEMENTED);
}
}
@Override
public Resource getChild(String name) {
// even sub-resources will land in the GET of this resource
return this;
}
}
关于@Copernic 的回答,我个人认为不符合 REST 的想法。URI 路径的每个部分都应返回与其父级相关的自己的资源,这使其成为每个定义的树结构,而不是简单地检查路径部分作为某种参数的平面列表。
恕我直言,甚至传感器示例也可以通过使用CoapResource
可以动态解析可变子资源的实现来解决。下面的片段只是一个示例,当然这需要取决于房屋及其房间需要以某种方式注册的实际情况。
public class HousesResource extends CoapResource {
public HousesResource() {
super("houses");
}
@Override
public void handleGET(CoapExchange exchange) {
// could return a list of available houses
exchange.respond(ResponseCode.NOT_IMPLEMENTED);
}
@Override
public Resource getChild(String name) {
Resource child = super.getChild(name);
if (child == null) {
child = new HouseResource(name);
add(child);
}
return child;
}
class HouseResource extends CoapResource {
public HouseResource(String name) {
super(name);
add(new RoomsResource());
}
@Override
public void handleGET(CoapExchange exchange) {
exchange.respond(ResponseCode.NOT_IMPLEMENTED);
}
}
class RoomsResource extends CoapResource {
public RoomsResource() {
super("rooms");
}
@Override
public void handleGET(CoapExchange exchange) {
// could return a list of available rooms
exchange.respond(ResponseCode.NOT_IMPLEMENTED);
}
@Override
public Resource getChild(String name) {
Resource child = super.getChild(name);
if (child == null) {
child = new RoomResource(name);
add(child);
}
return child;
}
}
class RoomResource extends CoapResource {
public RoomResource(String roomName) {
super(roomName);
add(new SensorsResource());
}
@Override
public void handleGET(CoapExchange exchange) {
// could return a summary board about the room
exchange.respond(ResponseCode.NOT_IMPLEMENTED);
}
}
class SensorsResource extends CoapResource {
public SensorsResource() {
super("sensors");
add(new TemperatureResource());
}
@Override
public void handleGET(CoapExchange exchange) {
// this could return a list of registered sensors
exchange.respond(ResponseCode.NOT_IMPLEMENTED);
}
}
class TemperatureResource extends CoapResource {
public TemperatureResource() {
super("temperature");
}
@Override
public void handleGET(CoapExchange exchange) {
// as the structure is fixed we know that two levels up
// there is the room, and four levels up there is the house
String room = getParent().getParent().getName();
String house = getParent().getParent().getParent().getParent().getName();
exchange.respond("The temperature of the " + house + " in the " + room + " is : 25 degree");
}
}
}
在该示例中,如果之前不存在资源,则会动态创建它们。这也可以与一些查找或注册机制进行交换(例如,通过 PUT 或 PUSH 注册房屋)。
不要在这里误会我的意思。@Copernic 的解决方案似乎可行,并且可能是某些场景的合适解决方案(例如,每个房子都有自己的服务器,并且需要重定向请求),但对于一个相当简单的场景,它在我看来是不正确的要走的路。