I’m not comfortable with this heavy escaping in my curls
curl -X POST -H "Content-Type:application/json" -d "{"username":"john","password":"12345"}" http://localhost:8100/signup
I read this answer. Single quotes are not accepted
curl -X POST -H "Content-Type:application/json" -d '{"username":"john","password":"12345"}' http://localhost:8100/signup
org.springframework.core.codec.DecodingException:
JSON decoding error: Unexpected character (''' (code 39)):
expected a valid value (JSON String, Number, Array, Object or token 'null', 'true' or 'false')
This doesn’t work as well (the exception is the same)
curl -X POST -H "Content-Type:application/json" -d "{'username':'john','password':'12345'}" http://localhost:8100/signup
My curl version:
curl --version
curl 8.4.0 (Windows) libcurl/8.4.0 Schannel WinIDN
Release-Date: 2023-10-11
Protocols: dict file ftp ftps http https imap imaps pop3 pop3s smtp smtps telnet tftp
Features: AsynchDNS HSTS HTTPS-proxy IDN IPv6 Kerberos Largefile NTLM SPNEGO SSL SSPI threadsafe Unicode UnixSockets
Can I do without that aggressive escaping when executing my curls with JSON payload (without payload files)?
UPD
I debugged and discovered the exception is thrown from com.fasterxml.jackson.core.json.UTF8StreamJsonParser
on a nextToken()
invocation if its _inputBuffer
(a byte array) starts with an apostrophe
I tried to write an MRE. It’s tricky. The class has a ton of parameters, some of them are tricky to instantiate. Here’s my best attempt. It does reproduce the error but since I pass nulls and mocks I can’t be sure I actually mimic the actual case. Basically, if the method reaches the end without recognition of the first character (it has lots of ifs and switch cases), it throws that exception
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.core.io.IOContext;
import com.fasterxml.jackson.core.json.UTF8StreamJsonParser;
import com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer;
import lombok.SneakyThrows;
import org.junit.jupiter.api.Test;
import java.io.InputStream;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.mockito.Mockito.mock;
public class GenericTest {
@Test
@SneakyThrows
void test() {
IOContext context = mock(IOContext.class);
int features = 65537;
InputStream inputStream = null;
ObjectCodec codec = null;
ByteQuadsCanonicalizer canonicalizer = null;
String input = "'{}'";
byte[] inputBuffer = input.getBytes();
int start = 0;
int end = input.length();
byte bytesPreProcessed = 0;
boolean bufferRecycleable = true;
UTF8StreamJsonParser jsonParser = new UTF8StreamJsonParser(
context, features, inputStream, codec,
canonicalizer, inputBuffer, start, end,
bytesPreProcessed, bufferRecycleable
);
assertThatCode(jsonParser::nextToken)
// the exception is translated and rethrown by Spring
.isInstanceOf(JsonParseException.class)
.hasMessageContaining("Unexpected character (''' (code 39))");
}
}
UPD2
I tried Power Shell instead of Command Prompt. I couldn’t make a request even with escaping, Shell threw all kinds of exceptions no matter what I tried
Attempt #1
curl -i -X POST -H "Content-Type:application/json" -d "{"username":"john","password":"12345"}" http://localhost:8100/signup
+ CategoryInfo : InvalidArgument: (:) [Invoke-WebRequest], ParameterBindingException
+ FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.InvokeWebRequestCommand
Attempt #2
curl -i -X POST -H Content-Type:application/json -d {"username":"john","password":"12345"} http://localhost:8100/signup
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : UnexpectedToken
Attempt #3
curl -i -X POST -H Content-Type:application/json -d {username:john,password:12345} http://localhost:8100/signup
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : MissingArgument
Attempt #4
curl -i -X POST -H Content-Type:application/json -d '{"username":"john","password":"12345"}' http://localhost:8100/signup
+ CategoryInfo : InvalidArgument: (:) [Invoke-WebRequest], ParameterBindingException
+ FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.InvokeWebRequestCommand
Attempt #5
curl -i -X POST -H 'Content-Type:application/json' -d '{"username":"john","password":"12345"}' http://localhost:8100/signup
+ CategoryInfo : InvalidArgument: (:) [Invoke-WebRequest], ParameterBindingException
+ FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.InvokeWebRequestCommand
2
Answers
Use a different shell (not cmd.exe or PowerShell), which supports single quotes for quoting command arguments. For example, Bash supports it. Bash is available on Windows e.g. in WSL2 and MSYS2.
This works in Bash:
Alternatively, save the POST data to a file, and ask curl to read it from the file.
Linux Bash
Windows CMD
Windows PowerShell
By adding JSON as a string, there’s unfortunately no other way around it.