I am creating an API by using spring boot. Basically, this API does CRUD operations. And also I created a client that consumes my own API. At first I use Postman to POST data, it successfully insert data to the database and gives me 200 OK code. Then I created web page and I use my API as form action. Then I tried to insert data using the API. But, couldn't. Then I removed #RequestBody from the method and after that I was able to insert data. But the thing is now I can't insert data using Postman. When I try to insert data using Postman, it gives me 200 OK, but nothing insert to the database.
How can I Fix this ??
package com.kisalka.pacrestapi.controller;
import java.util.List;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import com.kisalka.pacrestapi.repository.ImRepository;
import com.kisalka.pacrestapi.model.ImModel;
#RestController
#RequestMapping("/api")
public class ImController {
#Autowired
private ImRepository TaskRepository;
#RequestMapping(method=RequestMethod.POST, value="/tasks")
public ImModel createNote(ImModel note) {
return TaskRepository.save(note);
}
}
My web page.
<form class="form-horizontal" method="POST" action="">
<div class="form-group">
<label class="control-label col-md-3">Project Name</label>
<div class="col-md-7">
<input type="text" class="form-control" name="pname" id="txtPname"/>
</div>
</div>
<div class="form-group">
<label class="control-label col-md-3">Developer Name</label>
<div class="col-md-7">
<input type="text" class="form-control" name="devname" id="txtDevname"/>
</div>
</div>
<div class="form-group">
<input type="submit" class="btn btn-primary" value="Save" id="btnRegister"/>
</div>
</form>
In one of your #Configuration classes or #EnableAutoConfiguration class create a bean of CommonsRequestLoggingFilter, paste the code. This will log every incoming request
#Bean
public CommonsRequestLoggingFilter logFilter() {
CommonsRequestLoggingFilter filter
= new CommonsRequestLoggingFilter();
filter.setIncludeQueryString(true);
filter.setIncludePayload(true);
filter.setMaxPayloadLength(10000);
filter.setIncludeHeaders(false);
filter.setAfterMessagePrefix("REQUEST DATA : ");
return filter;
}
And in your application.properties file set logging level to DEBUG using logging.level.org.springframework.web.filter.CommonsRequestLoggingFilter=
DEBUG
All set! now call your endpoint from the WEB/Postman and check if you find the missing piece.
You need to use #RequestBody :
#RequestMapping(method=RequestMethod.POST, value="/tasks")
public ImModel createNote(#RequestBody ImModel note) {
return TaskRepository.save(note);
}
use the code written below.You need to add #RequestBody before ImModel note
#RequestMapping(method=RequestMethod.POST, value="/tasks")
public ImModel createNote(#RequestBody ImModel note) {
return TaskRepository.save(note);
}
Related
I have post mapping for URL: "/bank/addnew" My controller looks like:
Upon submission the form is submitted to "http://127.0.0.1:8082/banks/%20/banks" However, I need it to go to "http://127.0.0.1:8082/banks/addnew". Thanks for the Help!
#Controller
public class BankController {
#Autowired private BankService bankService;
#GetMapping("/banks")
public String bankList() {
return "bank/bank_list";
}
#PostMapping(value="/banks/addnew")
public String addNew(Bank bank) {
bankService.save(bank);
return "redirect: /banks";
}
}
And my template:
<form method="POST" action="#" th:action="#{/banks/addnew}" >
<div class="form-group">
<label for="recipient-name" class="col-form-label">Bank Name:</label>
<input type="text" class="form-control" id="recipient-name" name="name">
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>
The Post Method was actually working okay. I checked it by printing the Model object inside the target controller. However, it was being submitted to the database a NULL value because I didn't initialize getters and setters for my Model. Finally, there was a space in the return string, thus I removed it.
The controller Should have a return statement with no space.
return "redirect:/banks";
I tried replacing the spring security login screen with my custom one. While the default one works perfectly my own one doesn't seem to react to posting the form. It just reloads the login screen.
login.mustache:
{{> partials/header}}
<body class="grey lighten-4">
<div class="container">
<form action="/login" method="POST">
<div class="md-form">
<input type="text" name="username" id="loginField" class="form-control">
<label for="loginField">Login</label>
</div>
<div class="md-form">
<input type="password" name="password" id="passwordField" class="form-control">
<label for="passwordField">Password</label>
</div>
<button class="btn red text-white float-right" type="submit">Log in</button>
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
</form>
</div>
</body>
{{> partials/footer}}
LoginController.kt
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.ui.Model
import org.springframework.ui.set
#Controller
class LoginController {
#GetMapping("/login")
fun loginPage(model: Model): String {
return "login"
}
/*
#PostMapping("/login")
fun loginForm() {
print("post")
}*/
}
It doesn't even trigger the breakpoint in the currently commented part when posting the form.
SecurityConfig.kt
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
import org.springframework.security.core.userdetails.User
import org.springframework.security.provisioning.InMemoryUserDetailsManager
#Configuration
#EnableWebSecurity
class SecurityConfig : WebSecurityConfigurerAdapter() {
override fun configure(http: HttpSecurity) {
http.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/test")
.permitAll()
}
override fun configure(auth: AuthenticationManagerBuilder) {
auth.userDetailsService(
InMemoryUserDetailsManager(
User.withUsername("user").password("user").authorities(mutableListOf()).build()
)
)
}
}
I suspect I'm missing some crucial part to receiving and processing the contents of the form correctly. However after going through like 10 tutorials on this issue I've found so many inconsistencies between them that I'm honestly lost.
It seems you are using incorrect mustache syntax for the csrf parameter.
You need the {{ to render the context variable.
<input type="hidden" name="{{_csrf.parameterName}}" value="{{_csrf.token}}"/>
Additionally, you need to copy the "_csrf" object to the MVC model by adding this setting to you application.properties.
spring.mustache.expose-request-attributes=true
Finally, you may see an IllegalArgumentException because you haven't specified a password encoder. If that occurs, you will see the error in your stacktrace. It can be fixed by specifying a PasswordEncoder.
I'm trying to validate my form input, but after its submit it does not load my controller method, instead it shows a Whitelabel Error Page with Internal Server Errorand status 500 but with the correct validation errors.
I know it must be a matching issue with the expected parameters of my method, but so far from what I've gathered the rule is simply BindingResult then Model which I did but it still does not trigger my controller method...
Any thoughts are appreciated.
Controller-Method, that is not triggered unfortunately:
#Controller
#Validated
public class UserController {
#PostMapping("/users/add")
def String usersAdd(#Valid #ModelAttribute("user") User user, BindingResult bindingResult, Model model) {
if (bindingResult.hasErrors()) {
return "userAdd"
} else {
userRepository.save(user)
return "usersList"
}
}
Template-Form:
<form action="#" th:action="#{/users/add}" th:object="${user}" method="post">
<div class="form-row">
<div class="form-group col-md-12">
<input type="text" class="form-control" th:field="*{name}">
<div th:if="${#fields.hasErrors('name')}" th:errors="*{name}">Name Error</div>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<button type="submit" class="btn btn-success">Add</button>
</div>
</div>
</form>
Model-Class:
#Document(collection = "users")
public class User {
#Id
String id
#Size(min=3, max=100)
String name
}
Update:
Went debugging and found this part, so it seems it's invoking with the correct parameters. I am new to Spring, so I'm having difficulties thinking about other "newbie"-problems.
in InvocableHandlerMethod.java the method DoInvoke is called on my usersAdd with Station, BeanPropertyBindingResult, BindingAwareModelMap.
When Invoking the method it causes a ConstraintViolationException which will abort invocation, is this expected?
I figured it out after a looong debug session. The #Valid before my User user causes a ConstraintViolationException, which will, expectedly, abort the invocation of my usersAdd because, as expected, the parameter is not valid.
And the reason for this behaviour was the #Validated at the controller class, which I picked up from some tutorial, but when removed everything works and validates as expected.
I guess I was checking a REST-Tutorial and tried to use their approach... not the best idea ;)
The issue is that when I click on submit I get
Etat HTTP 405 - Request method 'POST' not supported
Here is the controller :
#RequestMapping(value = "/admin/login", method = RequestMethod.GET)
public ModelAndView login(
#RequestParam(value = "error", required = false) String error,
#RequestParam(value = "logout", required = false) String logout) {
LOGGER.debug("admin login page");
ModelAndView model = new ModelAndView();
if (error != null) {
model.addObject("error", "Invalid username and password!");
}
if (logout != null) {
model.addObject("msg", "You've been logged out successfully.");
}
model.setViewName("/admin/index");
LOGGER.debug("returning admin login page");
return model;
}
And the form :
<form class="m-t" role="form" th:action="#{/admin/login}" method="POST" autocomplete="off">
<div th:if="${param.error}" class="alert alert-danger">Invalid username and password.</div>
<div th:if="${param.logout}" class="alert alert-success">You have been logged out.</div>
<div class="form-group">
<input type="text" class="form-control" id="username" name="username" placeholder="Username" required="" />
</div>
<div class="form-group">
<input type="password" class="form-control" id="username" name="username" placeholder="Username" required="" />
</div>
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
<button type="submit" class="btn btn-primary block full-width m-b">Login</button>
<small>Forgot password?</small>
<p class="text-muted text-center">
<small>Do not have an account?</small>
</p>
<a class="btn btn-sm btn-white btn-block" href="register.html">Create an account</a>
</form>
It seems like csrf field not working.
I explain, I have normal users website which I refer here by and admin part which is /admin
The login form is correctly displayed. But I when I click submit I get Etat HTTP 405 - Request method 'POST' not supported
Any idea please ?
Here is my security configuration class :
package com.mintad.spring.security;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
/**
* #author Sofiane HAMMAMI
*/
#Configuration
#EnableWebSecurity
#ComponentScan
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
DataSource dataSource;
#Autowired
#Qualifier("customUserDetailsService")
UserDetailsService userDetailsService;
#Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.antMatchers("/admin/**").permitAll()
// .antMatchers("/admin/**").access("hasRole('ADMIN')")
.and().formLogin().loginPage("/login")
.defaultSuccessUrl("/welcome").usernameParameter("username").passwordParameter("password")
.and().exceptionHandling().accessDeniedPage("/404");
}
}
In controller , you defined the form as GET:
#RequestMapping(value = "/admin/login", method = RequestMethod.GET)
and in the form, you are calling it as POST:
form class="m-t" role="form" th:action="#{/admin/login}" method="POST"
change one of these, either at controller or in the form.
If you want to change at controller, replace existing line with:
#RequestMapping(value = "/admin/login", method = RequestMethod.POST)
or you can do it by modifying call from form like
form class="m-t" role="form" th:action="#{/admin/login}" method="GET"
Change your security config to use /admin/login as your login page:
...
.formLogin().loginPage("/admin/login")
Reason you get 405 is, you are trying to submit your form with http post method and defined the end point with http get method.
You need to change your request mapping to method as POST like:
#RequestMapping(value = "/admin/login", method = RequestMethod.POST)
OR you could do vice versa (which is not recommend by security reasons) in your form like:
<form class="m-t" role="form" th:action="#{/admin/login}" method="GET" autocomplete="off">
There are a lot of similar questions to this, I have looked at them but still couldn't figure out what my problem is. Would really appreciate help with this.
This is my controller:
#RestController
#RequestMapping(value = "/game")
public class YatzyController {
#Autowired
private Games games;
#RequestMapping(value = "/{DiceDTO}", method = RequestMethod.POST)
public GameState putGameState(#ModelAttribute("DiceDTO") DiceDTO diceDTO) {
return null;
}
And this is my HTML:
<form method="post" enctype='application/json' action="/game">
<button type="button" onclick="rollDice()">Roll dice</button>
<div>
<input type="text" id="dice1" disabled>
<input type="checkbox" id="keepdice1" value="Keep">
</div>
<div>
<input type="text" id="dice2" disabled>
<input type="checkbox" id="keepdice2" value="Keep">
</div>
<input type="submit">
</form>
Configuration class:
#Configuration
public class YatzyConfiguration {
#Bean
public Games games(){
return new Games();
}
}
Posting to /game
You don't need value = "/{DiceDTO}" in #RequestMapping annotation, because Spring considers it as path's part. So Spring actually maps POST request to /game/somenting path in your case.