问题
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