Search code examples
expresswebserverhttpresponserx-swiftrx-cocoa

Need my server to return a response that includes a data error. Need client to see what was wrong with data in request


As it will become quickly apparent, I have never seriously written a webserver before

Here is the current scenario:

  1. Clients make requests to webserver, asking to save some data
  2. Server looks at payload, and makes 2 checks
    a. Is this client banned from saving data?
    b. Does the payload of this data pass a language filter?
  3. Server responds with success, or one of those 2 errors

My endpoint is written with Express in TypeScript

class ChatRequest {
  public uid: string;
  public message: string;
}


export const register = (app: express.Application, deps: dependencies.IDependencies) => {
  app.post("/sendChat", (req: express.Request, res: express.Response) => {
    transformAndValidate(ChatRequest, req.body)
      .then((sendGlobalChatRequest: SendGlobalChatRequest) => {
        const payload = {
          message: sendGlobalChatRequest.message,
          uid: sendGlobalChatRequest.uid
        };

        //Check if uid is banned here

        //Check if payload passes language filter here

        //Save Payload here

        res.sendStatus(200);
      }, (err) => {
        deps.logger.error(err);
        res.sendStatus(503);
      });
  });

I have been using this article for reference: https://hackernoon.com/the-request-sent-bad-data-whats-the-response-94088bd290a

But I think my conclusion is that they are discussing something slightly different.

So from my understanding, I can just make up HTTP codes... so I could just do res.sendStatus(499); if the uid is banned, and maybe res.sendStatus(498); if the payload doesn't pass language filter

Then my client can just read the Int statusCode and quickly determine the failure.

But even though I think I can do that, and it would work, it doesn't seem right?

Should I instead be using a standard HTTP Response Code? https://developer.mozilla.org/en-US/docs/Web/HTTP/Status

And then add in the body of the response, a String or something that my client can parse to determine the error?

The String parsing seems way harder to maintain, but technically seems more "legal" if that makes sense?

What is the best way for me to have a client determine the type of server-side error?


Solution

  • I decided to return 400 with a JSON mapping errors to bools

        if (isProfane(message)) {
          res.status(400).json({messageContentBlocked: true});
        }
    

    In this way the client can receive multiple errors for the request at once, and it's more explicit

    And in case anyone is googling around, I am using RxSwift/RxCocoa

    Here is how I handle the error on the client:

    extension Error {
        var chatMessageBlockedURLError: Bool {
            guard let rxCocoaURLError = self as? RxCocoaURLError else {return false}
            switch rxCocoaURLError {
            case let .httpRequestFailed(response, data):
                guard response.statusCode == 400, let data = data else {return false}
                let decoder = JSONDecoder()
                decoder.dateDecodingStrategy = .millisecondsSince1970
                guard let errors = try? decoder.decode([String:Bool].self, from: data) else {return false}
                return errors["messageContentBlocked"] == true
            default:
                return false
            }
        }
    }