Spring MVC #GetMapping #ModelAttribute percent(%) symbol gives null value - spring

Here is my controller:
#CrossOrigin(origins = "http://localhost:3000")
#RestController
#RequestMapping("/api/v1/employees")
public class EmployeeController {
#Autowired
private EmployeeService employeeService;
#GetMapping()
public List<Employee> getEmployeesBySearch(#Valid #ModelAttribute SearchDto searchDto) {
return employeeService.getEmployeesBySearch(searchDto);
}
}
And Here is my SearchDto:
public class SearchDto {
private String firstName;
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
}
.
http://localhost:8080/api/v1/employees?firstName=%%%
http://localhost:8080/api/v1/employees?firstName=a%
http://localhost:8080/api/v1/employees?firstName=%a
Whenever there's percent(%) symbol in my GET request, it always give null value.

You should encode it.
https://www.urlencoder.org/
a% -> a%25
%%% -> %25%25%25
name%surname -> name%25surname
Your final url look like below
http://localhost:8080/api/v1/employees?firstName=a%25

Related

Can I return DTO and domain entities from services?

I have a spring-boot application and I use DTO like that:
Service
#Service
public class UnitOfMeasureServiceImpl implements IUnitOfMeasureService {
private final IUnitsOfMeasureRepository unitOfMeasureRepository;
#Autowired
public UnitOfMeasureServiceImpl(IUnitsOfMeasureRepository unitOfMeasureRepository) {
this.unitOfMeasureRepository = unitOfMeasureRepository;
}
#Override
public UnitOfMeasureDTO getUnitOfMeasureById(UUID id) {
Optional<UnitOfMeasure> optionalUnitOfMeasure = unitOfMeasureRepository.findById(id);
if (!optionalUnitOfMeasure.isPresent()){
// throw new ComponentNotFoundException(id);
return null;
}
return UnitOfMeasureDTO.factory(optionalUnitOfMeasure.get());
}
dto:
#Data
#JsonInclude(JsonInclude.Include.NON_NULL)
public class UnitOfMeasureDTO {
private String id;
private String name;
private String description;
private String sourceInfoCompanyName;
private String originalId;
public static UnitOfMeasureDTO factory(UnitOfMeasure unitOfMeasure) {
UnitOfMeasureDTO dto = new UnitOfMeasureDTO();
dto.id = unitOfMeasure.getId().toString();
dto.name = unitOfMeasure.getName();
dto.description = unitOfMeasure.getDescription();
dto.sourceInfoCompanyName = unitOfMeasure.getSourceInfo().getSourceCompany().getName();
dto.originalId = unitOfMeasure.getOriginalId();
return dto;
}
}
controller:
#RestController
#RequestMapping(UnitOfMeasureController.BASE_URL)
public class UnitOfMeasureController {
public static final String BASE_URL = "/api/sust/v1/unitOfMeasures";
private final IUnitOfMeasureService unitOfMeasureService;
public UnitOfMeasureController(IUnitOfMeasureService unitOfMeasureService) {
this.unitOfMeasureService = unitOfMeasureService;
}
#GetMapping(path = "/{id}")
#ResponseStatus(HttpStatus.OK)
public UnitOfMeasureDTO getUnitOfMeasureDTO(#PathVariable("id") UUID id) {
UnitOfMeasureDTO unitOfMeasureDTO = unitOfMeasureService.getUnitOfMeasureById(id);
return unitOfMeasureDTO;
}
So in my service I have getUnitOfMeasureById(UUID id) that return a UnitOfMeasureDTO.
Now I need to call, from another service, getUnitOfMeasureById(UUID id) that return the domain entity UnitOfMeasure. I think it's correct to call a service method from another service (not a controller method!) and the separation between business logic is at the service layer. So is it correct to have 2 methods: getUnitOfMeasureDTOById and getUnitOfMeasureById in the service? (getUnitOfMeasureDTOById call getUnitOfMeasureById to avoid code duplication)

Converter works for RequestParameter but not for RequestBody field

I have the following converter:
#Component
public class CountryEnumConverter implements Converter<String, CountryEnum> {
#Override
public CountryEnum convert(String country) {
CountryEnum countryEnum = CountryEnum.getBySign(country);
if (countryEnum == null) {
throw new IllegalArgumentException(country + " - Country is not supported!");
}
return countryEnum;
}
}
Registered it is invoked when used for RequestParam
#GetMapping(value = RestApiEndpoints.RESULTS, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<ResultDto> getResults(
Principal principal,
#RequestParam CountryEnum country) {
....
}
But this converter is never invoked when used for field in the RequstBody:
#GetMapping(value = RestApiEndpoints.RESULTS, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<ResultDto> getResults(
Principal principal,
#RequestBody MyBody myBody) {
....
}
public class MyBody {
#NotNull
private CountryEnum country;
public MyBody() {
}
public CountryEnum getCountry() {
return country;
}
public void setCountry(CountryEnum country) {
this.country = country;
}
}
Your existing org.springframework.core.convert.converter.Converter instance will only work with data submitted as form encoded data. With #RequestBody you are sending JSON data which will be deserialized using using the Jackson library.
You can then create an instance of com.fasterxml.jackson.databind.util.StdConverter<IN, OUT>
public class StringToCountryTypeConverter extends StdConverter<String, CountryType> {
#Override
public CountryType convert(String value) {
//convert and return
}
}
and then apply this on the target property:
public class MyBody {
#NotNull
#JsonDeserialize(converter = StringToCountryTypeConverter.class)
private CountryEnum country;
}
Given the similarity of the 2 interfaces I would expect that you could create one class to handle both scenarios:
public class StringToCountryTypeConverter extends StdConverter<String, CountryType>
implements org.springframework.core.convert.converter.Converter<String, CountryType> {
#Override
public CountryType convert(String value) {
//convert and return
}
}
I found out that if I add the following code to my CountryEnum will do the trick.
#JsonCreator
public static CountryEnum fromString(String value) {
CountryEnumConverter converter = new CountryEnumConverter();
return converter.convert(value);
}

