AJAX returns 404 in Spring MVC - ajax

ViewResolver (my jsp is in the right folder as specified on prefix value):
<!-- Resolves views selected for rendering by #Controllers -->
<!-- to .jsp resources in the /WEB-INF/views directory -->
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
Servlet mapping:
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>*.fst</url-pattern>
</servlet-mapping>
Controller:
#Controller
public class HomeController {
private static final Logger logger =
LoggerFactory.getLogger(HomeController.class);
#RequestMapping("/home")
public ModelAndView home(String user, HttpServletRequest request) {
logger.info("Home controller has been executed");
ModelAndView mv = new ModelAndView();
mv.addObject("userName", user);
mv.addObject("controllerName", request.getRequestURI());
mv.setViewName("home");
return mv;
}
#RequestMapping(value = "/testAjax", method = RequestMethod.POST)
public String testAjax(#RequestParam("memberId") String id,
HttpServletRequest request, HttpServletResponse response,
Locale locale, Model model) {
logger.info("Text Ajax action has been executed. My Parameter is " + id);
return id;
}
}
After turning on Tomcat 8 server on STS IDE, accessing this web with this url http://localhost:8080/home.fst works okay.
But on the page, calling AJAX like below throws a 404 error:
$.ajax({
type: "POST",
url: "/testAjax.fst",
data: {"memberId" : "test"},
success: function (result) {
console.log(result)
}
});
This is console error log:
POST http://localhost:8080/testAjax.fst 404 (Not Found)
k.cors.a.crossDomain.send jquery-2.1.3.min.js:4
n.extend.ajaxhome.fst:11 (anonymous function) jquery-2.1.3.min.js:3
n.event.dispatch jquery-2.1.3.min.js:3
r.handle
Strange thing is that it calls testAjax controller just fine and there's no error log on server.
logger.info("Text Ajax action has been executed. My Parameter is " + id);
When textAjax action is invoked by my AJAX, the log is printed as well. I checked it out with debug point too (it broke alright).
What seems to be the matter??

Everything's good just Add #ResponseBody annotation in your method and also I suggest you to change your request method POST to GET
Spring
#RequestMapping(value = "/testAjax", method = RequestMethod.GET) //Made Change
#ResponseBody //added
public String testAjax(#RequestParam("memberId") String id, HttpServletRequest request, HttpServletResponse response, Locale locale, Model model) {
logger.info("Text Ajax action has been executed. My Parameter is " + id);
return id;
}
JQuery
$.ajax({
type: "GET", //Made Change
url:"/testAjax.fst",
data: {"memberId" : "test"},
success: function (result) {
console.log(result)
}
});

Related

Cross origin blocked between Ajax and Spring Controller

I want a Javascript function to send data to a Spring controller and get a response. However, due to the strict-origin-when-cross-origin Referrer policy, the request does not go through.
Spring Controller :
#Controller
public class EventController {
#ResponseBody
#RequestMapping(value = "/event", method = RequestMethod.POST)
public String handleAjax(#RequestParam Integer id, HttpServletRequest request, HttpServletResponse response) {
response.setHeader("Access-Control-Allow-Origin", "*");
return "OK";
}
}
Javascript functions :
function getContextPath() {
return window.location.pathname.substring(0, window.location.pathname.indexOf("/",2));
}
function move(moveDir,velocity){
$.ajax({
type : "POST",
url : getContextPath() + "/event",
success: function(data){
console.log(data)
}
});
}
I know that I have to allow cross-origin for these files. So far, the things I tried didn't work.
List of what I tried :
-> Adding #CrossOrigin(origins = "http://localhost:8081", maxAge = 3600) to the controller
-> Adding response.setHeader("Access-Control-Allow-Origin", "*"); to the controller
-> Adding crossorigin="anonymous" to the Javascript <script> tag
Your code won't work because you specify to support POST method only
#RequestMapping(value = "/event", method = RequestMethod.POST)
OPTIONS method is required as Preflighted requests in CORS.
So you must support POST and OPTIONS also to make it work.
#RequestMapping(value = "/event")
#RequestMapping(value = "/event", method = { RequestMethod.POST, RequestMethod.OPTIONS })
https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS
I finally got it. There were two problems.
The first was that I had to add that header to the ajax request, to allow the request to be handled by the server: headers: { 'Access-Control-Allow-Origin': '/path_to_request_target' },. Putting '*' instead of '/path_to_request_target' would also work.
The second was with my dispatcher servlet. I had to put .html at the end of the URL : url : getContextPath() + "/event.html",. This is due of the mapping of my dispatcher servlet :
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/index.jsp</url-pattern>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
Adding .html to the URL worked because of <url-pattern>*.html</url-pattern>. Adding a <url-pattern> that satisfied the format of the inital URL would also work.

