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.
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.
Damn annoying bugOrFeature...
add @ResponseBody annotation
public @ResponseBody BorrowDTO borrow