Calls from my ionic app to my backend don't work on the smartphone. They work in the browser and in the emulator, but when I install the apk on the smartphone, it doesn't work.
My frontend is in ionic with capacitor.
My backend is java with Springboot.
This is my backend call:
url = `${SERVER_URL}/recipe`;
constructor(private httpClient: HttpClient, private alertController: AlertController) { }
findByIngredients(urlIds): Observable<Recipe[]> {
return this.httpClient.get<Recipe[]>(this.url + urlIds)
.pipe(
retry(2),
catchError(this.handleError))
}
findLastTen(): Observable<Recipe[]> {
return this.httpClient.get<Recipe[]>(`${this.url}/lastTen`)
.pipe(
retry(2),
catchError(this.handleError))
}
This is my backend cors configuration:
#Configuration
public class CorsConfiguration implements WebMvcConfigurer {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS", "HEAD", "TRACE");
}
}
This is one of my endpoints:
#RestController
#RequestMapping("/recipe")
public class RecipeController {
#Autowired
private RecipeFacade facade;
#GetMapping(value = "/lastTen")
public ResponseEntity<List<GetAllRecipesDTO>> getLastTenRecipes() {
List<GetAllRecipesDTO> dto = facade.getLastTenRecipes();
return new ResponseEntity<>(dto, HttpStatus.OK);
}
Error message that is returned:
http failure response for http://...:9000/recipe/lastTen: 0 Unknown Error
Related
updateStudent(userName: string, age: number): Observable<any> {
debugger;
let formData = new FormData();
let reqJson: any = {};
reqJson["userName"] = userName;
reqJson["age"] = age;
formData.append('info', JSON.stringify(reqJson));
return this.httpClient.post(this.url + '/update-student', formData);
}
... and I have this notation in my Java Spring Boot backend:
#CrossOrigin(origins = "*", maxAge = 3600)
#RestController
#RequestMapping("/api")
public class HomeController {
This is an error that I get:
Access to XMLHttpRequest at 'http://localhost:8080/api/update-student' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
You can use this code:
#Configuration
#EnableWebMvc
public class CourseConfig implements WebMvcConfigurer {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("POST", "GET", "OPTIONS", "PUT", "DELETE")
.maxAge(3600);
}
}
Assume that Project is our POJO class. Following function provides to delete a row from database. It is successfully working with POSTMAN requests.
#RestController
#RequestMapping(value = "/project")
#CrossOrigin
public class ProjectController {
private final ProjectServiceImpl projectServiceImpl;
------------
#DeleteMapping
#RequestMapping("/delete/{id}")
public ResponseEntity<Boolean> deleteProject(#PathVariable Long id) {
boolean result = projectServiceImpl.delete(id);
return ResponseEntity.ok(result);
}
------------
}
But requests from Angular project are rejecting with 403 message. And following message is appearing in console screen.
After some searches. I learned, the application have to answer pre-flight requests with 200. To provide this, following function was added to controller.
#GetMapping
#RequestMapping("/delete/{id:[0-9]+}")
public ResponseEntity.BodyBuilder retreive(#PathVariable Long id) {
return ResponseEntity.ok();
}
I used regex for request mapping because without it Spring Framework throws /project/delete/{id} already mapped with another function. Angular get its 200OK for pre-flight request with this way. But the application response is 406 for delete operation. Angular is sending http://localhost:8080/project/delete/2 url to the application. If I send same link without have a function for CORS. Row has id with 2 will delete successfully. Am I missing something?
Sources:
Why Angular sending OPTIONS message before DELETE
How to add CORS support to Spring Boot application
To implement server side proxy: proxy.conf.json
{
"/project/**": {
"target": "http://localhost:8080",
"secure": false
}
}
modified section in angular.json
"serve": {
"builder": "#angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "issue-management:build",
"proxyConfig": "proxy.conf.json"
},
and Angular project started with ng serve --proxy-config proxy.conf.json but result didn't change. Plus, suggestions in this article applied, again result didn't change.
Your applications are running on two different ports, that causing the CORS issue.
Add the proxy(file proxy.conf.json) in your Angular application.
{
"/project/**": {
"target": "http://localhost:8080",
"secure": false
}
}
and run this ng serve --proxy-config proxy.conf.json
Refference Angular doc
Update:
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedMethods("*")
.allowedOrigins("http://localhost:4200");
}
};
}
worked, For some reason Angular proxy is not working
If you are using spring security use the following:
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
// by default uses a Bean by the name of corsConfigurationSource
.cors(withDefaults())
...
}
#Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList("https://example.com"));
configuration.setAllowedMethods(Arrays.asList("GET","POST"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
See spring documentation: https://docs.spring.io/spring-security/site/docs/current/reference/html5/#cors
Global configuration:
#Configuration
#EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
#Value("${cors.origins.urls}")
public String allowedOrigins;
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedOrigins(allowedOrigins.split(","));
}
}
I was having some problem when trying to call RESTful API from Angular to Spring. Here is my typescript class in Angular:
import { Injectable } from "#angular/core";
import { CATEGORIES } from "./mock-category";
import { Observable, of } from "rxjs";
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { Category } from "./category";
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
#Injectable({
providedIn: "root"
})
export class CategoryService {
constructor(private http: HttpClient) { }
private categoryUrl = '/api/category';
getCategories() {
return this.http.get<Category[]>(this.categoryUrl);
}
}
And my controller class in Java:
package controller;
import domain.Category;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import service.CategoryService;
import java.util.List;
#CrossOrigin(origins = "http://localhost:4200", methods = { RequestMethod.POST, RequestMethod.GET, RequestMethod.DELETE,
RequestMethod.PUT })
#RestController
#RequestMapping({"/api"})
public class CategoryController {
#Autowired
private CategoryService categoryService;
#GetMapping("/categories")
public List findAll(){
return categoryService.findAll();
}
}
I managed to start the maven already but when I try to fetch from Angular, I am getting this error message in console:
zone.js:3243 GET http://localhost:4200/api/category 404 (Not Found)
Any ideas? Thanks in advance!
That's because you are requesting on url http://localhost:4200/. But on 4200 port your angular app is running, not the backend. Backend will most probably be running on 8080 port, so you need to specify full URL of backend service. like:
private categoryUrl = 'http://localhost:8080/api/categories';
NOTE: I just assumed your backend is running on 8080, you need to add the port on which you are running your backend. Also change /category to /categories as you have specified in your Service
For your CORS issue you can try this :
#Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
#Override
public void addCorsMappings(CorsRegistry registry) {
// localhost:4200 (for dev) or YOUR_SERVER:8080 (for production)
registry.addMapping("/api/**").allowedOrigins("http://localhost:4200", "http://YOUR_SERVER:8080").allowCredentials(true);
}
}
And here are my headers for client side :
const httpOptions = {
headers: new HttpHeaders({
'Content-Type': 'application/json',
'withCredentials': 'true'
})
};
Try adding this as java Configuration file under SpringBootApp package:
#Configuration
#EnableWebSecurity
public class AppSecurity extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().cors().disable().httpBasic()
.and()
.authorizeRequests()
.antMatchers("/api/categories").permitAll()//Permits this api for all
.anyRequest().authenticated();
}
}
Drawback of Ans- https://stackoverflow.com/a/56555192/6582610 is you have to change in every ts service file and add domain in every URL
rather than you can create Interceptor for this
Use the new HttpClient Interceptor to manipulate your request.
Create a proper injectable that implements HttpInterceptor:
import {Injectable} from '#angular/core';
import {HttpEvent, HttpInterceptor, HttpHandler, HttpRequest} from '#angular/common/http';
import {Observable} from 'rxjs/Observable';
#Injectable()
export class APIInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const apiReq = req.clone({ url: `http://localhost:8080/${req.url}` });
return next.handle(apiReq);
}
}
The HttpInterceptor can clone the request and change it as you wish, in this case I added localhost path in your every api URL http://localhost:8080/.
Provide the AppModule with the following configurations:
provide: HTTP_INTERCEPTORS,
useClass: APIInterceptor,
multi: true,
}
]
Now all your requests will start with http://localhost:8080, you don't have to go to every file and change it.Also if you have multiple domain you can configure here with if- else condition.
I have a Spring boot application with a rest controller and an Angular application as frontend.
For the moment they are both running in localhost and SpringSecurity is enabled is Spring.
Originally I was unable the make a getRequest from Angular to Spring because of Cors. I added #CrossOrigin to my restContoller and now I'm able to do a Get request from angular to Spring.
Now I have the same problem with post request. I want to send some form data from angular to Spring but I always get an error in Chrome. I added #CrossOrigin here as well but I still have the problem.
If I try a post request with postmen it's working just fine
zone.js:3243 Access to XMLHttpRequest at 'localhost:8080/rest/contact' from origin 'http://localhost:4200' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https.
contact.component.ts:51 HttpErrorResponse {headers: HttpHeaders, status: 0, statusText: "Unknown Error", url: "localhost:8080/rest/contact", ok: false, …}
This is my security configuration:
#EnableGlobalMethodSecurity(prePostEnabled = true)
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetailsService userDetailsService;
#Override
protected void configure (AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(getPasswordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors();
http
.authorizeRequests()
.antMatchers("/admin/**").authenticated()//.hasAnyRole("ADMIN","USER")
.and().formLogin().loginPage("/login").permitAll()
.and().logout();
http.csrf().disable();
//http.headers().frameOptions().disable();
}
private PasswordEncoder getPasswordEncoder() {
return new PasswordEncoder() {
#Override
public String encode(CharSequence charSequence) {
return charSequence.toString();
}
#Override
public boolean matches(CharSequence charSequence, String s) {
return encode(charSequence).equals(s);
}
};
}
}
My Cors configuration:
#Configuration
public class CorsConfiguration {
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
};
}
}
My rest controller:
#RestController()
#CrossOrigin(origins = "http://localhost:4200/**", maxAge = 3600)
public class GymRestController {
private final GymRepository gymRepository;
GymRestController (GymRepository gymRepository) {
this.gymRepository = gymRepository;
}
#GetMapping("/rest/gyms")
public List<Gym> findAll() {
return gymRepository.findAll();
}
#PostMapping ("/rest/contact")
public void submitContact(#RequestBody ContactForm contactForm) {
System.out.println(contactForm);
}
}
and my on submit method in angular
onSubmit() {
this.submitted = true;
if (this.messageForm.invalid) {
return;
}
this.success = true;
this.contactModel.fromName = this.messageForm.get('name').value;
this.contactModel.fromMail = this.messageForm.get('email').value;
this.contactModel.subject = this.messageForm.get('subject').value;
this.contactModel.message = this.messageForm.get('message').value;
let url = "http://localhost:8080/rest/contact";
// let url = "https://cors.io/?localhost:8080/rest/contact"
this.http.post(url, this.contactModel).subscribe(
res => console.log("success"),
error => console.log(error),
() => console.log("complete")
);
}
I've been trying for 3 days to get this working without any luck
Any help would be appreciated
I finally found the solution. I had to enable cors in Spring Security and disable csrf
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors().and()
.authorizeRequests()
.antMatchers("/admin/**").authenticated()//.hasAnyRole("ADMIN","USER")
.and().formLogin().loginPage("/login").permitAll()
.and().logout();
http.csrf().disable();
http.headers().frameOptions().disable();
}
I had to removed #CrossOrigin from the controller and I added the following configuration:
#Configuration
public class CorsConfiguration {
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedMethods("*")
.allowedOrigins("http://localhost:4200");
}
};
}
}
Following on Spring io link :
https://spring.io/blog/2015/06/08/cors-support-in-spring-framework
If you are using Spring Boot, it is recommended to just declare a WebMvcConfigurer bean as following:
#Configuration
public class MyConfiguration {
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**");
}
};
}
}
You can easily change any properties, as well as only apply this CORS configuration to a specific path pattern:
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://domain2.com")
.allowedMethods("PUT", "DELETE","POST")
.allowedHeaders("header1", "header2", "header3")
.exposedHeaders("header1", "header2")
.allowCredentials(false).maxAge(3600);
}
Above you can replace http://domain2.com with your localhost or required host/url.
According to the spring boot documentation I added bean
#Bean
WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("http://localhost:3000");
}
};
}
to enable global access from localhost:3000 , which is my frontend app.
I also use spring security, so if user enter localhost:8080/something he is redirected to login page ( if not logged ) . The problem is that this global cors configuration doesn't work.
I have simple controller which returns
List<String>
On the other hand I have angular service, which is responsible for making a get request to the server. It looks like this :
this.http.get("http://localhost:8080/words", {
headers: new Headers({
'Authorization': 'Basic ' + btoa('login:password')
})
}).map((res:Response) => res.json())
.subscribe(
data => { this.words = data},
err => console.error('Error : ' + err),
() => console.log('done')
);
and as a result I can see in google chrome console :
XMLHttpRequest cannot load http://localhost:8080/words. Response for preflight is invalid (redirect)
How can I fix this ?
This is because your front end application makes an OPTIONS HTTP before actual data transfer happens. Try adding this configuration to your spring project:
#Configuration
#EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
#Value("${angular}")
private String angularOrigin;
#Bean
public WebMvcConfigurer corsConfigurer(){
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry
.addMapping("/**")
.allowedOrigins(angularOrigin)
.allowedHeaders("Authorization", "Cache-Control", "Content-Type", "Accept", "X-Requested-With", "Access-Control-Allow-Origin", "Access-Control-Allow-Headers", "Origin")
.exposedHeaders("Access-Control-Expose-Headers", "Authorization", "Cache-Control", "Content-Type", "Access-Control-Allow-Origin", "Access-Control-Allow-Headers", "Origin")
.allowedMethods("PUT","GET","POST","DELETE","OPTIONS");
}
};
}
}