可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I am new to sparkjava and like it overall. However, do new routes/endpoints have to be defined in the main method? For any significant web application, this will result in a very long main method or I need to have multiple main methods (and therefore split server resources among multiple instances).
These two sparkjava documentation pages seem to define routes in the main method: http://sparkjava.com/documentation.html#routes and here http://sparkjava.com/documentation.html#getting-started.
Is there another way to do this that I'm not seeing? Cursory google searching hasn't shown me a better way ...
=========
Here is the full solution I did based on the answer from Andrew. In my opinion, adding endpoints outside of the main method should be part of the sparkjava documentation page:
Main Method:
public static void main(String[] args) {
//Do I need to do something more with the Resource instance so that sparkjava notices it and/or reads the routes?
Resource resource= new Resource(new Service());
}
My resource:
import static spark.Spark.*;
class Resource{
private Service service;
Resource(Service service){
this.service = service;
setupEndpoints();
}
private void setupEndpoints() {
get("/user/:id", "application/json",(request, response)
-> service.find(request.params(":id")), new JsonTransformer());
get("/users", "application/json", (request, response)
-> service.findAll(), new JsonTransformer());
}
}
My service:
public class Service {
public Object find(String id) {
return null;
}
public Object findAll() {
return null;
}
}
My JsonTransformer:
import spark.ResponseTransformer;
public class JsonTransformer implements ResponseTransformer {
@Override
public String render(Object model) throws Exception {
return null;
}
}
回答1:
You can set routes where you want. You just need call set up method in main thread. e.g.
public static void main(String[] args){
Resource resource= new Resource(new Service());
}
class Resource{
private Service service;
Resource(Service service){
this.service = service;
setupEndpoints();
}
private void setupEndpoints() {
get("/user/:id", "application/json",(request, response)
-> service.find(request.params(":id")), new JsonTransformer());
get("/users", "application/json", (request, response)
-> service.findAll(), new JsonTransformer());
}
}
回答2:
Here's a design idea you can use when there are multiple endpoints to configure:
First, create a builder interface:
public interface EndpointBuilder {
void configure(Service spark, String basePath);
}
Now, let's say you have one of many other rest endpoints resources to set up:
public class CustomerEndpoint implements EndpointBuilder {
private final CustomerService customerService;
public CustomerEndpoint(CustomerService service) {
this.customerService = service;
}
@Override
public void configure(Service spark, String basePath) {
spark.get(basePath + "/customer", (req, res) -> {
return "hello";
});
}
}
Finally, create a RestContext class that will hold the spark instance and will enable you to configure whatever routes you wish:
public class RestContext {
private static final Logger logger = LoggerFactory.getLogger(RestContext.class);
private final Service spark;
private final String basePath;
public RestContext(int port, String basePath) {
this.basePath = basePath;
spark = Service.ignite().port(port); // import spark.Service;
}
public void addEndpoint(EndpointBuilder endpoint) {
endpoint.configure(spark, basePath);
logger.info("REST endpoints registered for {}.", endpoint.getClass().getSimpleName());
}
// Then you can even have some fun:
public void enableCors() {
spark.before((request, response) -> {
response.header("Access-Control-Allow-Origin", "*");
response.header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
response.header("Access-Control-Allow-Headers", "Content-Type, api_key, Authorization");
});
logger.info("CORS support enabled.");
}
}
You should be able to use this context class in your main method (and optionally in your test classes):
public static void main(String... args) {
RestContext context = new RestContext(8080, "/api");
context.addEndpoint(new CustomerEndpoint(new CustomerService()));
context.addEndpoint(new AnotherEndpoint()); // you can add or remove as many as you want.
context.enableCors();
}
Obs.: Since version 2.5, spark java supports multiple instances through the Service.ignite() api.
回答3:
You can also integrate Spring into your Spark application. This is how I've configured my routes.
@Configuration
public class RoutesConfiguration {
RoutesConfiguration() {
get("/hello", (req, res) -> "Hello World!");
}
}
This allows me to avoid the setup invocation step in the Main method of the SparkApp.
回答4:
Well, spark-java is for people not wanting tons of extra config I guess.
So, I just put and additonal method in the controllers, named declareRoutes where I put the declarations.
For the sample application:
public class LoginController {
public static void declareRoutes(){
get(Path.Web.LOGIN, LoginController.serveLoginPage);
post(Path.Web.LOGIN, LoginController.handleLoginPost);
post(Path.Web.LOGOUT, LoginController.handleLogoutPost);
}
public static Route serveLoginPage = (Request request, Response response) -> {
Map<String, Object> model = new HashMap<>();
model.put("loggedOut", removeSessionAttrLoggedOut(request));
model.put("loginRedirect", removeSessionAttrLoginRedirect(request));
return ViewUtil.render(request, model, Path.Template.LOGIN);
};
So, in my application I initialize all routes like this:
// Routes
Class[] controllers = {
IndexController.class,
LoginController.class,
SandboxController.class,
ServicesController.class };
for (Class controller: controllers) {
Method m = controller.getMethod("declareRoutes");
m.invoke(m);
}
// Catch all
declareCatchAllRoute();
The declareCatchAllRoute() is just:
private void declareCatchAllRoute() {
get("*", ViewUtil.notFound);
}
Works well enough for my needs, does not require any new abstraction.
HTH