英文:
How to send json with GET params via Spring Boot / Tomcat?
问题
所以目前我正在处理一个项目,其中我们有包含“Origin”对象(包含region: String
和country: String
)的产品对象。
我正在尝试创建一个RestController,它可以接受一个可选的Origin对象,并对其执行某些操作(例如记录日志)。
以下是我目前的代码:
@GetMapping("search")
public Page<Wine> getProductByStuff(
@RequestParam(required = false) Origin origin,
/* other attributes */) {
log.info(origin); // 它有一个适当的toString方法。
}
这种方法有两个问题。首先,当我发送像这样的请求时:
http://[...]/search?origin={"region":"blah","country":"UK"}
甚至是转换为HTML编码的字符串,比如:
http://[...]/search?origin={%22region%22:%22blah%22%44%22country%22:%22UK%22}
... 它会显示以下错误:
> 在请求目标[/api/products/search?origin={%22region%22:%22blah%22%44%22country%22:%22DE%22}]中发现无效字符。有效字符在RFC 7230和RFC 3986中有定义。
据我所知,Tomcat仅允许使用大括号 {}
作为有效字符。其他字符我已经替换为HTML编码字符,但仍然无法正常工作。
为了解决这个问题,我做了以下更改:
@Component
public class TomcatWebServerCustomizer
implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
@Override
public void customize(TomcatServletWebServerFactory factory) {
factory.addConnectorCustomizers(connector -> {
connector.setAttribute("relaxedPathChars", "<>[]^`{|},\\");
connector.setAttribute("relaxedQueryChars", "<>[]^`{|},\\");
});
}
}
(请参见这里,注意,connector.setAttribute
方法已被弃用。)
这样修改后,会出现以下错误:
> MethodArgumentConversionNotSupportedException:无法将类型为'java.lang.String'的值转换为所需类型'[censored].backend.model.Origin'。
我的问题是:
- 是否有可能配置Tomcat/Spring,使其能够接受URL参数中的JSON数据?
- 如何在例如Postman中正确格式化请求,使其能够正常工作?目前我只能手动在Postman的参数选项卡中转换特殊字符。
英文:
so currently I'm working on a project where we have product objects which in turn contain "Origin" objects (containing region: String
and country: String
).
What I'm trying to do is a RestController which takes in an optional Origin object and does something with it (e.g. logs it).
This is what I have right now:
@GetMapping("search")
public Page<Wine> getProductByStuff(
@RequestParam(required = false) Origin origin,
/* other attributes */) {
log.info(origin); // it has a proper toString method.
}
There are two problem with this approach. First of all, when I send a request like:
http://[...]/search?origin={"region":"blah","country":"UK"}
or even the html converted string like:
http://[...]/search?origin={%22region%22:%22blah%22%44%22country%22:%22UK%22}
... it says
> Invalid character found in the request target [/api/products/search?origin={%22region%22:%22blah%22%44%22country%22:%22DE%22}]. The valid characters are defined in RFC 7230 and RFC 3986.
Afaik the only valid characters Tomcat has that I need are {}. All others I've replaced with the html encoded chars and it still doesn't work.
What I did to prevent this:
@Component
public class TomcatWebServerCustomizer
implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
@Override
public void customize(TomcatServletWebServerFactory factory) {
TomcatConnectorCustomizer a = null;
factory.addConnectorCustomizers(connector -> {
connector.setAttribute("relaxedPathChars", "<>[\\]^`{|},\"");
connector.setAttribute("relaxedQueryChars", "<>[\\]^`{|},\"");
});
}
}
(See this, which is, by the way, deprecated (at least connector.setAttribute).)
This produced:
> MethodArgumentConversionNotSupportedException: Failed to convert value of type 'java.lang.String' to required type '[censored].backend.model.Origin'.
My questions are:
- (How) is it possible to configure Tomcat/Spring so that they can actually accept json in the url params?
- How would I format it in e.g. Postman so that it would work? Currently I'm just converting special characters by hand in the params tab of Postman.
答案1
得分: 2
- 如果您想将其作为 JSON 查询参数发送,以下是需要执行的操作。
@RestController
public class OriginController {
@GetMapping("/search")
public void getOrigin(@RequestParam(value = "origin", required = false) Origin origin) {
System.out.println(origin);
}
}
- 注册一个转换器
@Component
public class StringToOriginConverter implements Converter<String, Origin> {
ObjectMapper objectMapper = new ObjectMapper();
@Override
public Origin convert(String source) {
try {
return objectMapper.readValue(source, Origin.class);
} catch (JsonProcessingException e) {
// 可以选择在此处抛出自定义错误异常
return null;
}
}
}
注意: 我的回答不会讨论您应该使用 POST
还是 GET
,因为这不是您提出的问题。它只是提供了一种选项,如果您想将一些数据作为查询参数发送。
英文:
- Here is what you need to do if you want to send it as json query param.
@RestController
public class OriginController {
@GetMapping("/search")
public void getOrigin(@RequestParam(value = "origin", required = false)
Origin origin) {
System.out.println(origin);
}
}
- Register a converter
@Component
public class StringToOriginConverter implements
Converter<String, Origin> {
ObjectMapper objectMapper = new ObjectMapper();
@Override
public Origin convert(String source) {
try {
return objectMapper.readValue(source, Origin.class);
} catch (JsonProcessingException e) {
//You could throw some exception here instead for custom error
return null;
}
}
}
Note
My answer is not debating whether you should use POST
or GET
as it is not what you have asked. It is just providing one option if you want to send some payload as query param
答案2
得分: 1
如前所述,不要将 JSON 用作路径参数。
直接使用路径参数,并转换为 Origin
对象。
@GetMapping("search")
public Page<Wine> getProductByStuff(
@RequestParam(required = false) String region,
@RequestParam(required = false) String country, /* other attributes */) {
Origin origin = new Origin(region, country);
log.info(origin); // 它有一个适当的 toString 方法。
}
英文:
As mentioned, don't use JSON as a path parameter.
Directly use path parameters, and convert to Origin
object.
@GetMapping("search")
public Page<Wine> getProductByStuff(
@RequestParam(required = false) String region,
@RequestParam(required = false) String country, /* other attributes */) {
Origin origin = new Origin(region, country);
log.info(origin); // it has a proper toString method.
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论