Spring Boot: Is it possible to consume #RequestParam's as json? - java

I have following rest endpoint in spring boot application:
#RequestMapping(method = RequestMethod.POST, value = "/sign-in")
public Client signIn(#RequestParam String username, #RequestParam String password, HttpServletRequest request) {
Client client = authService.signIn(username, password);
return client;
}
I want accept data as application/json.
Is it possible to do this WITHOUT creating class for this like this?:
public class LoginModel {
private String username;
private String password;
...
}
Also, without argument of type Map in signIn method
I need to accept application/json with this signature:
public Client signIn(#RequestParam String username, #RequestParam String password)
Is it possible?

No. I don't think it's possible.
You need to create the intermediary Login object.
#RequestParam mandates you have a request param.

Related

calling rest api with requestbody and authenticationprincipal

I have a rest api which has role based access to its http POST method. It uses spring-security to access api only for authorized users.
I have 2 questions,
how external client can pass request body and user object (#AuthenticationPrincipal) to make this api call
How can I write junit to test below piece of code,
#PreAuthorize("hasAuthority('ADMIN')")
#PostMapping("/api/access/submit")
public ResponseEntity<OrderAdminResponse> create(#RequestBody OrderAdminRequest orderAdminSubmitRequest,#AuthenticationPrincipal UserObject user)
{
return ResponseEntity.accepted().body(orderService.submit(orderAdminSubmitRequest));
}
My User Object is below,
<code>
public class UserObject {
private final String name;
private final String id;
private final String email;
private UserObject(String name, int id, String email){
this.name = name; this.id = id; this.email = email
}
public Collection<String> getRoles() {
return
(Collection)this.getAuthorities().stream()
.map(GrantedAuthority::getAuthority).collect(Collectors.toList());
}
public boolean isUserInRole(String role) {
return this.getAuthorities().stream().anyMatch((a) -> {
return a.getAuthority().equals(role);
})
}
}
</code>
The controller will automatically populate the Authentication Principal, when you make a request with an authenticated user.
For example, if you are using HTTP basic authentication to secure your endpoint, then the principal will be populated from the Authorization header. Your request body remains the same regardless of whether you are extracting the principal or not.
An easy way to test your controller is to use the support provided by Spring Security.
If you are using MockMvc, one option is to use a post processor to call your endpoint with different types of users.
this.mvc.perform(post("/api/access/submit")
.content("...")
.with(user("user").roles("USER")))
.andExpect(status().isUnauthorized());
this.mvc.perform(post("/api/access/submit")
.content("...")
.with(user("admin").roles("ADMIN")))
.andExpect(status().isOk());
You can learn more about Spring Security test support in the documentation here.

MissingServletRequestParameterException with PostMapping in Spring MVC

I'm getting this error when I try to create an instance of an Object and store it into a database:
org.springframework.web.bind.MissingServletRequestParameterException
The code for the method:
#PostMapping(path="accounts/add", consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
public
#ResponseBody
String addAccount(#RequestParam String accountNumber, #RequestParam String accountName ) {
Account account = new Account();
account.setAccountName(accountName);
account.setAccountNumber(accountNumber);
accountRepository.save(account);
return "Saved";
}
When I use PostMan and try to enter the JSON in, it gives me this message. What am I doing wrong?
Since your POSTing (or PUTing?) JSON as the content body, you cannot use #RequestParam to deconstruct it. That is used for query or form parameters. You need to specify a single method parameter with #RequestBody, which is a class that looks something like the following.
public class Account {
public String accountNumber;
public String accountName;
// setters and getters removed for brevity
}
See this answer for more info: #RequestBody and #ResponseBody annotations in Spring

Spring Boot REST: #DeleteMapping that consuming form_urlencoded not work as expect

I'm using Spring boot 1.4.0, Consider below code in a #RestController, what I expect is, the server side will receive a http body with form_urlencoded content type, but unfortunately it demands me a query parameter type with email and token. What's the problem here and how to fix?
#DeleteMapping(consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
#ResponseStatus(HttpStatus.NO_CONTENT)
public void removeAdmin(#RequestParam(value = "email") String email, #RequestParam(value = "token") String token) {
//...
}
#DeleteMapping is only a convenience extension the provides #RequestMapping(method=DELETE) It will not handle request paramters. You will still have to map those in the controllers method signature if you need the data to perform the work.
Since you want a body, You could create an object and mark it as #RequestBody:
public class DeleteBody {
public String email;
public String token;
}
public void removeAdmin(#RequestBody DeleteBody deleteBody) {
...
}

Custom annotation injection with Jersey 1.x

I am using jersey 1.9.1. I have rest method like following where
Authorization header contained encoded credentials such as username
and password and it is parsed in a method and mapped local values.
#PUT
#Path(SystemConstants.REST_MESSAGE_SENDSMS)
#Consumes(MediaType.APPLICATION_JSON)
#Produces({MediaType.APPLICATION_JSON})
public Response sendSms(#HeaderParam("Authorization") String authorization, String param) {
String[] credentials = ImosUtils.getUserCredentials(authorization);
String username = credentials[0];
String password = credentials[1];
}
I am trying to design a way to make this process automatically, without writing same parsing code in each method. I mean I would like to know if writing a special annotation such as HeaderParamExtended to this is used to parse this credentials.
I am using jersey 1.9.1 version as rest api. Where I have to edit a class in that life cycle?
#PUT
#Path(SystemConstants.REST_MESSAGE_SENDSMS)
#Consumes(MediaType.APPLICATION_JSON)
#Produces({MediaType.APPLICATION_JSON})
public Response sendSms(#HeaderParamExtended("Authorization","username") String username, #HeaderParamExtended("Authorization","password") String password, , String param) {
}
Normally you need an InjectableProvider to support the custom injection, and also an Injectable to provide the value.
Here's an example
#BasicAuth
#Target(ElementType.PARAMETER)
#Retention(RetentionPolicy.RUNTIME)
public #interface BasicAuth {
}
InjectableProvider
#Provider
public class BasicAuthInjectionProvider
implements InjectableProvider<BasicAuth, Parameter> {
#Override
public ComponentScope getScope() {
return ComponentScope.PerRequest;
}
#Override
public Injectable getInjectable(ComponentContext cc, BasicAuth a, Parameter c) {
return new BasicAuthInjectable();
}
}
Injectable
public class BasicAuthInjectable extends AbstractHttpContextInjectable<User>{
#Override
public User getValue(HttpContext hc) {
String authHeaderValue = hc.getRequest()
.getHeaderValue(HttpHeaders.AUTHORIZATION);
String[] credentials = ImosUtils.getUserCredentials(authHeaderValue);
return new User(credentials[0], credentials[1]);
}
}
One thing you'll notice is that I have a User class. This is to wrap the username and password, and just have one injection point. i.e.
public Response getSomething(#BasicAuth User user) {
}
I actually tried to do it your way, with
public Response getSomething(#BasicAuth("username") String username,
#BasicAuth("password") String password) {
And in the InjectableProvider get the annotation value from the annotation passed to the getInjectable, then pass that value onto the BasicAuthInjectable. From there check to see if the value is "username" or "password" and return the corresponding value. But for some reason the injection providers were not even called. You can play around with it to see if you can get it to work. But to me the User looks cleaner anyway, and with the two strings, the injection providers are called twice and you need to parse the headers twice. Seems unnecessary.

How to parse JSON Response to POJO with AndroidAnnotations?

I'm using AndroidAnnotations to build a Rest for an Android Application.
On the Serverside im using PHP, which send a json looking like :
{"tag":"register","success":0,"error":2,"msg":"User already existed","body":[]}
I have two POJOS :
User.java:
public class User implements Serializable {
private String name;
private String email;
private String password;
//getter and setter Methods
}
Response.java:
public class RegistrationResponse implements Serializable {
private String tag;
private int success;
private int error;
private String msg;
private String body;
//getter and setter Methods
}
Rest Client:
#Rest(rootUrl = "http://my.domain.com", converters = {
MappingJacksonHttpMessageConverter.class,
StringHttpMessageConverter.class, GsonHttpMessageConverter.class }, interceptors = { MyInterceptor.class })
public interface RestClient extends RestClientErrorHandling {
#Post("/user/register/{name}/{email}/{pass}")
#Accept(MediaType.APPLICATION_JSON_VALUE)
Response sendUserRegistration(User user, String name, String email,
String pass);
RestTemplate getRestTemplate();
}
Activity.java:
//User and Response are POJOs
Response result = RestClient.sendUserRegistration(user,
user.getName(),user.getEmail(),user.getPassword());
But i got an Null Pointer Exception error on Activity.java. But if i change the return value of "sendUserRegistration" function to String all work. So my "Response" POJO seems not to be converted from AndroidAnnotations.
How can i convert the Rest Response to my "Response"-POJO using AndroidAnnotations?
You don't need to return the entire response object per rest call, just set the response to your custom object. Or you can also return a JsonObject also and use gson to convert it later on.
#Rest(rootUrl = "http://my.domain.com", converters = {
MappingJacksonHttpMessageConverter.class,
StringHttpMessageConverter.class, GsonHttpMessageConverter.class }, interceptors = { MyInterceptor.class })
public interface RestClient extends RestClientErrorHandling {
#Post("/user/register/{name}/{email}/{pass}")
#Accept(MediaType.APPLICATION_JSON_VALUE)
User sendUserRegistration(User user, String name, String email,
String pass);
RestTemplate getRestTemplate();
}
then just simply call
User newUser = RestClient.sendUserRegistration(user,
user.getName(),user.getEmail(),user.getPassword());
AA relies on Spring Android RestTemplate to make the rest call. And in order to build requests and handle responses this lib uses converters. And to know which converter the RestTemplate should use, it checks the content-type response header.
As MappingJacksonHttpMessageConverter and GsonHttpMessageConverter handles only http response with content-type=application/json and your result is converted to string, I'm pretty sure you forgot to set this header in your php server. So it send the default one (ie: text/plain) which is only handle by StringHttpMessageConverter.
Also, the body field is an object in your json example, but in your POJO you declared it as a String. So parsing will fail on this point.

Categories