Decoding body parameters with Spring

折月煮酒 提交于 2020-01-04 04:41:07

问题


I'm developing a REST API backend with Spring for a Slack App. I was able to receive messages from Slack (the slash commands) but I'm not able to properly receive component interactions (button clicks).

The official documentation says:

Your Action URL will receive a HTTP POST request, including a payload body parameter, itself containing an application/x-www-form-urlencoded JSON string.

therefore I have written the following @RestController:

@RequestMapping(method = RequestMethod.POST, value = "/actions", headers = {"content-type=application/x-www-form-urlencoded"})
public ResponseEntity action(@RequestParam("payload") ActionController.Action action) {
    return ResponseEntity.status(HttpStatus.OK).build();
}

@JsonIgnoreProperties(ignoreUnknown = true)
class Action {

    @JsonProperty("type")
    private String type;

    public Action() {}

    public String getType() {
        return type;
    }

}

however I get the following error:

Failed to convert request element: org.springframework.web.method.annotation.MethodArgumentConversionNotSupportedException: Failed to convert value of type 'java.lang.String' to required type 'controllers.ActionController$Action'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'java.lang.String' to required type 'controllers.ActionController$Action': no matching editors or conversion strategy found

What does it mean, and how to resolve?


回答1:


You receive a string that contains a JSON content. You don't receive a JSON input as application/x-www-form-urlencoded is used as content type and not application/json as stated :

Your Action URL will receive a HTTP POST request, including a payload body parameter, itself containing an application/x-www-form-urlencoded JSON string.

So change the parameter type to String and use Jackson or any JSON library to map the String to your Action class :

@RequestMapping(method = RequestMethod.POST, value = "/actions", headers = {"content-type=application/x-www-form-urlencoded"})
public ResponseEntity action(@RequestParam("payload") String actionJSON) {
    Action action = objectMapper.readValue(actionJSON, Action.class);  
    return ResponseEntity.status(HttpStatus.OK).build();
}

As pvpkiran suggests, you could have replaced @RequestParam by @RequestBody if you could pass the JSON string directly in the body of the POST request, and not as a value of a parameter but it seems that is not the case there.
Indeed by using @RequestBody, the body of the request is passed through an HttpMessageConverter to resolve the method argument.

To answer to your comment, Spring MVC doesn't provide a very simple way to achieve your requirement : mapping the String JSON to your Action class.
But if you really need to automatize this conversion you have a lengthy alternative as stated in the Spring MVC documentation such as Formatters (emphasis is mine) :

Some annotated controller method arguments that represent String-based request input — e.g. @RequestParam, @RequestHeader, @PathVariable, @MatrixVariable, and @CookieValue, may require type conversion if the argument is declared as something other than String.

For such cases type conversion is automatically applied based on the configured converters. By default simple types such as int, long, Date, etc. are supported. Type conversion can be customized through a WebDataBinder, see DataBinder, or by registering Formatters with the FormattingConversionService, see Spring Field Formatting.

By creating a formatter (FormatterRegistry subclass) for your Action class you could add that in the Spring web config as documented :

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        // ... add action formatter here
    }
}

and use it in your parameter declaration :

public ResponseEntity action(@RequestParam("payload") @Action Action actionJ) 
{...}



回答2:


For simplicity, you could use the code block below. @Request body maps the the payload to the Action class. It also validates to make sure that the type is not blank. The @Valid and @NotBlank is from javax.validation package.

    @PostMapping("actions")
public ResponseEntity<?> startApplication(@RequestBody @Valid Action payload) {
    // use your payload here 
    return ResponseEntity.ok('done');
}


class Action {

    @NotBlank
    private String type;

    public Action() {
    }

    public String getType() {
        return type;
    }
}


来源:https://stackoverflow.com/questions/52091841/decoding-body-parameters-with-spring

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!