如何通过Spring Boot / Tomcat发送带有GET参数的JSON?

huangapple go评论112阅读模式
英文:

How to send json with GET params via Spring Boot / Tomcat?

问题

所以目前我正在处理一个项目,其中我们有包含“Origin”对象(包含region: Stringcountry: String)的产品对象。

我正在尝试创建一个RestController,它可以接受一个可选的Origin对象,并对其执行某些操作(例如记录日志)。

以下是我目前的代码:

@GetMapping("search")
public Page<Wine> getProductByStuff(
        @RequestParam(required = false) Origin origin, 
        /* other attributes */) {
    log.info(origin); // 它有一个适当的toString方法。
}

这种方法有两个问题。首先,当我发送像这样的请求时:

http://[...]/search?origin={&quot;region&quot;:&quot;blah&quot;,&quot;country&quot;:&quot;UK&quot;}

甚至是转换为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(&quot;search&quot;)
	public Page&lt;Wine&gt; 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={&quot;region&quot;:&quot;blah&quot;,&quot;country&quot;:&quot;UK&quot;}

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&lt;TomcatServletWebServerFactory&gt; {

	@Override
	public void customize(TomcatServletWebServerFactory factory) {
		TomcatConnectorCustomizer a = null;
		factory.addConnectorCustomizers(connector -&gt; {
			connector.setAttribute(&quot;relaxedPathChars&quot;, &quot;&lt;&gt;[\\]^`{|},\&quot;&quot;);
			connector.setAttribute(&quot;relaxedQueryChars&quot;, &quot;&lt;&gt;[\\]^`{|},\&quot;&quot;);
		});
	}
}

(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;
        }
    }
}
  • postman 发送请求
    如何通过Spring Boot / Tomcat发送带有GET参数的JSON?

注意: 我的回答不会讨论您应该使用 POST 还是 GET,因为这不是您提出的问题。它只是提供了一种选项,如果您想将一些数据作为查询参数发送。

英文:
  • Here is what you need to do if you want to send it as json query param.
     @RestController
     public class OriginController {

	  @GetMapping(&quot;/search&quot;)
	  public void getOrigin(@RequestParam(value = &quot;origin&quot;, required = false) 
                            Origin origin) {
		System.out.println(origin);
	  }

     }
  • Register a converter
    @Component
   public class StringToOriginConverter implements 
                              Converter&lt;String, Origin&gt; {

      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;
         }

       }
    }
  • Sending from postman
    如何通过Spring Boot / Tomcat发送带有GET参数的JSON?

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(&quot;search&quot;)
public Page&lt;Wine&gt; 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.
}

huangapple
  • 本文由 发表于 2020年7月26日 03:30:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/63092675.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定