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.

2 Answers 2

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

Sign up to request clarification or add additional context in comments.

2 Comments

There's no reason to use two handler methods. That's what ContentNegotationViewResolver is for
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.
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;
}

4 Comments

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.
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 RESTful
I guess I was just basing my model off of what I'd seen with Ruby's Rails framework.
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 guess

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.