Subscribing to Angular 6 POST request - spring-boot

Scenario
I have an Angular 6 front end that communicates with a Spring Boot back end. The back end uses PostgreSQL as a database. The thing I want to do is to send a username to the data base and then return his email and print it on a page using Angular 6.
But, I am struggling with the Angular POST request where on subscribing to it I get a JSON parsing error in Chrome developer tools.
Code
Spring Boot
This works fine as I have tested it by printing the email returned from the database on the console.
#RequestMapping(value = "/reset", method = RequestMethod.POST)
public String resetMail(#RequestBody String username) {
try{
User user = userService.findByUsername(username);
//System.out.println(user.getEmail()); Testing purpose
return user.getEmail();
}catch(Exception e) {
//e.printStackTrace(); Prints out NullException StackTrace.
return "Not Present";
}
}
Angular 6
In the following code, I think the problem is in the subscription method as I am getting a JSON parsing error in Chrome developer tools.
httpOptions = { headers: new HttpHeaders({'Content-Type':'text/plain', 'Access-Control-Allow-Origin': '*'})};
reset(username:string) {
this.http.post('http://localhost:8090/reset', username, this.httpOptions).subscribe(data=> {
this.email = data as any;
});
}
console.log(this.email) // Prints undefined
Error
HttpErrorResponse {headers: HttpHeaders, status: 200, statusText: "OK", url: "http://localhost:8090/reset", ok: false, …}
>error: {error: SyntaxError: Unexpected token a in JSON at position 0 at JSON.parse (<anonymous>) at XMLHttp…, text: "abcdefg#yahoo.com"}
>headers: HttpHeaders {normalizedNames: Map(0), lazyUpdate: null, lazyInit: ƒ}
message: "Http failure during parsing for http://localhost:8090/reset"
name: "HttpErrorResponse"
ok: false
status: 200
statusText: "OK"
url: "http://localhost:8090/reset"

You want to serialize a string to JSON. If your controller would return an object the #RestController annotation would serialize it properly. In case you want to return just a string you have to return JSONObject.quote(user.getEmail()); You can get the JSONObject dependency from here http://mvnrepository.com/artifact/org.json/json
But I encourage you to always return objects as a response from your rest controllers.

Related

Spring Boot Content Type Error even when specified in Kotlin

I have an issue in Spring Boot with Kotlin
I have a function that accepts all major Content Types as defined below:
#PostMapping(
value = ["/users/new"],
consumes = [
MediaType.APPLICATION_JSON_VALUE,
MediaType.APPLICATION_XML_VALUE,
MediaType.MULTIPART_FORM_DATA_VALUE,
MediaType.APPLICATION_FORM_URLENCODED_VALUE]
)
fun registerNewUser(
#RequestHeader("X-Forward-For") ipAddress: String?,
newUser: NewUser,
request: HttpServletRequest
): ResponseEntity<ObjectNode> {
var realIPAddress = ipAddress
if (realIPAddress == null) {
realIPAddress = request.remoteAddr
}
return userService.registerUser(realIPAddress!!, newUser)
}
Here is how my NewUser class is defined in kotlin
data class NewUser(val email: String?, val password: String?)
Here is how I am doing the check in the registration function
if (!StringUtils.hasText(newUser.email)) {
return responseHandler.errorResponse(
HttpStatus.BAD_REQUEST,
"Please provide an email address"
)
}
Now when I sent a post request with postman and even axios I keep getting the error as shown in the screenshot below
That error message should only be displayed if email address is not provided. But as you can see clearly, the email address is provided in the JSON Payload.
What could be wrong?
Note: This works when the Content-Type is application/x-www-form-urlencoded but doesn't work when the Content-Type is application/json
put #RequestBody before newUser parameter to specify that input should be inside http body part. by default function parameters in spring are considered to be url parameters which can be further clarified with #RequestParam.
there are 2 ways to insert request parameters into http request. one is to attach request parameters to end of the url and the other is to put in http body with application/x-www-form-urlencoded as the content-type.

