Android 上的 Restlet 服务器:客户端正在获取空对象

Restlet server on Android: client is getting a null object

我实现了一个简单的 Android Restlet 服务器演示,它可以向 Restlet 客户端提供传感器值。 然而,当 restlet 客户端收到对象时,它是空的。我想没有太多遗漏,因为事务显示它在服务器中正确完成logcat:

D/SensorTemperature: GET temperature: 20.0
W/System.err: 2015-10-23    20:28:37    192.168.2.129   -   -   8080    GET /sensors/temperature    -   200 -   0   198 http://192.168.2.94:8080    Restlet-Framework/2.3.5 -

并且在客户端,ClientResource 打印 (logcat):

ClientProxy for resource: GET http://192.168.2.94:8080/sensors/temperature HTTP/1.1 => HTTP/1.1 - OK (200) - The request has succeeded

此外,当使用 restlet request/response 时,浏览器界面按预期工作(参见下面的“/test”:)。

我遵循了官方的Restlet教程: http://restlet.com/technical-resources/restlet-framework/guide/2.2/introduction/first-steps/first-application

git: https://github.com/restlet/restlet-tutorial/tree/master/modules/org.restlet.tutorial.webapi/src/main/java/org/restlet/tutorial

连同这个例子: http://maxrohde.com/2011/09/02/restlet-quickstart/

代码:

这里是 restlet 服务器:

public class ServerFactory {

    static {
        // Get NIO engines, instead of defaults
        Engine.getInstance().getRegisteredServers().add(new HttpsServerHelper(null));
        Engine.getInstance().getRegisteredServers().add(new HttpServerHelper(null));
        // Engine.setLogLevel(Level.FINEST);
        Engine.getInstance().getRegisteredConverters().add(new JacksonConverter());
    }

    private static Restlet restlet = new Restlet() {
        @Override
        public void handle(Request request, Response response) {
            Date date = new Date();
            float temp = WSDataProvider.getInstance().getTemperature();
            response.setEntity("Hello World!\nTime: " + date + "\nTemp: " + temp, MediaType.TEXT_PLAIN);
        }
    };

    public static Server createServer(int port, final String rootUri) {
        Component component = new Component();

        Server server = component.getServers().add(Protocol.HTTP, port);

        // Attach the sample application.
        component.getDefaultHost().attach("/test", restlet);

        SensorsApplication sensorsApp = new SensorsApplication();
        component.getDefaultHost().attach("/sensors", sensorsApp);

        return server;  // server.start() and server.stop() called via Android buttons
    }
}

而客户:

Thread thread = new Thread(new Runnable(){
    @Override
    public void run() {
        try {
            Engine.getInstance().getRegisteredConverters().add(new JacksonConverter());

            // Initialize the resource proxy.
            final ClientResource cr = new ClientResource("http://192.168.2.94:8080/sensors/temperature");
            final SensorResource resource = cr.wrap(SensorResource.class);

            // Get the remote temperature sensor
            final SensorBase sensorBase = resource.retrieve();
            if (sensorBase != null)
                Log.e(TAG, "sensorBase.getData() = " + sensorBase.data);
            else
                Log.e(TAG, "sensorBase is null !!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
});

thread.start();

SensorBaseclass:(客户端+服务器)

public class SensorBase implements Serializable {

    private static final long serialVersionUID = 1L;

    public static final int TYPE_UNKNOWN = 0;
    public static final int TYPE_TEMPERATURE = 1;
    public static final int TYPE_HUMIDITY = 2;

    public int type;
    public float data;

    public SensorBase(final int type) {
        super();
        this.type = type;
    }
}

传感器资源class:(客户端+服务器)

public interface SensorResource {
    @Get
    public SensorBase retrieve();
}

传感器温度class:

public class SensorTemperature extends ServerResource implements SensorResource {

    private static final String TAG = "SensorTemperature";
    private static volatile SensorBase sensorBase = new SensorBase(SensorBase.TYPE_TEMPERATURE);

    public SensorBase retrieve() {
        float temp = WSDataProvider.getInstance().getTemperature();
        Log.d(TAG, "GET temperature: " + temp);
        sensorBase.data = temp;
        return sensorBase;
    }
}

传感器应用程序class:

public class SensorsApplication extends Application {
    public Restlet createInboundRoot() {
        Router router = new Router(getContext());
        router.attach("/temperature", SensorTemperature.class);
        //TODO add more sensors
        return router;
    }
}

更新

我已通过包含 jackson jar 文件 com.fasterxml.jackson.core.jar 修复了空指针异常。 class 未找到错误仅在 Restlet 客户端使用 Engine.setLogLevel(Level.FINEST); !

时显示

但是,我现在从客户端收到以下异常:

org.restlet.resource.ResourceException: Unprocessable Entity (422) - The server understands the content type of the request entity and the syntax of the request entity is correct but was unable to process the contained instructions

谢谢!

其实不是Restlet的问题,而是Jackson的问题。如果查看 422 错误的根本原因,您会看到:

Caused by: com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class SensorBase]: can not instantiate from JSON object (need to add/enable type information?)
 at [Source: sun.nio.ch.ChannelInputStream@1eb0d79; line: 1, column: 2]
 at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:148)
 at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1071)
 at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:264)
 at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:124)
 at com.fasterxml.jackson.databind.ObjectReader._bindAndClose(ObjectReader.java:1269)
 at com.fasterxml.jackson.databind.ObjectReader.readValue(ObjectReader.java:864)
 at org.restlet.ext.jackson.JacksonRepresentation.getObject(JacksonRepresentation.java:299)
 at org.restlet.ext.jackson.JacksonConverter.toObject(JacksonConverter.java:208)
 at org.restlet.service.ConverterService.toObject(ConverterService.java:229)
 at org.restlet.resource.Resource.toObject(Resource.java:889)
 ... 3 more

Jackson 对你的对象 SensorBase 的反序列化需要一个空构造函数 class:

public class SensorBase implements Serializable {
    private static final long serialVersionUID = 1L;

    public static final int TYPE_UNKNOWN = 0;
    public static final int TYPE_TEMPERATURE = 1;
    public static final int TYPE_HUMIDITY = 2;

    public int type;
    public float data;

    public SensorBase() {

    }

    public SensorBase(final int type) {
        super();
        this.type = type;
    }

    (...)
}

希望对你有帮助, 蒂埃里