Spring Boot - Apache Derby duplicating IDs of a ListArray objects

This little project follows a basic MVC pattern, i'm using spring boot and apache derby as an embedded data base.
1) When adding a hardcoded object list inside service class, they all share the same id. Is there an explanation for this behavior ?
This shows the problem (Don't mind the 'kkk' objects, i've solved that part already)
Screen1
So this is the object account i'm working with :
#Entity
public class Account {
#Id
#Column(name="id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
private String owner;
private double budget;
private double budgetInvest;
private double budgetFonction;
public Account() {
}
public Account(String owner, double budget, double budgetInvest, double budgetFonction
) {
this.owner=owner;
this.budget = budget;
this.budgetInvest = budgetInvest;
this.budgetFonction = budgetFonction;
}
public Account (String owner, double budget) {
this.owner = owner;
this.budget=budget;
}
public Account (String owner) {
this.owner=owner;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public double getBudget() {
return budget;
}
public void setBudget(double budget) {
this.budget = budget;
}
public double getBudgetInvest() {
return budgetInvest;
}
public void setBudgetInvest(double budgetInvest) {
this.budgetInvest = budgetInvest;
}
public double getBudgetFonction() {
return budgetFonction;
}
public void setBudgetFonction(double budgetFonction) {
this.budgetFonction = budgetFonction;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
}
These are the lines responsible for displaying the objects inside the view :
<tr th:each="account : ${accounts}">
<td th:text="${account.id}">id</td>
<td><a href="#" th:text="${account.owner}">Title
...</a></td>
<td th:text="${account.budget}">Text ...</td>
</tr>
Here is the controller :
#Controller
public class AccountController {
#Autowired
private AccountService accountService;
#RequestMapping(value="/", method=RequestMethod.GET)
public String index() {
return "index";
}
#RequestMapping(value="/accountAdd", method=RequestMethod.GET)
public String addAccount(Model model) {
model.addAttribute("account", new Account());
return "accountAdd";
}
#RequestMapping(value="/accountAdd", method=RequestMethod.POST)
public String postAccount(#ModelAttribute Account account) {
accountService.addAccount(account);
return "redirect:listAccount";
}
#RequestMapping(value="/listAccount", method=RequestMethod.GET)
public String listAccount(Model model) {
System.out.println(accountService.getAllAccounts());
model.addAttribute("accounts",accountService.getAllAccounts());
return "listAccount";
}
}
And finally the service class :
#Service
public class AccountService {
#Autowired
private AccountRepository accountRepository;
public List<Account> getAllAccounts(){
List<Account>accounts = new ArrayList<>(Arrays.asList(
new Account("Maths Department",1000000,400000,600000),
new Account("Physics Department",7000000,200000,500000),
new Account("Science Department",3000000,700000,1000000)
));
accountRepository.findAll().forEach(accounts::add);
return accounts;
}
public Account getAccount(long id) {
return accountRepository.findById(id).orElse(null);
}
public void addAccount(Account account) {
accountRepository.save(account);
}
public void updateAccount(long id, Account account) {
accountRepository.save(account);
}
public void deleteAccount(long id) {
accountRepository.deleteById(id);
}
}
Ok, so while i haven't yet found the exact answer as to why it affects the same id for every object in a static list.
I found an elegant workaround to not only solve the issue but also enhance the structure of the code.
Instead of doing whatever barbaric initialization I was trying to perform, It's way better to do this inside the main class :
#SpringBootApplication
public class PayfeeApplication {
#Autowired
private AccountRepository accountRepository;
public static void main(String[] args) {
SpringApplication.run(PayfeeApplication.class, args);
}
#Bean
InitializingBean sendDatabase() {
return () -> {
accountRepository.save(new Account("Maths Department",1000000,400000,600000));
accountRepository.save(new Account("Physics Department",7000000,200000,500000));
accountRepository.save(new Account("Science Department",3000000,700000,1000000));
};
}
}

Java Spring 4 (Annotated) Rest Controller not being hit by REST Client tool in Firefox

Hi,
I have a problem that is very confusing for me because the mapping should work and it looks like it does map when the Spring Boot is started in debug mode. I don't know where else I can check for an obvious solution to this problem.
Here is the application.properties:
server.port=8082
server.contextPath = /
Here is the SpringBootInitializer class that adds a further "/api" to the >Servlet registration:
public class App extends SpringBootServletInitializer {
#Bean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
#Bean
public ServletRegistrationBean dispatcherServletRegistration() {
final ServletRegistrationBean registration = new ServletRegistrationBean(dispatcherServlet(), "/api/*");
final Map<String, String> params = new HashMap<String, String>();
params.put("contextClass", "org.springframework.web.context.support.AnnotationConfigWebApplicationContext");
params.put("contextConfigLocation", "org.spring.sec2.spring");
params.put("dispatchOptionsRequest", "true");
registration.setInitParameters(params);
registration.setLoadOnStartup(1);
return registration;
}
//
#Override
protected SpringApplicationBuilder configure(final SpringApplicationBuilder application) {
return application.initializers(new MyApplicationContextInitializer()).sources(App.class);
}
public static void main(final String... args) {
new SpringApplicationBuilder(App.class).initializers(new MyApplicationContextInitializer()).run(args);
}
}
Here is the Controler which adds a further "users" to the mapping. The method >which I have set a debug point is the findAll and requires no futher mapping to >get to it (i.e. the root of /users/:
#Controller
#RequestMapping(value = users)
public class UserController extends AbstractController<User> {
#Autowired
private IUserService userService;
public UserController() {
super(User.class);
}
// API
// find
#RequestMapping(method = RequestMethod.GET)
#ResponseBody
public void getItsWorking() {
System.out.println("It's Working!!!");
}
}
Here is the User entity:
#Entity
public class User implements IEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="user_id")
private Long user_id;
#Column(name = "username", unique = true, nullable = false)
private String name;
#Column(unique = true, nullable = false)
private String email;
#Column(nullable = false)
private String password;
#Column(nullable = false)
private Boolean locked;
public User() {
super();
}
public User(final String nameToSet, final String passwordToSet, /*final
Set<Role> rolesToSet,*/ final Boolean lockedToSet) {
super();
name = nameToSet;
password = passwordToSet;
locked = lockedToSet;
}
// API
public Long getId() {
return user_id;
}
public void setId(final Long idToSet) {
user_id = idToSet;
}
public String getName() {
return name;
}
public void setName(final String nameToSet) {
name = nameToSet;
}
public String getEmail() {
return email;
}
public void setEmail(final String emailToSet) {
email = emailToSet;
}
public String getPassword() {
return password;
}
public void setPassword(final String passwordToSet) {
password = passwordToSet;
}
public Boolean getLocked() {
return locked;
}
public void setLocked(final Boolean lockedToSet) {
locked = lockedToSet;
}
}
Here is the output on my Spring Boot debug when it starts up:
Mapped "{[/users],methods=[GET]}" onto public
java.util.List<org.um.persistence.model.User>
org.um.web.controller.UserController.findAll(javax.servlet.http.HttpServletRequest)
So, it looks like it is mapping correctly, but when I hit it using the Rest >Client tool add on in Firefox, I get the following when doing a "GET" on the >following url: http://localhost:8082/api/users using Content-Type: application/json in my header .
What is going on? Very confused.
You should put a #RequestMapping("/api") on you class, and a #RequestMapping("/users") on your method (that should preferably return something to the client).
This ways your endpoint will be exposed as /api/users and you will be able to easily add further endpoints under /api/* into this class.

Spring MVC Data Binding: bind parameter to an object field with different name

Here is my #Controller:
#RequestMapping(value = "/add")
public void addMember(Member member) {
// ... ...
}
here is Member class:
class Member {
private Integer id;
private String username;
// ... ...
}
And here is the request url:
/add?memberId=1&username=bruce
How can I bind the memberId parameter to Member.id field?
Thanks!
If you don't want to use
#RequestMapping(value = "/add")
public void addMember(#RequestParam(value = "username") String username, ...) {
Member member = new Member(username, ...)
// ...
}
you can implement HandlerMethodArgumentResolver:
public class MemberHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
#Override
public boolean supportsParameter(MethodParameter parameter) {
return Member.class.equals(parameter.getParameterType());
}
#Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
String username = webRequest.getParameter("username");
...
return new Member(username, ...);
}
}
And in config:
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean id="memberResolver" class="com.company ... MemberHandlerMethodArgumentResolver"/>
</mvc:argument-resolvers>
</mvc:annotation-driven>
or JavaConfig:
#Configuration
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
...
#Override
public void addArgumentResolvers(List<Handlermethodargumentresolver> argumentResolvers) {
argumentResolvers.add(new MemberHandlerMethodArgumentResolver());
}
}
IMHO the simplest way to do that is to use an additional setter in Member class :
class Member {
private Integer id;
private String username;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public void setMemberId(Integer memberId) { // this one should to the trick
this.id = memberId;
}
// ... ...
}
You can use #JsonProperty Annotation for it.Like below.
class Member {
#JsonProperty("memberId")
private Integer id;
private String username;
// ... ...
}

Resources