简体   繁体   中英

RestController method returned Content-Type = application/javascript in Spring 4.3, but application/json in Spring 5.6

I have this method:

    @RequestMapping(value = "/getValuesAsJson", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public ValuesJsonObject getValuesAsJson(@RequestParam final String ownerId, @RequestParam final String searchString, @RequestParam final Long valueTypeId,
                                            @RequestParam(required = false) final boolean extendedSearch, @RequestParam(required = false) final String meta3Filter,
                                            @RequestParam(required = false) final String meta4Filter, @RequestParam(required = false) final String valueFilter,
                                            @RequestParam(required = false) final String searchFields, @RequestParam(required = false) final Boolean distinct) {
        //...Some Code
        final ValuesJsonObject valuesObject = new ValuesJsonObject();
        valuesObject.setValues(values);
            
        return valuesObject;
                                            
                                            
}

This method is used to fetch data that is used for a GWT select box. The response normally looks something like this:

/**/ gwt_jsonp .P0.onSuccess({"values":[{"denotation":"ZFO - Franconia","value":"ZFO - Franconia","description":null,"meta1":null,"meta3":null,"meta4":null}]});

It's some kind of GWT callback thing where JSON data is inside this Javascript function.

The response content-type was application/javascript in Spring 4.3 environment. Now, after the upgrade, the response-type is application/json. I tried to create a header and manually set up a ResponseEntity with a header that has Content-Type = "application/javascript" but still application/json is returned.

在此处输入图像描述

What I've tried so far:

  1. Set @RequestMapping to @RequestMapping(value = "/getValuesAsJson", method = RequestMethod.GET, produces = "text/javascript) -> Response Content-Type in browser turns to text/html

  2. Removed produces tag in RequestMapping entirely -> Response Content-Type in browser turns to application/xml;charset=UTF-8

  3. Tried to manually set the Response headers like that, but that got me a 500 error:

    HttpHeaders headers = new HttpHeaders(); headers.add(HttpHeaders.CONTENT_TYPE, "text/javascript"); return new ResponseEntity<>(valuesObject, headers, HttpStatus.OK);

Edit: Some new findings, When I remove the data from the body and just ad null. then the content-type is set properly, If I have the data in the body. I receive a 500 error. It must have something to do with the payload (but it worked before the Spring update....).

What I then tried was to properly transform the payload into JSON data using ObjectMapper. The response data then looks like normal JSON and the content-type is correctly set to application/javascript:

Response:

{"values":[{"denotation":"ZFO - Franconia","value":"ZFO - Franconia","description":null,"meta1":null,"meta3":null,"meta4":null}]}

BUT the Javascript callback is missing in this response. Question is how to add this back again. I think that's a GWT issue...

I have a solution for my problem.

When we upgraded to Spring 5.6, Spring's AbstractJsonpResponseBodyAdvice was removed. I think this extension class was responsible for setting the correct content-type in our response and converting everything to JSONP. When the class was gone, the wrong content-type was set. So I had to manually set content-type to text/javascript in @RequestMapping, but I also had to provide it in the header of the response.

HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_TYPE, "text/javascript");

I also had to capture the callback address of the JSONP / GWT call in a new parameter in method signature

@RequestMapping(value = "/getValuesAsJson", method = RequestMethod.GET, produces = "text/javascript")
    public ResponseEntity getValuesAsJson(@RequestParam final String ownerId, @RequestParam final String searchString, @RequestParam final Long valueTypeId,
                                          @RequestParam(required = false) final boolean extendedSearch, @RequestParam(required = false) final String meta3Filter,
                                          @RequestParam(required = false) final String meta4Filter, @RequestParam(required = false) final String valueFilter,
                                          @RequestParam(required = false) final String searchFields, @RequestParam(required = false) final Boolean distinct,
                                          @RequestParam(required = true) final String callback) {

And finally I wrapped the Javascript method name around the JSON data:

return ResponseEntity.ok()
        .headers(headers)
        .body(callback + "(" + valuesObjectAsJson + ")");

This was basically the solution someone else here on SA proposed but I can't find the thread again.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM