Spring: Connect 2 Backends, Jersey, Jackson, @ResponseBody, @Produces, Json (de)serialize

1. @ResponseBody, @Produces

不太清楚Spring中,两个backend互相call,返回的值会被如何打包并通过何种协议(应该是http)进行传输。

先总结一下:

1. @Produces, @Consumes可以和@ResponseBody一起使用,我的理解是在打包成http的response和request的时候,还能指定media type。
2. @Produces和@Consumes并不负责serialize或者deserialize POJO -> JSON / JSON -> POJO.而只是指定MessageBodyWriter可以serialize的类型是Json还是plain/text还是其它的,避免了一些歧义,所以有建议说我们任何时候都要用它。
3. 而Jersey应该是利用Jackson或一些其它module进行一些serialize和deserialize的,负责serialize/deserialize的主要部分应该是messageBodyWriter,而messageBodyWriter在Jackson里和一些其它module里也是有实现的。
4. 没有@ResponseBody,但看似仍然serialize的原因(而且加上了header,lastModified之类的等):见1.1.2.1

更详细的见下面

1.1 Spring @ResponseBody

1.1.1 definition

@RequestBody: enabling automatic deserialization of the inbound HttpRequest body onto a Java object.
@ResponseBody: tells a controller that the object returned is automatically serialized into JSON and passed back into the HttpResponse object.

Other definition:
@RequestBody annotation binds the HTTPRequest body to the domain object. Spring framework automatically deserializes incoming HTTPRequest to the Java object using Http Message Converters. We pass the body of the request through a HttpMessageConverter to resolve the method argument depending on the content type of the request.

1.1.2 @RequestMapping(consumes = {“application/json”}) vs @ResponseBody?

1.1.2.1 https://stackoverflow.com/questions/35123835/spring-requestmapping-for-controllers-that-produce-and-consume-json

You shouldn’t need to configure the consumes or produces attribute at all. Spring will automatically serve JSON based on the following factors.

  • The accepts header of the request is application/json
  • @ResponseBody annotated method (or @RestController at class level)
  • Jackson library on classpath

You should also follow Wim’s suggestion and define your controller with the @RestController annotation. This will save you from annotating each request method with @ResponseBody

怪不得我感觉即使我不用@ResponseBody也没用关系,应该是因为我用了@RestController

1.1.2.2 @ResponseBody and @Produce combined?

… Annotate the method with @ResponseBody, and the method parameter with @RequestBody, and it will work (no need for 2 methods).

所以我的理解是,有了@ResponseBody就不用@RequestMapping(consumes = {“application/json”}),也就是说有了@ResponseBody就不用@Produces(APPLICATION_JSON)这样,因为@Consumes(APPLICATION_JSON)和@RequestMapping(consumes = {“application/json”})是等价的

Explanation:

  • First, produces and consumes attributes are used to narrow the mapping types. By default the first HttpMessageConverter found, that matches the media type requested, will be used.

  • Second, client requests a media type by giving the media type in:

    • Accept request header
    • URL sufix (http: //…//some .xml => “application/xml” media type requested)
    • URL format parameter (…/some?format=xls)
  • Third, produces in combination with @ResponseBody will produce the object in the requested media type (nice for GET requests, when you need to send something back to the client), and consumes in combination with @RequestBody will consume the object with the requested media type (nice for POST requests, when you need to get something from the client).

  • Four, when @ResponseBody not used, HttpMessageConverters are not used. Rather ViewResolvers kick in and produce a view (HTML, PDF…), and the return type should follow the rules that accompany ViewResolvers (check default view resolver and InternalResourceViewResolver for more).

1.2 Jersey @Produces(APPLICATION_JSON)

  • You should always declare the @Produces and @Consumes annotations (either at the class level or method level) for the purpose of Content Negotiation and HTTP protocol correctness. Without these annotations, the result will be dependent on the client request and the default behavior of the server (which may be different across implementations), which leads to unpredictable and ambiguous results.

  • With these annotations, we advertise what media types we can produce and consume. On Retrieve (GET) requests, the client should send an Accept header with the media type of the resource they expect back. And on Create requests (PUT, POST), the client should send a Content-Type header telling the server what media type the data is that they are sending. If these headers don’t match what the server is advertised to handle, then the client will get error responses back telling them what the problem is; with a Retrieve request and a non-matching Accept header, the response will be a 406 Not Acceptable. With a Create request and a non-matching Content-Type header, the response will be a 415 Unsupported Media Type.

  • If your resource methods produce JSON as representation of your resources, they should be annotated with @Produces(MediaType.APPLICATION_JSON). As a result, the response will have a Content-Type header indicating the media type of the payload.

  • The media type defined in the @Produces annotation indicates the media type that will be produced by the MessageBodyWriter instances registered in the application. If your application uses Jackson, for example, the JacksonJsonProvider will be used to convert Java objects to JSON documents.

  • Why need @Produce since when comment out the @Produces annotation, it still returns JSON.

1.3 JAX-RS vs Jersey vs Jackson

  • JAX-RS is an specification (just a definition) and Jersey is a JAX-RS implementation.

  • Coming to Jackson, It is just a JSON processor used to marshall and unmarshall objects from Java to JSON. Jersey uses Jackson internally to convert Java objects to JSON and vice versa.

  • Use one JSON provider that integrates with Jersey and it will provide you a MessageBodyWriter implementation. At time of writing, Jersey integrates with the following modules to provide JSON support:

    • MOXy
    • Java API for JSON Processing (JSON-P)
    • Jackson
    • Jettison

1.4 Spring and Jersey annotation cheat sheet

https://technoless.wordpress.com/2013/04/02/spring-rest-vs-jersey-jax-rs/
https://dzone.com/articles/lets-compare-jax-rs-vs-spring-for-rest-endpoints


– @RequestBody (spring) vs method(String input) (jersey) ?
– @ResponseBody (spring) vs method return type (jersey)
– @ExceptionHandler (spring) vs @Provider and ExceptionMapper interface (jersey)
– @RequestMapping(produces = {“application/json”}) @Produces(“application/json”)
– @RequestMapping(consumes = {“application/json”}) @Consumes(“application/json”)

https://dzone.com/articles/lets-compare-jax-rs-vs-spring-for-rest-endpoints

2. A service call another service endpoint

总结: 2.1 & 2.3: 不管Service1返回的是List还是Response,service1所返回的response自动被deserialize了的感觉,只要service2在这里指定了返回类型

Service 1:

@Component
@Path("/base")
@Produces(APPLICATION_JSON)
@Consumes(APPLICATION_JSON)
@Api(value="its functionality", description="")
public class Service1 {
	@GET
	@Path("/myPath/path1")
	@ApiOperation()
	@ApiResponses()
	public List<String> find(){ 
		try {
			List<String> infos = ...
			return infos;
		} catch (Exception e) {
			return null;
		}
	}
	// or: 
	public Response find(){ 
		try {
			List<String> infos = ...
			return Response.status(Response.Status.OK).entity(infos).build();
		} catch (Exception e) {
			return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e).build();
		}
	}
}

