{"id":15350079,"url":"https://github.com/takezoe/resty","last_synced_at":"2025-04-14T23:36:54.397Z","repository":{"id":57723495,"uuid":"74620365","full_name":"takezoe/resty","owner":"takezoe","description":"Super easy REST API framework for Scala","archived":false,"fork":false,"pushed_at":"2019-07-17T17:11:24.000Z","size":2263,"stargazers_count":69,"open_issues_count":4,"forks_count":3,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-03-28T11:42:51.298Z","etag":null,"topics":["hystrix","microservices","scala","swagger","web","webframework","zipkin"],"latest_commit_sha":null,"homepage":null,"language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/takezoe.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-11-23T22:57:16.000Z","updated_at":"2024-03-17T22:43:45.000Z","dependencies_parsed_at":"2022-08-28T14:01:01.972Z","dependency_job_id":null,"html_url":"https://github.com/takezoe/resty","commit_stats":null,"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/takezoe%2Fresty","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/takezoe%2Fresty/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/takezoe%2Fresty/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/takezoe%2Fresty/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/takezoe","download_url":"https://codeload.github.com/takezoe/resty/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248980913,"owners_count":21193140,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["hystrix","microservices","scala","swagger","web","webframework","zipkin"],"created_at":"2024-10-01T11:57:05.660Z","updated_at":"2025-04-14T23:36:54.378Z","avatar_url":"https://github.com/takezoe.png","language":"Scala","funding_links":[],"categories":["开发框架"],"sub_categories":["Web框架"],"readme":"Resty [![Build Status](https://travis-ci.org/takezoe/resty.svg?branch=master)](https://travis-ci.org/takezoe/resty) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.takezoe/resty_2.12/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.github.takezoe/resty_2.12) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/takezoe/resty/blob/master/LICENSE)\n========\n\nSuper easy REST API framework for Scala\n\nYou can run the sample project by hitting following commands:\n\n```\n$ git clone https://github.com/takezoe/resty-sample.git\n$ cd resty-sample/\n$ sbt ~jetty:start\n```\n\nCheck APIs via Swagger UI at: `http://localhost:8080/swagger-ui/`.\n\n## Getting started\n\nThis is a simplest controller example:\n\n```scala\nimport com.github.takezoe.resty._\n\nclass HelloController {\n  @Action(method = \"GET\", path = \"/hello/{name}\")\n  def hello(name: String): Message = {\n    Message(s\"Hello ${name}!\")\n  }\n}\n\ncase class Message(message: String)\n```\n\nDefine a web listener that registers your controller.\n\n```scala\n@WebListener\nclass InitializeListener extends ServletContextListener {\n  override def contextDestroyed(sce: ServletContextEvent): Unit = {\n  }\n  override def contextInitialized(sce: ServletContextEvent): Unit = {\n    Resty.register(new HelloController())\n  }\n}\n```\n\nLet's test this controller.\n\n```\n$ curl -XGET http://localhost:8080/hello/resty\n{\"message\": \"Hello resty!\" }\n```\n\n## Annotations\n\nResty provides some annotations including `@Action`.\n\n### @Controller\n\nYou can add `@Controller` to the controller class to define the controller name and description. They are applied to Swagger JSON.\n\n|parameter   |required |description                   |\n|------------|---------|------------------------------|\n|name        |optional |name of the controller        |\n|description |optional |description of the controller |\n\n```scala\n@Controller(name = \"hello\", description = \"HelloWorld API\")\nclass HelloController {\n  ...\n}\n```\n\n### @Action\n\nWe already looked `@Action` to annotate the action method. It has some more parameters to add more information about the action.\n\n|parameter   |required |description                                          |\n|------------|---------|-----------------------------------------------------|\n|method      |required |GET, POST, PUT or DELETE                             |\n|path        |required |path of the action (`{name}` defines path parameter) |\n|description |optional |description of the method                            |\n|deprecated  |optional |if true then deprecated (default is false)           |\n\n```scala\nclass HelloController {\n  @Action(method = \"GET\", path = \"/v1/hello\", \n    description = \"Old version of HelloWorld API\", deprecated = true)\n  def hello() = {\n    ...\n  }\n}\n```\n\n### @Param\n\n`@Param` is added to the arguments of the action method to define advanced parameter binding.\n\n|parameter   |required |description                                          |\n|------------|---------|-----------------------------------------------------|\n|from        |optional |query, path, header or body                          |\n|name        |optional |parameter or header name (default is arg name)       |\n|description |optional |description of the parameter                         |\n\n```scala\nclass HelloController {\n  @Action(method = \"GET\", path = \"/hello\")\n  def hello(\n    @Param(from = \"query\", name = \"user-name\") userName: String,\n    @Param(from = \"header\", name = \"User-Agent\") userAgent: String\n  ) = {\n    ...\n  }\n}\n```\n\n## Types\n\nResty supports following types as the parameter argument:\n\n- `Unit`\n- `String`\n- `Int`\n- `Long`\n- `Boolean`\n- `Option[T]`\n- `Seq[T]`\n- `Array[T]`\n- `Array[Byte]` (for Base64 encoded string)\n- `AnyRef` (for JSON in the request body)\n\nAlso following types are supported as the return value of the action method:\n\n- `String` is responded as `text/plain; charset=UTF-8`\n- `Array[Byte]`, `InputStream`, `java.io.File` are responded as `application/octet-stream`\n- `AnyRef` is responded as `application/json`\n- `ActionResult[_]` is responded as specified status, headers and body\n- `Future[_]` is processed asynchronously using `AsyncContext`\n\n## Servlet API\n\nYou can access Servlet API by defining method arguments with following types:\n\n- `HttpServletRequest`\n- `HttpServletResponse`\n- `HttpSession`\n- `ServletContext`\n\n```scala\nclass HelloController {\n  @Action(method = \"GET\", path = \"/hello\")\n  def hello(request: HttpServletRequest): Message = {\n    val name = request.getParameter(\"name\")\n    Message(s\"Hello ${name}!\")\n  }\n}\n```\n\n## Validation\n\nIt's possible to validate JSON properties by asserting properties in the constructor of the mapped case class.\n\n```scala\ncase class Message(message: String){\n  assert(message.length \u003c 10, \"message must be less than 10 charactors.\")\n}\n```\n\nWhen the parameter value is invalid, Resty responds the following response with the `400 BadRequest` status:\n\n```javascript\n{\n  \"errors\": [\n    \"message must be less than 10 charactors.\"\n  ]\n}\n```\n\n## HTTP client\n\n`HttpClientSupport` trait offers methods to send HTTP request. You can call other Web APIs easily using these methods.\n\n```scala\nclass HelloController extends HttpClientSupport {\n  @Action(method = \"GET\", path = \"/hello/{id}\")\n  def hello(id: Int): Message = {\n    // Call other API using methods provided by HttpClientSupport\n    val user: User = httpGet[User](s\"http://localhost:8080/user/${id}\")\n    Message(s\"Hello ${user.name}!\")\n  }\n  \n  @Action(method = \"GET\", path = \"/hello-async/{id}\")\n  def helloAsync(id: Int): Future[Message] = {\n    // HttpClientSupport also supports asynchronous communication\n    val future: Future[Either[ErrorModel, User]] = httpGetAsync[User](s\"http://localhost:8080/user/${id}\")\n    future.map {\n      case Right(user) =\u003e Message(s\"Hello ${user.name}!\")\n      case Left(error) =\u003e throw new ActionResultException(InternalServerError(error))\n    }\n  }\n}\n```\n\nThese methods have retrying ability and circuit breaker. You can configure these behavior by defining `HttpClientConfig` as an implicit value.\n\n```scala\nclass HelloController extends HttpClientSupport {\n\n  implicit override val httpClientConfig = HttpClientConfig(\n    maxRetry      = 5,     // max number of retry. default is 0 (no retry)\n    retryInterval = 500,   // interval of retry (msec). default is 0 (retry immediately)\n    maxFailure    = 3,     // max number until open circuit breaker. default is 0 (disabling circuit breaker)\n    resetInterval = 60000  // interval to reset closed circuit breaker (msec). default is 60000\n  )\n  \n  ...\n}\n```\n\n## Swagger integration\n\nResty provides [Swagger](http://swagger.io/) integration in default. Swagger JSON is provided at `http://localhost:8080/swagger.json` and also Swagger UI is available at `http://localhost:8080/swagger-ui/`.\n\n![Swagger integration](swagger.png)\n\nAdd following parameter to `web.xml` to enable Swagger integration:\n\n```xml\n\u003ccontext-param\u003e\n  \u003cparam-name\u003eresty.swagger\u003c/param-name\u003e\n  \u003cparam-value\u003eenable\u003c/param-value\u003e\n\u003c/context-param\u003e\n```\n\nBy enabling [runtime-scaladoc-reader](https://github.com/takezoe/runtime-scaladoc-reader) plugin in your project, Scaladoc of controller classes is reflected to Swagger JSON. For example, Scaladoc of the controller class is used as the tag description, and Scaladoc of the method is used as the operation description, the parameter description and the response description.\n\n```scala\naddCompilerPlugin(\"com.github.takezoe\" %% \"runtime-scaladoc-reader\" % \"1.0.1\")\n```\n\n## Hystrix integration\n\nResty also provides [Hystrix](https://github.com/Netflix/Hystrix) integration in default. Metrics are published for each operations. The stream endpoint is available at `http://localhost:8080/hystrix.stream`. Register this endpoint to the Hystrix dashboard.\n\n![Hystrix integration](hystrix.png)\n\nAdd following parameter to `web.xml` to enable Hystrix integration:\n\n```xml\n\u003ccontext-param\u003e\n  \u003cparam-name\u003eresty.hystrix\u003c/param-name\u003e\n  \u003cparam-value\u003eenable\u003c/param-value\u003e\n\u003c/context-param\u003e\n```\n\n## Zipkin integration\n\nFurthermore, Resty supports [Zipkin](http://zipkin.io/) as well. You can send execution results to the Zipkin server by enabling Zipkin support and using `HttpClientSupport` for calling other APIs.\n\nAdd following parameters to `web.xml` to enable Zipkin integration:\n\n```xml\n\u003ccontext-param\u003e\n  \u003cparam-name\u003eresty.zipkin\u003c/param-name\u003e\n  \u003cparam-value\u003eenable\u003c/param-value\u003e\n\u003c/context-param\u003e\n\u003ccontext-param\u003e\n  \u003cparam-name\u003eresty.zipkin.service.name\u003c/param-name\u003e\n  \u003cparam-value\u003eresty-sample\u003c/param-value\u003e\n\u003c/context-param\u003e\n\u003ccontext-param\u003e\n  \u003cparam-name\u003eresty.zipkin.sample.rate\u003c/param-name\u003e\n  \u003cparam-value\u003e1.0\u003c/param-value\u003e\n\u003c/context-param\u003e\n\u003ccontext-param\u003e\n  \u003cparam-name\u003eresty.zipkin.server.url\u003c/param-name\u003e\n  \u003cparam-value\u003ehttp://127.0.0.1:9411/api/v1/spans\u003c/param-value\u003e\n\u003c/context-param\u003e\n```\n\n## WebJars support\n\n[WebJars](http://www.webjars.org/) is a cool stuff to integrate frontend libraries with JVM based applications. Resty can host static files that provided by WebJars for frontend applications.\n\nAdd a following parameter to `web.xml` to enable WebJars hosting:\n\n```xml\n\u003ccontext-param\u003e\n  \u003cparam-name\u003eresty.wabjars\u003c/param-name\u003e\n  \u003cparam-value\u003eenable\u003c/param-value\u003e\n\u003c/context-param\u003e\n\u003ccontext-param\u003e\n  \u003cparam-name\u003eresty.wabjars.path\u003c/param-name\u003e\n  \u003cparam-value\u003e/public/assets/*\u003c/param-value\u003e\n\u003c/context-param\u003e\n```\n\nYou can add WebJars dependencies in your application as following:\n\n```scala\nlibraryDependencies += \"org.webjars\" %  \"jquery\" % \"3.1.1-1\"\n```\n\nThen import JavaScript library as following:\n\n```html\n\u003cscript src=\"/public/assets/jquery.min.js\" type='text/javascript'\u003e\u003c/script\u003e\n```\n\n## Static files hosting\n\nResty is including some base servlets to host static files. You can provide a frontend application through Resty application from the classpath or the file system by defining following servlet based on these classes.\n\n```scala\n// Host static files on the file system\n@WebServlet(name=\"FileResourceServlet\", urlPatterns=Array(\"/public/*\"))\nclass MyFileResourceServlet extends FileResourceServlet(\"src/main/webapp\")\n\n// Host static files in the classpath\n@WebServlet(name=\"ClasspathResourceServlet\", urlPatterns=Array(\"/public/*\"))\nclass MyClasspathResourceServlet extends ResourceServlet(\"com/github/resty/sample/public\")\n```\n\n## CORS support\n\nCORS support can be enabled by adding following parameters to `web.xml`:\n\n```xml\n\u003ccontext-param\u003e\n  \u003cparam-name\u003eresty.cors\u003c/param-name\u003e\n  \u003cparam-value\u003eenable\u003c/param-value\u003e\n\u003c/context-param\u003e\n\u003ccontext-param\u003e\n  \u003cparam-name\u003eresty.cors.allowedOrigins\u003c/param-name\u003e\n  \u003cparam-value\u003ehttp://localhost:8080\u003c/param-value\u003e\n\u003c/context-param\u003e\n\u003ccontext-param\u003e\n  \u003cparam-name\u003eresty.cors.allowedMethods\u003c/param-name\u003e\n  \u003cparam-value\u003eGET, POST\u003c/param-value\u003e\n\u003c/context-param\u003e\n\u003ccontext-param\u003e\n  \u003cparam-name\u003eresty.cors.allowedHeaders\u003c/param-name\u003e\n  \u003cparam-value\u003eContent-Type\u003c/param-value\u003e\n\u003c/context-param\u003e\n\u003ccontext-param\u003e\n  \u003cparam-name\u003eresty.cors.allowCredentials\u003c/param-name\u003e\n  \u003cparam-value\u003etrue\u003c/param-value\u003e\n\u003c/context-param\u003e\n\u003ccontext-param\u003e\n  \u003cparam-name\u003eresty.cors.preflightMaxAge\u003c/param-name\u003e\n  \u003cparam-value\u003e1800\u003c/param-value\u003e\n\u003c/context-param\u003e\n```\n\nDescription about optional parameters:\n\n- `resty.cors.allowedOrigins`: Comma separated list of hosts and ports which will be allowed to make cross-origin requests (default is `*`).\n- `resty.cors.allowedMethods`: Comma separated list of HTTP methods will be allowed (default is `GET, POST, PUT, DELETE`).\n- `resty.cors.allowedHeaders`: Comma separated list of allowed HTTP headers (most headers are allowed in default).\n- `resty.cors.allowCredentials`: Set this parameter to true to allow cookies in CORS requests (default is `false`).\n- `resty.cors.preflightMaxAge`: Number of seconds that preflight request can be cached in the client (default is `0`).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftakezoe%2Fresty","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftakezoe%2Fresty","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftakezoe%2Fresty/lists"}