{"record":{"id":"42d1503e769848a5","repo":"languagetool-org/languagetool","slug":"url-needs-to-be-called-with-post","errorCode":null,"errorMessage":"<url> needs to be called with POST","messagePattern":"<url> needs to be called with POST","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"languagetool-server/src/main/java/org/languagetool/server/ApiV2.java","lineNumber":408,"sourceCode":"          StringWriter sw = new StringWriter();\n          new ObjectMapper().writeValue(sw, DatabaseAccess.getInstance().getExtendedUserInfo(user));\n          sendJson(httpExchange, sw);\n        }\n      } else {\n        throw new IllegalStateException(\"Could not fetch user information\");\n      }\n    }\n  }\n\n  private void ensureGetMethod(HttpExchange httpExchange, String url) {\n    if (!httpExchange.getRequestMethod().equalsIgnoreCase(\"get\")) {\n      throw new BadRequestException(url + \" needs to be called with GET\");\n    }\n  }\n  \n  private void ensurePostMethod(HttpExchange httpExchange, String url) {\n    if (!httpExchange.getRequestMethod().equalsIgnoreCase(\"post\")) {\n      throw new BadRequestException(url + \" needs to be called with POST\");\n    }\n  }\n\n  @NotNull\n  private UserLimits getUserLimits(Map<String, String> parameters, HTTPServerConfig config) {\n    UserLimits limits = ServerTools.getUserLimits(parameters, config);\n    if (limits.getPremiumUid() == null) {\n      throw new BadRequestException(\"This end point needs a user id\");\n    }\n    return limits;\n  }\n\n  private void writeResponse(String fieldName, boolean added, HttpExchange httpExchange) throws IOException {\n    StringWriter sw = new StringWriter();\n    try (JsonGenerator g = factory.createGenerator(sw)) {\n      g.writeStartObject();\n      g.writeBooleanField(fieldName, added);\n      g.writeEndObject();","sourceCodeStart":390,"sourceCodeEnd":426,"githubUrl":"https://github.com/languagetool-org/languagetool/blob/2e990059ce67d5e2a0f7f7ca5d31160c6709df4b/languagetool-server/src/main/java/org/languagetool/server/ApiV2.java#L390-L426","documentation":"ensurePostMethod guards mutating endpoints (/words/add, /words/delete, /users/refresh) that only accept POST. Any other HTTP method triggers a BadRequestException '<url> needs to be called with POST'.","triggerScenarios":"GET to /v2/words/add or /v2/words/delete; DELETE HTTP verb used against /words/delete; link-based or browser navigation hitting the endpoint with GET.","commonSituations":"Clients confusing the word-removal endpoint path ('/words/delete') with the DELETE HTTP method; test harnesses defaulting to GET; firewalls or tools rewriting POST bodies and falling back to GET.","solutions":["Send the request as HTTP POST with parameters in the form-encoded body.","Use POST (not the DELETE verb) for /v2/words/delete — the operation is encoded in the URL, not the method.","Do not call these endpoints via browser address bar or simple GET links; use a client that can POST."],"exampleFix":"// before\ncurl -X DELETE 'https://server/v2/words/delete?username=u&token=t&word=foo'\n// after\ncurl -X POST 'https://server/v2/words/delete' -d 'username=u&token=t&word=foo'","handlingStrategy":"validation","validationCode":"if (method.toUpperCase() !== 'POST') throw new Error(`${url} must be called with POST; put params in the form body`);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use POST (the verb) for /words/add, /words/delete and /users/refresh regardless of path names.","Send parameters as an application/x-www-form-urlencoded POST body.","Do not access mutating endpoints via GET links or the browser address bar."],"tags":["http","rest-api","method-not-allowed","languagetool"],"backgroundTag":"unsupported-operation","analyzedSha":"2e990059ce67d5e2a0f7f7ca5d31160c6709df4b","analyzedAt":"2026-09-06T09:20:17.015Z","contentChangedAt":"2026-09-06T09:20:17.015Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}