2

I am used to JAX-RS and would like to have similar comfort when sending requests using Spring MVC and working with the responses, i.e. on the client side inside my tests.

On the server (controller) side I'm quite happy with the automatic conversion, i.e. it suffices to just return an object instance and have JSON in the resulting HTTP response sent to the client.

Could you tell me how to work around the manual process of converting objectInstance to jsonString or vice versa in these snippets? If possible, I'd also like to skip configuring the content type manually.

String jsonStringRequest = objectMapper.writeValueAsString(objectInstance);

ResultActions resultActions = mockMvc.perform(post(PATH)
    .contentType(MediaType.APPLICATION_JSON)
    .content(jsonStringRequest)
)

String jsonStringResponse = resultActions.andReturn().getResponse().getContentAsString();
Some objectInstanceResponse = objectMapper.readValue(jsonStringResponse, Some.class);

For comparison, with JAX-RS client API I can easily send an object using request.post(Entity.entity(objectInstance, MediaType.APPLICATION_JSON_TYPE) and read the response using response.readEntity(Some.class);

C-Otto
  • 5,615
  • 3
  • 29
  • 62

1 Answers1

0

if you have lot's of response objects, you could create some generic JsonToObject mapper-factory. It could be then used to detect the object type from a generic response (all response objects inherit from the same generic class) and respond/log properly from a bad mapping attempt.

I do not have a code example at hand, but as a pseudocode:

public abstract GenericResponse {
  public String responseClassName = null;
  // get/set
}

In the server code, add the name of the actual response object to this class.

The JsonToObject factory

public ConverterFactory<T> {
   private T objectType;
   public ConverterFactory(T type) {
     objectType = type;
   }

  public T convert(String jsonString) {
    // Type check
    GenericResponse genResp = mapper.readValue(result.getResponse().getContentAsString(),
            GenericResponse.class);
    if (objectType.getClass().getSimpleName().equals(genResp.getResponseClassName())) {
      // ObjectMapper code
      return mapper.readValue(result.getResponse().getContentAsString(),
            objectType.class);
    } else {
      // Error handling
    }
  }
}

I think this could be extended to be used with annotation to do more automation magic with the response. (start checking with BeanPostProcessor)

@Component
public class AnnotationWorker implements BeanPostProcessor {

  @Override
  public Object postProcessBeforeInitialization(final Object bean, String name) throws BeansException {
    ReflectionUtils.doWithFields(bean.getClass(), field -> {
        // make the field accessible if defined private
        ReflectionUtils.makeAccessible(field);
        if (field.getAnnotation(MyAnnotation.class) != null) {
            field.set(bean, log);
        }
    });
    return bean;
  }
}

The above code snippet is copied from my current project and it injects to fields, you need to change it so, that it works for methods, eg ... where you may need it.

Having this all implemented may be tricky and can't say it necessarily works even, but it's something to try if you don't mind a bit of educative work.

Ville Myrskyneva
  • 1,560
  • 3
  • 20
  • 35