Class RestApi

java.lang.Object
io.github.grumpystuff.grumpyrest.RestApi

public final class RestApi extends Object
This class is the main Entry point into grumpyrest. It is used by the application to define the REST API in terms of endpoints and supported data types.
  • Constructor Details

    • RestApi

      public RestApi(JsonEngine jsonEngine)
      Constructor.

      No API routes will be registered by default, not even a route for the root path. You can add routes / handlers after construction. You can also add a route that handles all unmatched requests, to implement a custom "not found" response -- but make sure to add this route last, because it should match all requests, and then no other route after than can be matched anymore.

      Unmatched requests: By default, no fallback route for unmatched requests is present. Instead, the API handles such requests internally by sending a standard 404 response that cannot be changed. This is just a formal way to specify that a manually added catch-all route is the one and only way to implement custom "not found" behavior.

      From-string parsers, JSON Type adapters and response factories: After construction, this API object will have default from-string parsers (for path / querystring parameters) and default response factories registered. It will also use a newly created JsonEngine which has default type adapters registered. All of these defaults can be removed to support special cases that, for example, use different rules / formats or require a more lenient parser. In normal cases, the defaults support standard types out of the box and support for application types can be added without removing any of the standard implementations.

      Parameters:
      jsonEngine - the JSON engine to use
  • Method Details

    • addRoute

      public void addRoute(Route route)
      Adds a route to handle requests. This route can only be matched by requests that do not match any previously added route, and takes precedence over any route added later.

      The Route object defines both which requests it will match as well as the handler to invoke for such requests.

      Parameters:
      route - the route to add
    • addComplexRoute

      public void addComplexRoute(HttpMethod method, Path path, ComplexHandler handler)
      Adds a route to handle requests. This route can only be matched by requests that do not match any previously added route, and takes precedence over any route added later.
      Parameters:
      method - the HTTP method to match
      path - the path to match
      handler - the handler to invoke
    • addComplexRoute

      public void addComplexRoute(HttpMethod method, String path, ComplexHandler handler)
      Adds a route to handle requests. This route can only be matched by requests that do not match any previously added route, and takes precedence over any route added later.
      Parameters:
      method - the HTTP method to match
      path - the path to match
      handler - the handler to invoke
    • addRoute

      public void addRoute(HttpMethod method, Path path, SimpleHandler handler)
      Adds a route to handle requests. This route can only be matched by requests that do not match any previously added route, and takes precedence over any route added later.
      Parameters:
      method - the HTTP method to match
      path - the path to match
      handler - the handler to invoke
    • addRoute

      public void addRoute(HttpMethod method, String path, SimpleHandler handler)
      Adds a route to handle requests. This route can only be matched by requests that do not match any previously added route, and takes precedence over any route added later.
      Parameters:
      method - the HTTP method to match
      path - the path to match
      handler - the handler to invoke
    • getRoutes

      public List<Route> getRoutes()
      Returns a snapshot of the currently present routes.
      Returns:
      the routes, as an immutable snapshot
    • registerResponseFactory

      public void registerResponseFactory(ResponseFactory responseFactory)
      Registers a ResponseFactory to support new kinds of response values. This includes exception types for which a specific response shall be generated (by default, exceptions just cause a standard 500 response).

      Note that support for new JSON-able types should not be implemented as a response factory, but by registering a JsonSerializer with the JsonRegistries / JsonSerializerRegistry returned by getJsonEngine() / StructuralJsonEngine.getRegistries(). A custom response factory, OTOH, would be appropriate to send a JSON response (using the JsonEngine implicitly by calling one of the ResponseTransmitter.writeJson(java.lang.Object) methods) together with a custom HTTP status code or custom HTTP headers.

      Parameters:
      responseFactory - the response factory to register
    • getResponseFactoryRegistry

      public ResponseFactoryRegistry getResponseFactoryRegistry()
      Getter method for the registry for custom response factories. See registerResponseFactory(ResponseFactory) for some information on when such a factory helps. Getting the registry itself is necessary when you want to remove the default response factories present after construction.
      Returns:
      the response factory registry
    • registerFromStringParser

      public void registerFromStringParser(FromStringParser parser)
      Registers a from-string parser to support new types of path parameters and querystring parameters. This is needed, for example, to support a custom date format in the path or querystring. Note that you might need to call getFromStringParserRegistry() to remove standard parsers if you want that custom format to be mapped to one of the built-in types such as LocalDate.

      Each from-string parser will only see a single path argument or a single querystring argument. If you have to support whole-path logic, you will have to do so in the handler. If you have to support a custom whole-querystring parser, see getQuerystringParserRegistry().

      Parameters:
      parser - the parser to register
    • getFromStringParserRegistry

      public FromStringParserRegistry getFromStringParserRegistry()
      Getter method for the registry for from-string parsers. See registerFromStringParser(FromStringParser) for some information on when such a parser helps. Getting the registry itself is necessary when you want to remove the default parsers after construction.
      Returns:
      the from-string parser registry
    • getQuerystringParserRegistry

      public QuerystringParserRegistry getQuerystringParserRegistry()
      Getter method for the registry for whole-querystring parsers. Dealing with this registry is rarely needed because it relates to how the whole querystring gets parsed, not how individual fields are parsed! If you want to support custom types for querystring parameters, see registerFromStringParser(FromStringParser) (and possibly getFromStringParserRegistry()) instead.

      The registry returned here is relevant for redefining how the whole querystring is interpreted as individual fields. You will have to deal with it in the following cases:

      • If the querystring uses a custom format instead of the standard key/value list using & and = characters (Note: I just realized this isn't possible right now because that format is imposed by the servlet API. Fortunately, we don't need it yet. If we do, it's time to change the QuerystringParser interface)
      • If the type to parse the whole querystring as cannot use an auto-generated record parser, for example because it cannot be a Java record for some reason
      There is no "registerQuerystringParser()" method just so nobody is confused and thinks that you need to use it to support custom field types.
      Returns:
      the whole querystring parser registry
    • registerSerializer

      public void registerSerializer(JsonSerializer<?> serializer)
      Registers the specified serializer with the JsonEngine.
      Parameters:
      serializer - the serializer to register
    • registerDeserializer

      public void registerDeserializer(JsonDeserializer deserializer)
      Registers the specified deserializer with the JsonEngine.
      Parameters:
      deserializer - the deserializer to register
    • registerDualConverter

      public <T extends JsonSerializer<?> & JsonDeserializer> void registerDualConverter(T converter)
      Registers the specified dual converter with the JsonEngine.
      Type Parameters:
      T - the dual converter type which must implement both JsonSerializer and JsonDeserializer
      Parameters:
      converter - the dual converter to register
    • getJsonEngine

      public JsonEngine getJsonEngine()
      Getter method for the JsonEngine. This method is needed to register custom types for request/response bodies using custom JsonSerializers and JsonDeserializers.

      (We might consider adding convenience methods to register converters here in RestApi)

      Returns:
      the JSON registries
    • seal

      public void seal()
      Seals this API, also sealing all registries used in it.
    • match

      public RouteMatchResult match(RequestCycle requestCycle)
      Matches the specified request cycle against all routes. This will not apply the match result to the request cycle, i.e. not bind path arguments.

      If multiple routes match, then the one that was first added to this API will be returned.

      Parameters:
      requestCycle - the request cycle to match
      Returns:
      if a route matched, the match result for that route. Otherwise null.
    • handle

      public void handle(RequestCycle requestCycle)
      Handles a request cycle. This first matches the request cycle against all routes to find the route that will handle it, then apply information gathered from matching (i.e. the path arguments) to the request cycle. It will then invoke the handler from the matched route to perform application logic and obtain a response value. This response value gets mapped to a response using an appropriate factory. Finally, the response will be transmitted to the client.
      Parameters:
      requestCycle - the request cycle to handle