
Marco web ligero para Java 8 con enrutamiento, filtros y servicio de archivos estáticos. Incluye aviso de seguridad para versiones anteriores y ejemplos de API CRUD.
NOTICIAS: ¡Spark 2.7.0 ha sido lanzado! Por favor, pruébalo y reporta cualquier error.
<dependency>
<groupId>com.sparkjava</groupId>
<artifactId>spark-core</artifactId>
<version>2.7.0</version>
</dependency>
Importante - Existe una vulnerabilidad en versiones antiguas de Spark (versiones inferiores a 2.5.2). Actualiza a la última versión.
Para la documentación, visita: http://sparkjava.com/documentation
Para preguntas de uso, utiliza stack overflow con la etiqueta “spark-java”
Javadoc: http://javadoc.io/doc/com.sparkjava/spark-core
<dependency>
<groupId>com.sparkjava</groupId>
<artifactId>spark-core</artifactId>
<version>2.7.0</version>
</dependency>
import static spark.Spark.*;
public class HelloWorld {
public static void main(String[] args) {
get("/hello", (request, response) -> "Hello World!");
}
}
Ver en: http://localhost:4567/hello
Revisa y prueba los ejemplos en el código fuente. También puedes consultar el javadoc. Después de obtener el código de github ejecuta:
mvn javadoc:javadoc
El resultado se coloca en /target/site/apidocs
Ejemplo sencillo que muestra algunas funcionalidades básicas
import static spark.Spark.*;
/**
* Un ejemplo simple que muestra solo algunas funcionalidades básicas
*/
public class SimpleExample {
public static void main(String[] args) {
// port(5678); <- Descomenta esto si quieres que Spark escuche en el puerto 5678 en lugar del predeterminado 4567
get("/hello", (request, response) -> "Hello World!");
post("/hello", (request, response) ->
"Hello World: " + request.body()
);
get("/private", (request, response) -> {
response.status(401);
return "Go Away!!!";
});
get("/users/:name", (request, response) -> "Selected user: " + request.params(":name"));
get("/news/:section", (request, response) -> {
response.type("text/xml");
return "<?xml version=\"1.0\" encoding=\"UTF-8\"?><news>" + request.params("section") + "</news>";
});
get("/protected", (request, response) -> {
halt(403, "I don't think so!!!");
return null;
});
get("/redirect", (request, response) -> {
response.redirect("/news/world");
return null;
});
get("/", (request, response) -> "root");
}
}
Un ejemplo CRUD sencillo que muestra cómo crear, obtener, actualizar y eliminar recursos de libros
import static spark.Spark.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
/**
* Un ejemplo CRUD simple que muestra cómo crear, obtener, actualizar y eliminar recursos de libros.
*/
public class Books {
/**
* Mapa que almacena los libros
*/
private static Map<String, Book> books = new HashMap<String, Book>();
public static void main(String[] args) {
final Random random = new Random();
// Crea un nuevo recurso de libro, devolverá el ID del recurso creado
// author y title se envían en el cuerpo de la petición POST como valores x-www-urlencoded, ej. author=Foo&title=Bar
// los obtienes usando request.queryParams("valuename")
post("/books", (request, response) -> {
String author = request.queryParams("author");
String title = request.queryParams("title");
Book book = new Book(author, title);
int id = random.nextInt(Integer.MAX_VALUE);
books.put(String.valueOf(id), book);
response.status(201); // 201 Created
return id;
});
// Obtiene el recurso de libro para el id proporcionado
get("/books/:id", (request, response) -> {
Book book = books.get(request.params(":id"));
if (book != null) {
return "Title: " + book.getTitle() + ", Author: " + book.getAuthor();
} else {
response.status(404); // 404 Not found
return "Book not found";
}
});
// Actualiza el recurso de libro para el id proporcionado con nueva información
// author y title se envían en el cuerpo de la petición como valores x-www-urlencoded, ej. author=Foo&title=Bar
// los obtienes usando request.queryParams("valuename")
put("/books/:id", (request, response) -> {
String id = request.params(":id");
Book book = books.get(id);
if (book != null) {
String newAuthor = request.queryParams("author");
String newTitle = request.queryParams("title");
if (newAuthor != null) {
book.setAuthor(newAuthor);
}
if (newTitle != null) {
book.setTitle(newTitle);
}
return "Book with id '" + id + "' updated";
} else {
response.status(404); // 404 Not found
return "Book not found";
}
});
// Elimina el recurso de libro para el id proporcionado
delete("/books/:id", (request, response) -> {
String id = request.params(":id");
Book book = books.remove(id);
if (book != null) {
return "Book with id '" + id + "' deleted";
} else {
response.status(404); // 404 Not found
return "Book not found";
}
});
// Obtiene todos los recursos de libro disponibles (ids)
get("/books", (request, response) -> {
String ids = "";
for (String id : books.keySet()) {
ids += id + " ";
}
return ids;
});
}
public static class Book {
public String author, title;
public Book(String author, String title) {
this.author = author;
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
}
Ejemplo que muestra un filtro de autenticación muy simple (y tonto) que se ejecuta antes que todos los demás recursos
import static spark.Spark.*;
import java.util.HashMap;
import java.util.Map;
/**
* Ejemplo que muestra un filtro de autenticación muy simple (y tonto) que se
* ejecuta antes que todos los demás recursos.
*
* Al solicitar el recurso con, por ejemplo,
* http://localhost:4567/hello?user=some&password=guy
* el filtro detendrá la ejecución y el cliente recibirá un 401 UNAUTHORIZED con el contenido 'You are not welcome here'
*
* Al solicitar el recurso con, por ejemplo,
* http://localhost:4567/hello?user=foo&password=bar
* el filtro aceptará la solicitud y continuará hacia la ruta /hello.
*
* Nota: Hay un segundo "before filter" que añade una cabecera a la respuesta
* Nota: También hay un "after filter" que añade una cabecera a la respuesta
*/
public class FilterExample {
private static Map<String, String> usernamePasswords = new HashMap<String, String>();
public static void main(String[] args) {
usernamePasswords.put("foo", "bar");
usernamePasswords.put("admin", "admin");
before((request, response) -> {
String user = request.queryParams("user");
String password = request.queryParams("password");
String dbPassword = usernamePasswords.get(user);
if (!(password != null && password.equals(dbPassword))) {
halt(401, "You are not welcome here!!!");
}
});
before("/hello", (request, response) -> response.header("Foo", "Set by second before filter"));
get("/hello", (request, response) -> "Hello World!");
after("/hello", (request, response) -> response.header("spark", "added by after-filter"));
afterAfter("/hello", (request, response) -> response.header("finally", "executed even if exception is throw"));
afterAfter((request, response) -> response.header("finally", "executed after any route even if exception is throw"));
}
}
Ejemplo que muestra cómo usar atributos
import static spark.Spark.after;
import static spark.Spark.get;
/**
* Ejemplo que muestra el uso de atributos
*/
public class FilterExampleAttributes {
public static void main(String[] args) {
get("/hi", (request, response) -> {
request.attribute("foo", "bar");
return null;
});
after("/hi", (request, response) -> {
for (String attr : request.attributes()) {
System.out.println("attr: " + attr);
}
});
after("/hi", (request, response) -> {
Object foo = request.attribute("foo");
response.body(asXml("foo", foo));
});
}
private static String asXml(String name, Object value) {
return "<?xml version=\"1.0\" encoding=\"UTF-8\"?><" + name +">" + value + "</"+ name + ">";
}
}
Ejemplo que muestra cómo servir recursos estáticos
import static spark.Spark.*;
public class StaticResources {
public static void main(String[] args) {
// Servirá todos los archivos estáticos que estén bajo "/public" en el classpath si la ruta no es consumida por otras rutas.
// Cuando se usa Maven, se asume que la carpeta "/public" está en "/main/resources"
staticFileLocation("/public");
get("/hello", (request, response) -> "Hello World!");
}
}
Ejemplo que muestra cómo definir contenido según el tipo de aceptación
import static spark.Spark.*;
public class JsonAcceptTypeExample {
public static void main(String args[]) {
//Ejecutando curl -i -H "Accept: application/json" http://localhost:4567/hello se lee el mensaje json.
//Ejecutando curl -i -H "Accept: text/html" http://localhost:4567/hello se lanza un error HTTP 404.
get("/hello", "application/json", (request, response) -> "{\"message\": \"Hello World\"}");
}
}
Ejemplo que muestra cómo renderizar una vista desde una plantilla. Observa que usamos la clase ModelAndView para configurar el objeto y el nombre/ubicación de la plantilla.
Primero definimos una clase que maneja y renderiza la salida dependiendo del motor de plantillas utilizado. En este caso FreeMarker.
public class FreeMarkerTemplateEngine extends TemplateEngine {
private Configuration configuration;
protected FreeMarkerTemplateEngine() {
this.configuration = createFreemarkerConfiguration();
}
@Override
public String render(ModelAndView modelAndView) {
try {
StringWriter stringWriter = new StringWriter();
Template template = configuration.getTemplate(modelAndView.getViewName());
template.process(modelAndView.getModel(), stringWriter);
return stringWriter.toString();
} catch (IOException e) {
throw new IllegalArgumentException(e);
} catch (TemplateException e) {
throw new IllegalArgumentException(e);
}
}
private Configuration createFreemarkerConfiguration() {
Configuration retVal = new Configuration();
retVal.setClassForTemplateLoading(FreeMarkerTemplateEngine.class, "freemarker");
return retVal;
}
}
Luego podemos usarlo para generar nuestro contenido. Observa cómo configuramos los datos del modelo y el nombre de la vista. Como estamos usando FreeMarker, en este caso se requiere un Map y el nombre de la plantilla:
public class FreeMarkerExample {
public static void main(String args[]) {
get("/hello", (request, response) -> {
Map<String, Object> attributes = new HashMap<>();
attributes.put("message", "Hello FreeMarker World");
// El archivo hello.ftl está ubicado en el directorio:
// src/test/resources/spark/examples/templateview/freemarker
return modelAndView(attributes, "hello.ftl");
}, new FreeMarkerTemplateEngine());
}
}
Ejemplo de uso de Transformer.
Primero definimos la clase transformadora, en este caso una clase que transforma un objeto a formato JSON usando la API gson.
public class JsonTransformer implements ResponseTransformer {
private Gson gson = new Gson();
@Override
public String render(Object model) {
return gson.toJson(model);
}
}
Y luego el código que devuelve un simple POJO para ser transformado a JSON:
public class TransformerExample {
public static void main(String args[]) {
get("/hello", "application/json", (request, response) -> {
return new MyMessage("Hello World");
}, new JsonTransformer());
}
}
Ver Spark-debug-tools como un módulo separado.