27 Ocak 2017 Cuma

Sending Date and Time parameter from Android to Spring @RestController action

You may encounter problems while passing Date parameters to Java RESTful Web service because of Date format.  To standardize the date format I send to server with HTTP requests and the date format that I expect in my services I add some extensions to both my client and server applications.

I use Android Annotations to consume RESTful Web Services.

Client Side
Here is a sample REST API client

import org.androidannotations.annotations.rest.Accept;
import org.androidannotations.annotations.rest.Post;
import org.androidannotations.annotations.rest.Rest;
import org.androidannotations.api.rest.MediaType;

@Rest(rootUrl = Constants.Services.UserApi.ServiceBase, converters = {WorkbookHttpMessageConverter.class})
public interface UserApi {

    @Post(Constants.Services.UserApi.ServiceMethods.PostUser)
    @Accept(MediaType.APPLICATION_JSON)
    OperationResult<User> postUser(SaveUserPostRequestModel model);

}



The key point here to set the expected date format is to provide your own converter class. As you can see, here I used my own converter class "WorkbookHttpMessageConverter" which is an extension to GsonHttpMessageConverter :


import com.adresyazilim.workbook.model.Constants;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import org.springframework.http.converter.json.GsonHttpMessageConverter;

import java.util.Date;

public class WorkbookHttpMessageConverter extends GsonHttpMessageConverter {

    public WorkbookHttpMessageConverter()  {
        super(buildGson());
    }

    protected static Gson buildGson() {
        GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.setDateFormat(Constants.DateTimeConstants.DateTimeFormatString);
        gsonBuilder.registerTypeAdapter(Date.classnew WorkbookJsonDateSerializer());
        return gsonBuilder.create();
    }
}


In my converter class, notice that I provided my own DateSerializer which implements both JsonSerializer<Date> and JsonDeserializer<Date>.


import com.adresyazilim.workbook.business.helper.DateTimeUtil;

import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;

import java.lang.reflect.Type;
import java.util.Date;

public class WorkbookJsonDateSerializer implements JsonSerializer<Date>, JsonDeserializer<Date> {

    @Override
    public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext contextthrows JsonParseException {
        JsonObject jsonObject = (JsonObject)json;
        String dateString = jsonObject.get("datetime").getAsString();
        return DateTimeUtil.getDateTimeFromString(dateString);
    }

    @Override
    public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) {
        final JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("datetime", DateTimeUtil.getDateTimeString(src));
        return jsonObject;
    }

}

You can implement DateSerializer and DateDeserializer instead of JsonSerializer and JsonDeserializer. If you do so, your date value will be serialized as a value, not a Json object. No big difference, do as you like. With the serialization strategy I provided here, my date values are serialized like the following.
    
     { "datetime" : "...." }
 
That's all you need to do at client side.


Server Side
At server side I used Spring-MVC. Below is a sample controller method :

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;

import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/user")
public class UserController {

@Autowired
IUserService userService;

@RequestMapping(value = "/save", method = RequestMethod.POST)
public OperationResult<User> saveUser(@Valid @RequestBody SaveUserPostRequestModel requestBody, BindingResult validationResult, HttpServletRequest request, HttpServletResponse response) {
...
}

}


The "SaveUserPostRequestModel" includes the User class inside it. Below I give the User class. Notice the annotations above the Date values.


import java.util.Date;

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

import org.hibernate.validator.constraints.Email;

public class User {

private String id;
private String username;
private String password;

@Size(min = 2, max = 20)
private String name;

@Size(min = 2, max = 20)
private String surname;

@Email
@NotNull
private String email;

private String photoUrl;

@JsonSerialize(using = WorkbookDateSerializer.class)
@JsonDeserialize(using = WorkbookDateDeserializer.class)
private Date lastUpdateTime;

}


I used JsonSerialize and JsonDeserialize annotations of "com.fasterxml.jackson" and provided my own serializer and deserializer classes.

The Deserializer

import java.io.IOException;
import java.util.Date;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;

@JacksonStdImpl
public class WorkbookDateDeserializer extends JsonDeserializer<Date> {

@Override
public Date deserialize(JsonParser jp, DeserializationContext ctxtthrows IOException, JsonProcessingException {
JsonToken token = jp.getCurrentToken();
if (token == JsonToken.START_OBJECT) {
ObjectCodec oc = jp.getCodec();
JsonNode node = oc.readTree(jp);
String dateString = node.get("datetime").asText();
return DateTimeUtil.getDateTimeFromString(dateString);
}

return null;
}
}



The Serializer

import java.io.IOException;
import java.util.Date;

import com.adresyazilim.workbook.backend.util.DateTimeUtil;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JacksonStdImpl;
import com.fasterxml.jackson.databind.ser.std.DateSerializer;

@JacksonStdImpl
public class WorkbookDateSerializer extends DateSerializer {

@Override
public void serialize(Date value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonGenerationException {
jgen.writeStartObject();
jgen.writeStringField("datetime", DateTimeUtil.getDateTimeString(value));
jgen.writeEndObject();
}
}


This way I standardize and take control over the date format expected in the server side and the format sent from the client side.

Hiç yorum yok:

Yorum Gönder