AJAX get returns 404 in Spring

help me
index.jsp
$("#btn-submit").click(function () {
var username=document.getElementById("username");
var password=document.getElementById("password");
$.ajax({
url:"login",
contentType: 'application/json;charset=utf-8',
dataType: 'text',
headers: {'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')},
data: {
username:username.value,
password:password.value
},
type: 'get',
success: function (response) {
if (response=="1") {
alert(response);
}
else alert(response);
},
error: function (x, e) {
console.log(e)
}
});
});
LoginController.java
#RequestMapping("/login")
#Controller
public class LoginController {
#Autowired
private UserService userService;
#RequestMapping(value = { "/login" }, method = RequestMethod.GET)
#ResponseBody
public int checkValid(#RequestParam("username") String username,#RequestParam("password") String password, HttpServletRequest request, HttpServletResponse response, Locale locale, Model model){
try {
if (userService.findByUserName(username).equals(hashPass(password))){
return 1;
}
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return 0;
}
return 0;
}
public String hashPass(String pass) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] hashInBytes = md.digest(pass.getBytes(StandardCharsets.UTF_8));
// bytes to hex
StringBuilder sb = new StringBuilder();
for (byte b : hashInBytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
}
spring-config-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
http://www.springframework.org/schema/jdbc/spring-jdbc-4.3.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
/WEB-INF/pages/
.jsp
/resources/jdbc.properties
<!-- Enable Annotation based Declarative Transaction Management -->
<tx:annotation-driven proxy-target-class="true"
transaction-manager="transactionManager" />
<!-- Creating TransactionManager Bean, since JDBC we are creating of type
DataSourceTransactionManager -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="postsDAO" class="com.blog.dao.impl.PostsDAO">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
<bean id="postsService" class="com.blog.service.impl.PostsService">
<property name="postsDAO" ref="postsDAO"/>
</bean>
<bean id="userDAO" class="com.blog.dao.impl.UserDAO">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
<bean id="userService" class="com.blog.service.impl.UserService">
<property name="userDAO" ref="userDAO"/>
</bean>
I use tomcat 9
Error:Failed to load resource: the server responded http://localhost:8080/Blog_war_exploded/login?username=root&password=root with a status of 404 ()
Look at your error: You are accessing http://localhost:8080/Blog_war_exploded/login but you actually want to access http://localhost:8080/login.
The reason is that you specified your URL as login instead of /login, so it is relative to the current "directory" and not to the root.
Changing the code to use /login should fix it:
$.ajax({
url: "/login",
...
})
On a side note, it's not a good idea to this via GET requests - among other things, the password will be stored in the server log in clear text. You should use a POST request instead.
Update:
Also, it seems you are use two request mappings for /login on top of each other, so you'll end up with /login/login. Check out how to use #RequestMapping properly.
Try changing the second (method-level) one to #RequestMapping(value = { "/" }, method = RequestMethod.GET) or just #RequestMapping("/").
I think the issue is related to your RequestMapping definition on both controller level and method level.
the first login at the controller level, means if you want to access any services in this controller, your requests have to start with "/login"
#RequestMapping("/login")
#Controller
public class LoginController {
and the second login at the method level, means you want to call the /login service under /login.
#RequestMapping(value = { "/login" }, method = RequestMethod.GET)
#ResponseBody
public int checkValid(#RequestParam("username") String username,#RequestParam("password") String password, HttpServletRequest request, HttpServletResponse response, Locale locale, Model model){
So the valid URL to call the /login service under /login controller is: /login/login
and because of this, your url /login was not found
you can either remove the first /login at the controller level, or use the /login/login from your ajax request...

How to return JSON response for unauthorized AJAX calls instead of login page as AJAX response?

I have implemented Spring Security in my application. Whenever someone tries to access any url if authentication is required for these urls user will be redirected to login page. Now, if AJAX call is made for any such url I would like to return JSON response instead of login page's HTML as AJAX response. How can I do that ?
You have to create json for this i am doing here with .net
var url="url";
$.ajax({
type: "get",
dataType: "json",
data:url,
async: true,
url: "testCall",//this can be your api or any server side call
success: function (data) {
},
failure: function () {
alert(textStatus);
}
});
//here is server side code for creating json
[WebMethod(EnableSession = true)]
[ScriptMethod(UseHttpGet = true)]
public void testCall(string url)
{
Context.Response.Write()//here your will just hard code the json data
//it will receive by ajax success method.
}
Faced the same thing not long ago, came out with this solution.
You'll have to redefine the authentication entry point to handle the exception and returning a proper JSON response.
First create a class for your response. Needs to be a POJO.
public class MyErrorResponse {
// your stuff here, and getters / setters
}
Then go define the authentication entry point
public class MyBasicAuthenticationEntryPoint extends BasicAuthenticationEntryPoint {
private List<HttpMessageConverter<Object>> messageConverters = new ArrayList<>();
private MediaType retrieveRequestMediaType(HttpServletRequest request) {
String accept = request.getHeader("accept");
if(Strings.isNullOrEmpty(accept))
accept = MediaType.APPLICATION_JSON_VALUE;
MediaType requestMediaType = MediaType.valueOf(accept);
return requestMediaType;
}
private HttpMessageConverter<Object> retrieveMessageConverter(List<HttpMessageConverter<Object>> messageConverters, Class<?> clazz, MediaType mediaType) {
for (HttpMessageConverter<Object> httpMessageConverter : messageConverters) {
if(httpMessageConverter.canWrite(clazz, mediaType)) {
return httpMessageConverter;
}
}
}
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
log.warn(String.format("Unauthorized access with session id '%s'", request.getSession().getId()));
MyErrorResponse esponse = new MyErrorResponse();
// populate your response object with all the info you need
MediaType mediaType = MediaType.APPLICATION_JSON;
try{
mediaType = retrieveRequestMediaType(request);
} catch(InvalidMediaTypeException imte) {
// log, do nothing
}
// getting the best fitting message converter, according to the "accept" header of the request
HttpMessageConverter<Object> httpMessageConverter = retrieveMessageConverter(messageConverters, MyErrorResponse.class, mediaType);
if(httpMessageConverter == null) {
log.info("Could not find specific handler. Using JSON.");
httpMessageConverter = retrieveMessageConverter(messageConverters, MyErrorResponse.class, MediaType.APPLICATION_JSON);
}
response.setStatus(HttpStatus.UNAUTHORIZED.value());
ServletServerHttpResponse serverHttpResponse = new ServletServerHttpResponse(errorResponse);
httpMessageConverter.write(response, mediaType, serverHttpResponse);
}
}
Once you got your bean set up, time to wire it up in the security context:
<beans:bean class="[fully qualified name of the entry point class]" id="myBasicAuthenticationEntryPoint">
<beans:property name="messageConverters">
<beans:list>
<!-- add message converters here -->
<!-- Spring provide lots of them, google it -->
</beans:list>
</beans:property>
</beans:bean>
<http use-expressions="true">
<http-basic entry-point-ref="myBasicAuthenticationEntryPoint" />
<!-- add other stuff here, if needed -->
</http>
Hope it helps

No mapping found for HTTP request with URI - in spring 3.2.4

I have this controller:
...
#RequestMapping(value = "/accounts/manageaccount.do", method = RequestMethod.GET)
public String initForm(HttpServletRequest request, Model model) {
model.addAllAttributes(getModel(request));
return "registerAccountView";
}
#RequestMapping(value = "/accounts/saveaccount.do", method = RequestMethod.POST)
public String saveaccount(HttpServletRequest request, Model model) {
model.addAllAttributes(getModel(request));
return "registerAccountView";
}
...
The controller its mapping well when I put this URL in the browser
http://127.0.0.1:7001/devices/accounts/manageaccount.do
Then I have this jsp
<form method="post" action="saveaccount.do">
</form>
But when I submit I got this strange error
URL: /devices/accounts/saveaccount.do
???error404.error???
Make sure the servlet mapping in your web-xml is configured to handle the .do requests :
<servlet-mapping>
<servlet-name>yourServletName</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>

Spring MVC redirect with variables

I have following Spring MVC 3.2.4 method:
#RequestMapping(value = "/products/{product}", method = RequestMethod.POST)
public String update(Product product, #Valid #ModelAttribute("productForm") ProductForm productForm, BindingResult bindingResult, Model model) {
if (bindingResult.hasErrors()) {
return "products/view";
}
mapper.map(productForm, product);
productService.saveProduct(product);
return "redirect:/products/{product}";
}
After success it should redirect back user to detail of product. Problem is that instead of redirecting to page "/products/1" I am redirected to page "/products/Product [code=1234567890, name=Nejaky]". It looks like placeholder {product} is replaced by product.toString() instead of original ID from URL.
I am using built-in Spring Data converter:
<mvc:annotation-driven conversion-service="conversionService">
<mvc:argument-resolvers>
<bean class="org.springframework.data.web.PageableHandlerMethodArgumentResolver" />
</mvc:argument-resolvers>
</mvc:annotation-driven>
<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean" />
<bean class="org.springframework.data.repository.support.DomainClassConverter">
<constructor-arg ref="conversionService" />
</bean>
What should I do to make it work correctly and redirect me back to "/products/1" without doing things like "redirect:/product" + product.getId()?
Our story starts in RedirectView source code, in the method replaceUriTemplateVariables.
protected StringBuilder replaceUriTemplateVariables(
String targetUrl, Map<String, Object> model, Map<String, String> currentUriVariables, String encodingScheme)
throws UnsupportedEncodingException {
StringBuilder result = new StringBuilder();
Matcher m = URI_TEMPLATE_VARIABLE_PATTERN.matcher(targetUrl);
int endLastMatch = 0;
while (m.find()) {
String name = m.group(1);
Object value = model.containsKey(name) ? model.remove(name) : currentUriVariables.get(name);
Assert.notNull(value, "Model has no value for '" + name + "'");
result.append(targetUrl.substring(endLastMatch, m.start()));
result.append(UriUtils.encodePathSegment(value.toString(), encodingScheme));
endLastMatch = m.end();
}
result.append(targetUrl.substring(endLastMatch, targetUrl.length()));
return result;
}
As you had predicted, the method uses value.toString() where value is your product object in the Model. No other component like a conversion system is involved here. Your options are as follows:
Use
"redirect:/product" + product.getId()
Add a model attribute called "productId" and use that in your view name
model.addAttribute("productId", product.getId());
"redirect:/product/{productId}"
Or use uri variables. I don't have information on those yet.
Ok, finally found reason for this. I had to annotate product param with #PathVariable. Wondering that it worked without it.
I know it's an old question but for anyone facing the same problem here is the answer
Just inject RedirectAttributes to your controller and use redirectAtrr.addAttribute([attrbuteName],[attributeValue])
#RequestMapping(value = "/products/{product}", method = RequestMethod.POST)
public String update(Product product,#Valid,#ModelAttribute("productForm") ProductForm productForm,BindingResult bindingResult,Model model,RedirectAttributes redirectAttr) {
if (bindingResult.hasErrors()) {
return "products/view";
}
mapper.map(productForm, product);
productService.saveProduct(product);
redirectAttr.addAttributte("productId",product.getId());
return "redirect:/products/{productId}";
}
Read documentation for more understanding.

Resources