开发者

ExtJS 4 Spring 3 file upload. Server sends bad response content type

开发者 https://www.devze.com 2023-03-27 01:29 出处:网络
I was creating file upload using ExtJS 4 frontend and Spring 3 as backend. File upload works, but the response from server has wrong content type. When I send {success:true} using Map<String, Objec

I was creating file upload using ExtJS 4 frontend and Spring 3 as backend. File upload works, but the response from server has wrong content type. When I send {success:true} using Map<String, Object> serialized by Jackson, ExtJS returns error

Uncaught Ext.Error: You're trying to decode an invalid JSON String: <pre style="word-wrap: break-word; white-space: pre-wrap;">{"success":true}</pre>

Why is my response wrapped with <pre> tag? I've searched and found out that I should change response type to text/html for example. But changing content type in servlet response didn't help

@RequestMapping(value = "/upload", method = RequestMethod.POST)
public @ResponseBody Map<String, Object> upload(
    FileUpload uploadItem, BindingResult result, HttpServletResponse response) {

    response.setContentType("text/html");

    // File processing   

    Map<String, Object> jsonResult = new HashMap<String, Object>();
    jsonResult.put("success", Boolean.TRUE);
    return jsonResult;
}

When I change return value of upload method to String, everythin开发者_StackOverflow社区g works correctly, but I want to return Map and have it serialized by Jackson

@RequestMapping(value = "/upload", method = RequestMethod.POST)
public @ResponseBody String upload(
    FileUpload uploadItem, BindingResult result, HttpServletResponse response) {

    // File processing   

    return "{success:true}";
}

My Spring configuration

<bean 
    id="stringHttpMessageConverter" 
    class="org.springframework.http.converter.StringHttpMessageConverter">
</bean>
<bean 
    id="jacksonMessageConverter" 
    class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/>  
<bean    
    class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
    <property name="messageConverters">
        <list>
            <ref bean="jacksonMessageConverter"/>
            <ref bean="stringHttpMessageConverter" />
        </list>
    </property>
</bean>

How to tell Spring to return correct content type? Why is response of this method incorrect when response of other methods is interpreted correctly?


You need to set the content-type of response as "text/html". If the content-type is "application/json" will have this problem. It's odd.


You can return boolean if you need only to return value of success :

@RequestMapping(value = "/upload", method = RequestMethod.POST)
public @ResponseBody boolean upload(
    FileUpload uploadItem, BindingResult result, HttpServletResponse response) {

    return true; //or false
}


Well, not really the best solution, but it solves the problem. I've created class, which has Map inside and method for adding parameters into Map. Also there is implemented method toString().

public class ExtJSJsonResponse {

    /** Parameters to serialize to JSON */
    private Map<String, Object> params = new HashMap<String, Object>();

    /**
     * Add arbitrary parameter for JSON serialization. 
     * Parameter will be serialized as {"key":"value"};
     * @param key name of parameter
     * @param value content of parameter
     */
    @JsonIgnore
    public void addParam(String key, Object value) {
        params.put(key, value);
    }

    /**
     * Gets all parameters. Also is annotated with <code>@JsonValue</code>.
     * @return all params with keys as map
     */
    @JsonValue
    public Map<String, Object> getParams() {
        return params;
    }

    /**
     * Returns specified parameter by <code>key</code> as string "key":"value"
     * @param key parameter key 
     * @return  "key":"value" string or empty string when there is no parameter 
     *          with specified key
     */
    private String paramToString(String key) {
        return params.containsKey(key) 
            ? "\"" + key + "\":\"" + params.get(key) + "\""
            : "";
    }

    /**
     * Manually transforms map parameters to JSON string. Used when ExtJS fails 
     * to decode Jackson response. i.e. when uploading file.
     * @return 
     */
    @Override
    @JsonIgnore
    public String toString() {
        StringBuilder sb = new StringBuilder("{");
        String delimiter = "";

        for (String key : params.keySet()) {
            sb.append(delimiter);
            sb.append(paramToString(key));
            delimiter = ",";
        }

        sb.append("}");
        return sb.toString();
    }
}

So when Uncaught Ext.Error: You're trying to decode an invalid JSON String occurs, you simply do this

@RequestMapping(value = "/upload", method = RequestMethod.POST)
public @ResponseBody String upload(
    FileUpload uploadItem, BindingResult result, HttpServletResponse response) {
    ExtJSJsonResponse response = new ExtJSJsonResponse();

    // File processing
    response.addParam("success", true);
    response.addParam("message", "All OK");   

    return response.toString();
}

In other methods which doesn't have problem with serialization you can simply call return response; and it will be automatically serialized.

Method toString() will work only for simple classes such as String. For more complicated classes you'll have to change it.


I think you can use the "produces" attribute of Spring's @RequestMapping annotation:

@RequestMapping(value = "/upload", method = RequestMethod.POST, produces = MediaType.TEXT_HTML_VALUE)
public @ResponseBody Map<String, Object> upload(
    FileUpload uploadItem, BindingResult result, HttpServletResponse response) {

    // File processing   

    Map<String, Object> jsonResult = new HashMap<String, Object>();
    jsonResult.put("success", Boolean.TRUE);
    return jsonResult;
}

In config file, you should make this Content-Type available:

<bean id="jacksonMessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
    <property name="supportedMediaTypes">
        <array>
            <value>text/html</value>
            <value>application/json</value>
        </array>
    </property>
</bean>  

This is available in Spring 3.1.1.RELEASE, maybe in older versions it doesn't work.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号