我正在为我们的应用程序开发一个新的RESTful Web服务。

当对某些实体执行GET时,客户端可以请求实体的内容。如果他们想添加一些参数(例如排序列表),可以在查询字符串中添加这些参数。

或者,我希望人们能够在请求主体中指定这些参数。HTTP/1.1似乎并没有明确禁止这一点。这将允许他们指定更多信息,可能会更容易指定复杂的XML请求。

我的问题:

这完全是个好主意吗?HTTP客户端在GET请求中使用请求体时会遇到问题吗?

https://www.rfc-editor.org/rfc/rfc2616


当前回答

我向IETF HTTP工作组提出了这个问题。Roy Fielding(1998年,http://1.1文档的作者)的评论是

“…一个实现将被破坏,无法执行除解析和丢弃该主体之外的任何操作”

RFC 7213(HTTPbis)规定:

“GET请求消息中的有效负载没有定义的语义;”

现在看来很明显,其意图是禁止GET请求体上的语义,这意味着请求体不能用于影响结果。

如果你在GET上包含一个主体,那么有一些代理肯定会以各种方式破坏你的请求。

总之,不要这样做。

其他回答

如果您真的想将可计算的JSON/XML正文发送到web应用程序,那么放置数据的唯一合理位置是使用RFC4648:Base64Encoding和URL和文件名安全字母表编码的查询字符串。当然,您可以只对JSON进行URL编码,并将其放入URL参数的值中,但Base64给出的结果较小。请记住,URL大小有限制,请参阅不同浏览器中URL的最大长度是多少。

您可能会认为Base64的padding=字符可能对URL的参数值有害,但这似乎不是-请参阅以下讨论:http://mail.python.org/pipermail/python-bugs-list/2007-February/037195.html . 但是,您不应该将编码数据放在没有参数名称的位置,因为带填充的编码字符串将被解释为具有空值的参数键。我会用类似的东西_b64=<编码器数据>。

哪个服务器会忽略它?–2012年8月30日21:27

例如,谷歌做得比忽视它更糟糕,它会认为这是一个错误!

用一个简单的netcat自己试试:

$ netcat www.google.com 80
GET / HTTP/1.1
Host: www.google.com
Content-length: 6

1234

(1234内容后面是CR-LF,总共6个字节)

你会得到:

HTTP/1.1 400 Bad Request
Server: GFE/2.0
(....)
Error 400 (Bad Request)
400. That’s an error.
Your client has issued a malformed or illegal request. That’s all we know.

您还可以从Bing、Apple等获得400个Bad Request,这些请求由AkamaiGhost提供。

因此,我不建议对主体实体使用GET请求。

创建Requestfactory类

import java.net.URI;

import javax.annotation.PostConstruct;

import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

@Component
public class RequestFactory {
    private RestTemplate restTemplate = new RestTemplate();

    @PostConstruct
    public void init() {
        this.restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestWithBodyFactory());
    }

    private static final class HttpComponentsClientHttpRequestWithBodyFactory extends HttpComponentsClientHttpRequestFactory {
        @Override
        protected HttpUriRequest createHttpUriRequest(HttpMethod httpMethod, URI uri) {
            if (httpMethod == HttpMethod.GET) {
                return new HttpGetRequestWithEntity(uri);
            }
            return super.createHttpUriRequest(httpMethod, uri);
        }
    }

    private static final class HttpGetRequestWithEntity extends HttpEntityEnclosingRequestBase {
        public HttpGetRequestWithEntity(final URI uri) {
            super.setURI(uri);
        }

        @Override
        public String getMethod() {
            return HttpMethod.GET.name();
        }
    }

    public RestTemplate getRestTemplate() {
        return restTemplate;
    }
}

和@Autowired,这里是一个带有RequestBody的GET请求示例代码

 @RestController
 @RequestMapping("/v1/API")
public class APIServiceController {
    
    @Autowired
    private RequestFactory requestFactory;
    

    @RequestMapping(method = RequestMethod.GET, path = "/getData")
    public ResponseEntity<APIResponse> getLicenses(@RequestBody APIRequest2 APIRequest){
        APIResponse response = new APIResponse();
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        Gson gson = new Gson();
        try {
            StringBuilder createPartUrl = new StringBuilder(PART_URL).append(PART_URL2);
            
            HttpEntity<String> entity = new HttpEntity<String>(gson.toJson(APIRequest),headers);
            ResponseEntity<APIResponse> storeViewResponse = requestFactory.getRestTemplate().exchange(createPartUrl.toString(), HttpMethod.GET, entity, APIResponse.class); //.getForObject(createLicenseUrl.toString(), APIResponse.class, entity);
    
            if(storeViewResponse.hasBody()) {
                response = storeViewResponse.getBody();
            }
            return new ResponseEntity<APIResponse>(response, HttpStatus.OK);
        }catch (Exception e) {
            e.printStackTrace();
            return new ResponseEntity<APIResponse>(response, HttpStatus.INTERNAL_SERVER_ERROR);
        }
        
    }
}

虽然您可以做到这一点,但只要HTTP规范没有明确排除它,我建议避免它,因为人们不希望事情以这种方式工作。HTTP请求链中有许多阶段,虽然它们“大部分”符合HTTP规范,但唯一可以保证的是,它们的行为将与web浏览器传统使用的一样。(我想的是透明代理、加速器、A/V工具包等)

这就是稳健性原则背后的精神。大致上,“接受的内容要自由,发送的内容要保守”,你不想在没有充分理由的情况下突破规范的界限。

然而,如果你有充分的理由,那就去做吧。

您试图实现的目标已经用一种更常见的方法完成了很长时间,这种方法不依赖于在GET中使用有效负载。

您可以简单地构建特定的搜索中介类型,或者如果您希望更具RESTful,可以使用类似OpenSearch的方法,并将请求POST到服务器指示的URI,例如/search。然后,服务器可以生成搜索结果或构建最终URI并使用303重定向。

这具有遵循传统PRG方法的优点,有助于缓存中介缓存结果等。

也就是说,URI无论如何都是针对非ASCII的任何内容进行编码的,application/x-www-form-urlencoded和multipart/form数据也是如此。如果您打算支持ReSTful场景,我建议使用此格式,而不是创建另一种自定义json格式。