Axios authentifaction request fails to send header to Spring Boot

I am trying to implement Authorization with Axios on RN part and send the token to Spring Boot backend. I've tried to do it before with simple sending email and password as parameters of GET request and it worked fine, but now when I'am trying to send basic headers with btoa to backend part, it keeps receiving null value.
My React Native part:
login(user) {
const headers = {
authorization: 'Basic ' + btoa(user.email + ':' + user.password)
};
return axios.get(API_URL + 'login', {headers: headers})
.then(response => {
console.log('function called')
And my Controller on Spring Boot:
#RequestMapping(value = {"/login"}, method = RequestMethod.GET)
public ResponseEntity<?> login(Principal principal) {
if(principal == null){
//logout will also use here so we should return ok http status.
return (ResponseEntity<?>) ResponseEntity.badRequest();
}
UsernamePasswordAuthenticationToken authenticationToken =
(UsernamePasswordAuthenticationToken) principal;
When I debug the controller, I see that my method parameter principle is receiving null.
I guess the issue might be somewhere either in the header or Controller parameter, but have real idea.
Headers should be:
{
headers: {
"Authorization": "Basic "...
}
}
Alternatively,
{
auth: {
username: "",
password: ""
}
}
Which will add the basic auth header for you
https://github.com/axios/axios#request-config for reference

Spring + Angular: How to parse ResponseEntity in angular?

I'm using Spring Boot to create an API that needs to be consumed in Angular 4. Spring and Angular are on different ports.
The problem is that Spring's ResponseEntity raises an error in Angular.
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public ResponseEntity getFlow(#PathVariable int id) {
Flow flow = flowService.findById(id);
return new ResponseEntity(flow, HttpStatus.FOUND);
}
Now, I can perfectly use Postman to test the API and it works.
But when I make a request from Angular, it returns an error:
Strangely, it returns an error alongside the requested object.
Now, the cause of the problem is that the Spring Boot application returns a ResponseEntity and not a normal object (like String), and Angular doesn't know how to interpret it. If the controller returns just a Flow object, it works.
How can it be solved using ResponseEntity? Or, how else can I send the object alongside the HTTP status code?
Also, in #RequestMapping put produces = "application/json", and in get request in angular, add http options :
const httpOptions = {
headers: new HttpHeaders({
'Accept': 'application/json',
'Content-Type': 'application/json'
})
};
So your get request looks like this:
this.http.get(url, httpOptions)
As per the document mentioned here
https://docs.angularjs.org/api/ng/service/$http
A response status code between 200 and 299 is considered a success status and will result in the success callback being called. Any response status code outside of that range is considered an error status and will result in the error callback being called. Also, status codes less than -1 are normalized to zero. -1 usually means the request was aborted, e.g. using a config.timeout. Note that if the response is a redirect, XMLHttpRequest will transparently follow it, meaning that the outcome (success or error) will be determined by the final response status code.
As you are sending an instance of ResponseEntity(HttpStatus.Found) whose Http status code is 302 which doesnt fall under the success range thats why error callback is called.
Try returning the content like this
return new ResponseEntity(flow, HttpStatus.OK);

Unsupported Media Type in postman

I am implementing spring security with oauth2 and jwt.
the below is my login function
function doLogin(loginData) {
$.ajax({
url : back+"/auth/secret",
type : "POST",
data : JSON.stringify(loginData),
contentType : "application/json; charset=utf-8",
dataType : "json",
async : false,
success : function(data, textStatus, jqXHR) {
setJwtToken(data.token);
},
error : function(jqXHR, textStatus, errorThrown) {
alert("an unexpected error occured: " + errorThrown);
window.location.href= back+'/login_page.html';
}
});
}
And down I have the Controller
#RequestMapping(value = "auth/secret", method = RequestMethod.POST)
public ResponseEntity<?> createAuthenticationToken(#RequestBody JwtAuthenticationRequest authenticationRequest, Device device) throws AuthenticationException {
System.out.println();
logger.info("authentication request : " + authenticationRequest.getUsername() + " " + authenticationRequest.getPassword());
// Perform the security
System.out.println( authenticationRequest.getUsername()+"is the username and "+authenticationRequest.getPassword());
final Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
authenticationRequest.getUsername(),
authenticationRequest.getPassword()
)
);
SecurityContextHolder.getContext().setAuthentication(authentication);
logger.info("authentication passed");
// Reload password post-security so we can generate token
final UserDetails userDetails = userDetailsService.loadUserByUsername(authenticationRequest.getUsername());
final String token = jwtTokenUtil.generateToken(userDetails, device);
logger.info("token " + token);
// Return the token
return ResponseEntity.ok(new JwtAuthenticationResponse(token));
}
But when I try the post request with the postman it shows me
{
"timestamp": 1488973010828,
"status": 415,
"error": "Unsupported Media Type",
"exception": "org.springframework.web.HttpMediaTypeNotSupportedException",
"message": "Content type 'multipart/form-data;boundary=----WebKitFormBoundaryY4KgeeQ9ONtKpvkQ;charset=UTF-8' not supported",
"path": "/TaxiVis/auth/secret"
}
But when I do cosole.log(data) in the ajax call it prints the token?I could not figure out what is wrong.Any help is appreciated.
You need to set the content-type in postman as JSON (application/json).
Go to the body inside your POST request, there you will find the raw option.
Right next to it, there will be a drop down, select JSON (application.json).
Http 415 Media Unsupported is responded back only when the content type header you are providing is not supported by the application.
With POSTMAN, the Content-type header you are sending is Content type 'multipart/form-data not application/json. While in the ajax code you are setting it correctly to application/json. Pass the correct Content-type header in POSTMAN and it will work.
I also got this error .I was using Text inside body after changing to XML(text/xml) , got result as expected.
If your request is XML Request use XML(text/xml).
If your request is JSON Request use JSON(application/json)
If you are still failing with Unsupported Media Type in postman
when calling a SOAP endpoint you could try:
Content-Type: application/soap+xml
i was also having a similar issue. in my case i made two changes
Click on headers tag and add a key 'Content-Type' with Value 'application/json'
Second step is to click on Body tab and select 'raw' radio button and select type as 'JSON' from dropdown as shown below
I had this problem. I had authentication on the authentication tab set up to pass credentials in body.
This error occurred for me when I had the Body set to None.
So I needed an empty body in postman, set to raw JSON to allow this to work even though my main request was parameters in the querystring.
{
}
When this was happening with me in XML;
I just changed "application/XML" to be "text/XML",
which solved my problem.

Enable authenticator manually

Currently my client authenticates request only on case of 401 response:
this.client.authenticator(new okhttp3.Authenticator() {
public Request authenticate(Route route, Response response) throws IOException {
String credentials = authenticator.getCredentials();
if (credentials.equals(response.request().header("Authorization"))) {
throw new TraversonException(401, "Unauthorized", response.request().url().toString());
} else {
defaultHeader("Authorization", credentials);
Request.Builder newRequest = response.request().newBuilder()
.headers(Headers.of(defaultHeaders));
return newRequest.build();
}
});
But I'd like to change this behavior and be able to call it either manually or auto per first call? Is it possible somehow?
If the authentication is predictably required and not related to a proxy, then the solution is to implement an Interceptor instead of Authenticator.
OkHttpClient.Builder clientBuilder = ...;
clientBuilder.networkInterceptors().add(0, myInterceptor);
client = clientBuilder.build();
Example Interceptor https://github.com/yschimke/oksocial/blob/48e0ca53b85e608443eab614829cb0361c79aa47/src/main/java/com/baulsupp/oksocial/uber/UberAuthInterceptor.java
n.b. There is discussion around possible support for this usecase in https://github.com/square/okhttp/pull/2458. One issue with current Authenticator API is that it assumes a Response from the failed (401) request.

Resources