dvf/blockchain · error

$

Error message

$"{new HttpResponseMessage(HttpStatusCode.MethodNotAllowed)}"

What it means

The /transactions/new route only accepts POST; any other HTTP method (typically GET) is rejected with a 405 MethodNotAllowed response. However, the handler stringifies an HttpResponseMessage object, so instead of a real HTTP 405 status the server returns 200 OK whose body is the object's ToString() dump (e.g. 'StatusCode: 405, ReasonPhrase: Method Not Allowed'). Clients expecting JSON fail to parse the body and standard HTTP error handling never fires.

Solutions

  1. Send the request with method POST and a JSON transaction body
  2. Fix the server to set the real status code on the HttpListenerResponse (response.StatusCode = 405) instead of interpolating an HttpResponseMessage into the returned string
  3. Add an 'Allow: POST' header to the 405 response so clients learn the supported method
  4. Ensure the client serializes Sender/Recipient/Amount as JSON, since a correct POST still needs a deserializable body

Example fix

// before
if (request.HttpMethod != HttpMethod.Post.Method)
    return $"{new HttpResponseMessage(HttpStatusCode.MethodNotAllowed)}";

// after (server-side, respond with a real 405)
if (request.HttpMethod != HttpMethod.Post.Method)
{
    response.StatusCode = (int)HttpStatusCode.MethodNotAllowed;
    response.Headers["Allow"] = "POST";
    return "Method Not Allowed: use POST for /transactions/new";
}
Defensive patterns

Strategy: validation

Validate before calling

// client-side check before sending
using (var req = new HttpRequestMessage(HttpMethod.Post, "http://localhost:12345/transactions/new"))
{
    req.Content = new StringContent(json, Encoding.UTF8, "application/json");
    // send req; a 405 (or a 'StatusCode: 405' body) means the wrong method was used
}

Type guard

static bool IsPostRequest(string method) =>
    string.Equals(method, "POST", StringComparison.OrdinalIgnoreCase);

Try / catch

try
{
    var resp = await client.PostAsJsonAsync(url, trx);
    if (resp.StatusCode == HttpStatusCode.MethodNotAllowed)
        throw new InvalidOperationException("Use POST for /transactions/new");
}
catch (HttpRequestException ex)
{
    // log/handle the failed request; do not retry with the same verb
}

Prevention

When it happens

Trigger: Calling GET http://localhost:12345/transactions/new (or PUT/DELETE/HEAD) instead of POST with a JSON body like {"Amount":123,"Recipient":"ebeabf5cc1d54abdbca5a8fe9493b479","Sender":"31de2e0ef1cb4937830fcfd5d2b3b24f"}. Opening the URL in a browser or a client configured with the wrong method hits this branch.

Common situations: Testing the endpoint in a browser address bar (always GET); copying a GET-based REST call pattern to a POST-only route; client libraries defaulting to GET when no body is set; Postman collections or API docs with the wrong verb; proxies or health checkers issuing GET probes.


AI-assisted analysis of dvf/blockchain@68066d6482 (2026-09-13). Data as JSON: /api/errors/d90e69b870d22dfc. Report an issue: GitHub.

Appendix: source

Thrown at csharp/BlockChain/WebServer.cs:39

                    string json = "";
                    if (path.Contains("?"))
                    {
                        string[] parts = path.Split('?');
                        path = parts[0];
                        query = parts[1];
                    }

                    switch (path)
                    {
                        //GET: http://localhost:12345/mine
                        case "/mine":
                            return chain.Mine();

                        //POST: http://localhost:12345/transactions/new
                        //{ "Amount":123, "Recipient":"ebeabf5cc1d54abdbca5a8fe9493b479", "Sender":"31de2e0ef1cb4937830fcfd5d2b3b24f" }
                        case "/transactions/new":
                            if (request.HttpMethod != HttpMethod.Post.Method)
                                return $"{new HttpResponseMessage(HttpStatusCode.MethodNotAllowed)}";

                            json = new StreamReader(request.InputStream).ReadToEnd();
                            Transaction trx = JsonConvert.DeserializeObject<Transaction>(json);
                            int blockId = chain.CreateTransaction(trx.Sender, trx.Recipient, trx.Amount);
                            return $"Your transaction will be included in block {blockId}";

                        //GET: http://localhost:12345/chain
                        case "/chain":
                            return chain.GetFullChain();

                        //POST: http://localhost:12345/nodes/register
                        //{ "Urls": ["localhost:54321", "localhost:54345", "localhost:12321"] }
                        case "/nodes/register":
                            if (request.HttpMethod != HttpMethod.Post.Method)
                                return $"{new HttpResponseMessage(HttpStatusCode.MethodNotAllowed)}";

                            json = new StreamReader(request.InputStream).ReadToEnd();
                            var urlList = new { Urls = new string[0] };

View on GitHub (pinned to 68066d6482)