Controller for JSON and HTML with Spring Boot
Asked Answered
S

2

6

I am writing an application where among other things I need to do CRUD operations with certain objects. I need to be able to serve both HTML pages for human users, and JSON for other applications. Right now my URLs look like this for "Read":

GET  /foo/{id}      -> Serves HTML
GET  /rest/foo/{id} -> Serves JSON
etc.

This seems a little redundant. I would rather have something like this:

GET /foo/{id}.html OR /foo/{id} -> Serves HTML
GET /foo/{id}.json              -> Serves JSON

Can Spring Boot do this? If so, how?

I know how to return JSON:

@RequestMapping(value = "/foo/{id}", method = RequestMethod.GET, produces = "application/json")
public Object fetch(@PathVariable Long id) {
    return ...;
}

I also know how to return HTML:

@RequestMapping("/app/{page}.html")
String index(@PathVariable String page) {
    if(page == null || page.equals(""))
        page = "index";
    return page;
}

But I'm not sure how to have a controller do one or the other based on the request.

Separation answered 3/12, 2014 at 18:15 Comment(0)
M
20

It's a default behavior for Spring Boot. The only thing is that you have to mark one of @RequestMapping to produce JSON. Example:

@Controller
class HelloController {

    // call http://<host>/hello.json
    @RequestMapping(value = "/hello", method = RequestMethod.GET, produces = "application/json")
    @ResponseBody
    public MyObject helloRest() {
        return new MyObject("hello world");
    }

    // call http://<host>/hello.html or just http://<host>/hello 
    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public String helloHtml(Model model) {
        model.addAttribute("myObject", new MyObject("helloWorld"));
        return "myView";
    }
}

Read more at: http://spring.io/blog/2013/05/11/content-negotiation-using-spring-mvc and http://spring.io/blog/2013/06/03/content-negotiation-using-views

Muslim answered 3/12, 2014 at 19:11 Comment(2)
There's no reason to use two handler methods. That's what ContentNegotationViewResolver is forDaggerboard
Its not entirely true. You may want to perform additional actions if its a call for classic HTML page. ContentNegotiationViewResolver is just one of the options, that's why I linked articles explaining both approaches.Muslim
D
0

Actually, you are mixing rest web service with html pages, it's a bad practice. If you want to build something really great, here is my advice. Write only CRUD operations in your controllers and all html/css/js keep in some static folder and when you will want to see ui part - just call that static index.html file You can read more about that here - http://spring.io/blog/2013/12/19/serving-static-web-content-with-spring-boot

But if you really want to do things as it is now, here is the solution:

@RequestMapping(value = "/foo/{id}", method = RequestMethod.GET)
public Object serve(final HttpServletRequest req, final HttpServletResponse resp, @PathVariable final Long id) {
    String header = req.getHeader("Accept");

    // If Accept header will be text/html - then we are forwarding to jsp page.
    if(header.equals("text/html")) {
        req.getRequestDispatcher("login.jsp").forward(req, resp);
    }

    // In other cases we are returning json and setting appropriate headers.
    resp.setHeader("Content-Type", "application/json");
    Object object = "Some string";

    return object;
}
Durstin answered 3/12, 2014 at 18:33 Comment(4)
I certainly want to use good practices. The reason I didn't use the static folder for the HTML pages is because I think that removes the Thymeleaf templating that I'm using.Separation
If you want to use good practices, why do you want to template engines with rest web service ?There's a lot of template engines in js world. Be RESTfulDurstin
I guess I was just basing my model off of what I'd seen with Ruby's Rails framework.Separation
I don't really get why Ivan was downvoted, the OP was receptive of his criticism and he is actually making a valid point. woops two years too late I guessRamulose

© 2022 - 2024 — McMap. All rights reserved.