Spring REST服务在单个方法中使用和生成HTML表单POST和AJAX / JSON

时间:2015-07-16 05:10:54

标签: java spring rest spring-mvc

我试图通过创建一个非常简单的Web应用程序来自学Spring。我有一个班级来创建"注意"对象:

@Controller
@RequestMapping(value = "/notes")
public class NoteRestController {

    @Autowired
    private MappingJackson2JsonView jsonView;

    [...]

    @ResponseStatus(HttpStatus.CREATED)
    @RequestMapping(method = RequestMethod.POST, consumes = {
        MediaType.APPLICATION_JSON_VALUE,
        MediaType.APPLICATION_FORM_URLENCODED_VALUE })
    public ModelAndView create(final Model model,
            @Valid @ModelAttribute final Note note, final BindingResult result) {

        ModelAndView mav;

                // how can I test the request source?
                if (<requesting from HTML FORM>) {
                        // return jsonView
                mav = new ModelAndView(jsonView);
                } else {
                        // return JSP view
                mav = new ModelAndView("note", "model", model);
                }

        model.addAttribute("note", note);

        if (result.hasErrors()) {
            model.addAttribute("errors", result.getAllErrors());

            // on error, redirect back to note page with form
            // return new ModelAndView("note/note", "model", model);
            return mav;
        }

        note.setId(daoService.createNote(note));

        return mav;
    }
}

我希望能够使用单个方法(如上所述)来处理来自AJAX帖子和HTML表单帖子的请求。如果由AJAX触发,我想返回JSON(如果存在验证错误),如果它是由HTML表单触发的,我想使用表单taglib返回JSP

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>

并使用例如输入字段显示验证错误。

<form:errors path="title" cssClass="errorMessage"></form:errors>

这是可能的,还是应该创建两个控制器;一个用于REST / JSON,一个用于HTML /表单?也许有些东西可以传递给方法,可以通过查询来确定请求来源,但我现在无法看到它。

&#34;最佳做法&#34;在这种情况下?

编辑1: 首先尝试从@ ring-bearer回答,因为它允许相同的URL模式,但有问题。

使用方法:

// used to handle JSON/XML
@RequestMapping(method = RequestMethod.POST, produces = {
        MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE })
public @ResponseBody Note create(
        @Valid @ModelAttribute final Note note, final BindingResult result) {
            [...]
}

// used to handle form view
@RequestMapping(method = RequestMethod.POST)
public ModelAndView createForView(final Model model,
        @Valid @ModelAttribute final Note note, final BindingResult result) {
            [...]
}

有趣的是,HTML表单提交仍然create()而不是createForView()处理。在查看表单提交请求标头后,我看到这个Accept标头:

    text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

produces = "text/html"上向@RequestMapping添加createForView(),所有3个方案都有效(表单,AJAX / JSON,AJAX / XML)。

这是正常的,还是我还缺少某些东西?

2 个答案:

答案 0 :(得分:2)

这可以通过内容协商&#34;来实现。需要使用&#34; contentNegotiationManager&#34;来启用Spring MVC以进行内容协商。定义。它可以使用Java或XML配置进行设置。配置将集中管理媒体类型映射(json,xml等)。设置完成后,可以构建一个控制器类来满足JSON和View(HTML)。下面是一个通用示例(未编译),应该很容易将您的类重构为类似的结构,以避免违反DRY

@Controller
class ReportController{

   //1- Method for JSON/marshalling types(XML)
    @RequestMapping(value="/report", produces={"application/xml", "application/json"})
    @ResponseStatus(HttpStatus.OK)
    public @ResponseBody List<ReportPara> generateReport(Principal principal) {
        return reportService.generateReport(principal);
    }

    //2- For View technologies ( Eg:JSP HTML)
    @RequestMapping("/report")
    public String generateReportForView(Model model, Principal principal) {
        model.addAttribute( generateReport(principal) );

        // Return the view to use for rendering the response
        return ¨reports/main¨;
    }
}

两个@RequestMapping方法中的哪一个会执行?它由内容协商定义决定。例如:report.xml或report.json等URL映射到第一个方法,以report.anything结尾的任何其他URL映射到第二个。

答案 1 :(得分:1)

以下将更容易维护:

@Controller
class NoteController {
  @Autowired NoteService service;

  @RequestMapping(method = RequestMethod.POST, value = "/note")
  public ModelAndView createFromForm(@ModelAttribute @Valid Note note, BindingResult result) {
    return new ModelAndView("note", create(note));
  }

  @RequestMapping(method = RequestMethod.POST, value = "/api/note")
  @ResponseBody
  public Note createFromApi(@RequestBody Note note) {
    return create(note);
  }

  private Note create(Note note) {
    return service.create(note);
  }
}