Angular JS and Spring MVC @RestController - POST s

2019-09-04 15:07发布

问题:

I am currently try to develop RESTful application using Angular on font-end and Spring MVC on back-end(@RestController). I already have implemented few GET and POST method to fetch or save data, however now I am facing a problem with one POST method:

angular.module("app").factory("BorrowerService", function($http, $log){
   var borrowBook = function(borrow){
    $log.debug("Borrowing book: " );
    $log.debug(borrow);
    $http({
        method: 'POST',
        url: 'Zadanie3/borrow',
        data: borrow
   }).then(function successCallback(response) {
        $log.debug("success");
   }, function errorCallback(response) {
          $log.error("failed to borrow the book");
          $log.error(response);
      });
   };
   return{
    borrowBook: borrowBook
   };
});

POST result is always errorCallback with output:

failed to borrow the book
Object {data: null, status: 0, config: Object, statusText: ""}

Interesting part is that on most browsers (except Firefox) the book actually do borrow. I have already searched about status:0 response (also on stackoverflow) and unfortunatelly have not found the correct solution to my problem. I would appreciate any help. Here my RestController method:

@RestController
public class BookRestPostController {

 @RequestMapping(value = "/borrow", method=RequestMethod.POST)
 public BorrowDTO borrow(@RequestBody  BorrowDTO borrow ) {
    borrowerService.borrowBook(borrow);
    return borrow;
 }
}

Edit: I forgot to mention that I use @RestController annotation on my controller class. It automatically includes @ResponseBody.

回答1:

For security reasons some of the browsers don't allow to make ajax request that are not in the same origin. If you want read more on CORS I'm recommending this article. You have several choices to resolve your problem.

The first one is if you want to keep your current projects structure, both projects to exists on different servers. You should enable CORS (Cross origin resource sharing) support on server side and I see that you are using newer Spring Framework so you can just add one annotation that will enable CORS support. The annotation is @CrossOrigin see this article.

@RestController
@CrossOrigin(maxAge = 3600)
public class BookRestPostController {

 @RequestMapping(value = "/borrow", method=RequestMethod.POST)
 public BorrowDTO borrow(@RequestBody  BorrowDTO borrow ) {
    borrowerService.borrowBook(borrow);
    return borrow;
 }
}

If you are using older version of Spring Framework you can do in the manual way - add Filter which will add needed response headers. See this article

@Component
public class SimpleCORSFilter implements Filter {

    @Override
    public void init(FilterConfig arg0) throws ServletException {}

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp,
            FilterChain chain) throws IOException, ServletException {
        // TODO Auto-generated method stub
        HttpServletResponse response=(HttpServletResponse) resp;

        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "x-requested-with");

        chain.doFilter(req, resp);
    }

    @Override
    public void destroy() {}

}

This is probably the cleanest way to achieve your goal. Other two alternatives are to create server side proxy or to deploy both client and server side code in one server but my suggestion is not use proxy or same server !.

With simple words the browser will preflight request, it will send before every request, one extra request of type OPTIONS with specific headers (Access-Control-Request-Method and Access-Control-Request-Headers). These request headers will ask the server for permissions to make the actual request. Your preflight response needs to acknowledge these headers in order for the actual request to work.



回答2:

Damn annoying bugOrFeature... add @ResponseBody annotation

public @ResponseBody BorrowDTO borrow