编程求助,IOS的捷径使用POST方法上传json格式的数据,但是服务端没有办法收到

问题描述

关于项目

我上手Java不久,对网络编程不太熟悉,于是做了一个网络同步剪切板的小程序,思路是这样的:在服务端开一个api url,对这个URL使用POST,上传JSON数据以对剪切板做操作,比如如果想要写数据就定义如下的JSON数据:

{
  "action": "write",
  "contents": "Something you wanna upload."
}

就是这样一个简单的小程序,应该能够扩展成历史剪切板之类的功能,不过感觉这样发展下去就是Pocket或者Flomo了,所以还是应该使其保持简单。

遇到的问题

我使用Emacs的request库调试的时候工作正常,Debug的时候能够显示我做了哪些操作,System.out输出如下:

Method: POST
Write from client!
{"code":200,"content":"为啥啊䓍","message":"Success!"}
Method: POST
Read from client!
{"code":200,"content":"为啥啊䓍","message":"Success!"}
Method: POST
Write from client!
{"code":200,"content":"为啥啊䓍","message":"Success!"}

但是如果是从iPad上面,使用捷径里面那个“获取URL内容”用POST方法上传JSON的话却不能完成功能。

更加离奇的是昨天试的时候我在局域网试验发现可以用iPad上传只是服务器不能够上传,但是今天却发现连局域网里面也上传不了了,如上面的Debug记录可见,上面的行为都是我使用Emacs的request发出的,iPad捷径里面做的操作好像根本没有被程序察觉到。

我做的尝试

是Cache的问题吗?

我试了请求的时候要求 no-cache 也还是没有作用,而且每次我上传的东西是不同的,也会触发缓存机制吗?因此我觉得这个可能性不大。

是因为POST的实现问题吗?

POST可以用GET实现,将表单里面的东西放到URL里面,也许就会让我的JSON处理程序无东西可以处理,但是这样说不通,因为我调试的时候定义了doGet方法为返回一个提示用户"Please do POST"的一级标题。

这是我用netcat调试的时候收到的iPad数据:

POST /netcat/ HTTP/1.0
Host: localhost:1453
X-Real-IP: 172.20.10.1
X-Forwarded-For: 172.20.10.1
Connection: close
Content-Length: 37
Content-Type: application/json
User-Agent: BackgroundShortcutRunner/1144.4 CFNetwork/1325.0.1 Darwin/21.1.0
Accept: */*
Accept-Language: zh-CN,zh-Hans;q=0.9
Accept-Encoding: gzip, deflate
Cache-Control: no-cache

{"action":"write","contents":"Hello"}

附上我的nginx的配置

server {
  listen 80;
  server_name 172.20.10.5;
  root /var/www/public;
  location /netcat/ {
    proxy_pass http://localhost:1453;
    proxy_set_header Host $proxy_host; # 修改转发请求头,让8080端口的应用可以受到真实的请求
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  }
}

netcat 监听命令

nc -l localhost 1453

我似乎也遇到过类似问题,用elisp的(url-retrieve)发起的post请求,数据是json形式的,服务器端是php的取不到post数据,即php的全局变量$_POST没有值。

最后解决办法是:服务器端直接读取http请求头的原始数据流,即json数据,将其转换为需要的数据格式(如数组)在处理。

我解决的代码是:$_POST=json_decode( file_get_contents("php://input"),1); , java没有折腾过网络编程不知道如何读取http原始请求头

Java 代码发出来看一下吧

好的,项目在这里

https://github.com/colawithsauce/ClipboardServer

新手项目,可能有些地方会有点匪夷所思。

主要的逻辑

https://github.com/colawithsauce/ClipboardServer/blob/master/src/main/java/cn/colawithsauce/clipboardServer.java 思路是根据用户的要求来初始化不同的类,然后再对此类进行执行操作来将内容输出给用户

ActionHandler 接口类

WriteActionHandler implements ActionHandler

https://github.com/colawithsauce/ClipboardServer/blob/master/src/main/java/ActionHandler/WriteActionHandler.java

代码逻辑应该不算太复杂,上面列出的都是我觉得跟这个问题有关的代码。

意思是硬编码到URL里面并改用GET吗?其实也是一个思路,就是觉得这样不太优雅所以一直不肯去搞,明天试试

iOS的捷径是啥?

就是快捷指令啊

https://support.apple.com/zh-cn/guide/shortcuts/welcome/ios

不是,我就是先判断http请求头的数据流是不是json字符串,如果是就用json_decode()转换成数组,然后数组命名成了$_POST,这个命名可能会造成误解,也许应该换一个。

意思是把json字符串硬编码到url里面吗?这样也不错。

server 的代码看起来没问题。再确认一下你的快捷指令用的没问题吧,可以给https://httpbin.org/post 发送一个请求验证一下,这个接口会把你的请求信息返回来,类似这样

好的,谢谢了。我马上试试 :handshake:

和用Emacs的request发送的请求头的区别只有UserAgent不同,但是如果手动在捷径里面指定UserAgent为curl,也不能解决问题。

解决了,居然是URL的问题,将URL从 https://path/to/url/api/clipboard 在最后加一个斜杠就好了 https://path/to/url/api/clipboard/ 就能够正常发送了。

但是为什么?我更加疑惑了,这两个有什么不同吗?

按照Servlet规范, 容器是不应该 trailing-slash 的,所以 /foo/ 的请求无法匹配 /foo的servlet。

参考这个问题:https://stackoverflow.com/questions/4377541/servlet-mapping-url-pattern-for-urls-with-trailing-slash

servlet规范参考:12章 https://javaee.github.io/servlet-spec/downloads/servlet-4.0/servlet-4_0_FINAL.pdf

2 个赞

谢谢,学习到了!谷歌了一下才知道原来加了 trailing-slash 会有不同的

这问题我也遇到过,有时候要加/,有时候又不能加,还有些api,通过url get方法传参数,在生成参数时字符串最后多了一个分割符&,get参数就不能被解析了,而一般多数网站会忽略掉get参数最后的&,或url最后的/。

理由有很多,但我感觉可能就是程序员偷赖而已,在服务端忘记了多写几行代码判断一下

我们这暗夜模式的特殊格式文字的字体背景颜色不行啊。。。