Service 2:

@Named
@Path("/base")
@Produces(APPLICATION_JSON)
@Consumes(APPLICATION_JSON)
public interface Client{
	@GET
	@Path("/myPath/path1")
	List<String> find(){
	}
	// or: 
	Response find(){
	}
}

@Component
public class Processor{

	@Inject
	private Client client;
	
	public void process() {
		List<String> infos = client.find();
		
		// or:
		Response response = client.find();
		if (response.getStatus()= 200) {
			Exception error = response.readEntity(Exception.class);
			throw new Exception(error.getMessage());
		}
		List<String> infos = response.readEntity(List.class);
		// is determined by the response type in cliet interface
	}
}

2.1 service 1 return List, service 2 specify return type as list

returned type from service 1: List
received type of service 2: List
service1 Spring run log 返回204,code中service2 没法拿到status code,List为null
service2 返回200,code中service2 没法拿到status code,List为service1正确返回的List

2.2 service 1 return List, service 2 specify return type as Response

returned type from service 1: List
received type of service 2: InBoundJarxResponse
service1 Spring run log 返回204,code中service2 拿到204,error为null
service1 返回200,code中service2 拿到200,List为service1正确返回的List

public void process() {
		Response response = client.find();
		if (response.getStatus()= 200) {  // can get status: 200, 204... corresponding to real http status in service 1
			Exception error = response.readEntity(Exception.class); // error is null
			throw new Exception(error.getMessage());
		}
		List<String> infos = response.readEntity(List.class); // works normally
	}

service2 也可以用 response.getStatus()得到service1返回的status,包括200, 204…是与service1一致的,即使service1返回的是null… 但error是null

2.3 service 1 return Response, service 2 specify return type as list

returned type from service 1: OutBoundJarxResponse
received type of service 2: List
service1 Spring run log 返回500(不再是204),service2直接抛出是500,List断点到不了
service1 返回200,service2 没法拿到status code,List为service1正确返回的List

2.4 service 1 return Response, service 2 specify return type as Response

The returned type from service 1: OutBoundJarxResponse
The received type of service 2: InBoundJarxResponse
service1 Spring run log返回500(不再是204),code中service2 拿到500,error为service1正确返回的error
service1 返回200,code中service2 拿到200,List为service1正确返回的List

public void process() {
		Response response = client.find();
		if (response.getStatus()= 200) {  // can get status, 200, 500... but is not 204, both in service 1 and service 2, the GET/POST status is just 500...
			Exception error = response.readEntity(Exception.class); // error is not null, is the error bind into Jaxs Response
			throw new Exception(error.getMessage());
		}
		List<String> infos = response.readEntity(List.class); // works normally
	}

service2 也可以用 response.getStatus()得到service1返回的status,包括200, 500…是与service1你定义的一致的,比如除了200,剩下的都归为500,那么service1也不会有204出现在Spring的log里… 因此service 2也不会有除了500意外的error code。error不是null,是service bind进Response里的error。

综上,2.4最适合,又能拿到error message,又能拿到List,同时还能catch住service 1的500

你可能感兴趣的:(One,week,Learning,Notes,Learning,spring)