I can't make heads or tails of what I'm doing wrong here. I'm calling a new controller I'm setting up using JavaScript fetch(). I'm using MVC 5 within the React template.
The template controller (WeatherForecastController) works, if I call it using fetch, it'll hit the controller. Yet using the exact same implementation but for a new controller it won't touch the new controller. However Network debugger shows that it called and returned OK. If I call the controller directly through the browser, it's as if there isn't a route for it at all! No json is returned from my new controller vs the template controller. I'm simply trying to define a new controller I can fetch data from my Middle tier (.NET 6). Using .Net 6.0 as well.
Naming convention is correct as it ends with Controller in call.
DEFAULT TEMPLATE CONTROLLER CALL FROM JS
async populateWeatherData() {
const response = await fetch('weatherforecast');
const data = await response.json();
this.setState({ forecasts: data, loading: false });
}
DEFAULT TEMPLATE CONTROLLER
using Microsoft.AspNetCore.Mvc;
namespace ReactTest.Controllers
{
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
}
}
Yet I call my controller I setup with the exact same implementation. If I try to call it I get exactly nothing!
NEW CONTROLLER TEMPLATE CALL FROM JS
async populateWeatherData() {
const response = await fetch('database');
const data = await response.json();
this.setState({ forecasts: data, loading: false });
}
IMPLEMENTATION OF NEW CONTROLLER (That I actually want to call)
using Microsoft.AspNetCore.Mvc;
namespace ReactTest.Controllers
{
[ApiController]
[Route("[controller]")]
public class DatabaseController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<DatabaseController> _logger;
public DatabaseController(ILogger<DatabaseController> logger)
{
_logger = logger;
}
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
}
}
I've tried everything but modifying the default routing (the controller lives in the same folder as the template controller, so this shouldn't be why).
Before anyone asks:
ROUTING CONFIG:
app.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
ProjectFileDirectory
DatabaseControllerResponseHeader
So what exactly am I doing wrong? Yes I have read the documentation, no it hasn't helped.
Thanks in advance!
Related
I'm attempting to use fetch api to load data from my asp .net core web api application to my react component. But I'm very confused that when I use the example await fetch('weatherforecast') in FetchData component to call WeatherForecastController. It works. I did a debugging in the example code, the function in WeatherForecastController is well reached, but if I use this idea in my project, it does not work, my function in debugging is not reached. Could someone explain the usage of fetch api in my case?
The controller class
[ApiController]
[Route("[controller]")]
public class HelloController : ControllerBase
{
[HttpGet]
public IEnumerable<T> Get()
{
//do something
}
}
My react component
const response = await fetch('Hello');
In program.cs (I'm using .net 6)
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
IConfigurationRoot configuration = new ConfigurationBuilder()
.SetBasePath(AppDomain.CurrentDomain.BaseDirectory)
.AddJsonFile("appsettings.json")
.Build();
builder.Services.AddDbContext<T>(options =>
options.UseSqlServer(configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddControllers();
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new() { Title = "MY API", Version = "v1" });
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (builder.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "MY APP v1"));
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.UseRouting();
app.MapControllers();
app.Run();
I found something interesting in setupProxy
const { createProxyMiddleware } = require('http-proxy-middleware');
const context = [
'/weatherforecast',
'/controllerName1',
'/controllerName2',
];
module.exports = function (app) {
const appProxy = createProxyMiddleware(context, {
target: 'https://localhost:5001',
secure: false
});
app.use(appProxy);
};
The default weatherforecast controller works, de route is correct via debugging. But if I try it in another controller, it does not work. Can someone explain it pls?
Can you check Startup.cs? May be you have route prefix setup there. like api. So in that case your url would be /api/hello
Or configure swashbuckle that will tell exact route and also give you option to try your API
In my main.js file I want to have data from spring boot controller in some specific json format.
eg.
var contactsJSON = [{"userId": "firstuser", "displayName": "firstuser"},
{"userId": "seconduser", "displayName": "seconduser"}];
Now in my controller "/users" i'm returning list of all users.
I want that at the time of application loading the value of contactsJSON gets populated dynamically in required json format (I only need username to create JSON).
main.js
var contactsJSON = [{"userId": "firstuser", "displayName": "firstuser"
},
{"userId": "seconduser", "displayName": "seconduser"
},
{"userId": "thirduser", "displayName": "thirduser"
}
];
UserController.java
#RequestMapping(value = "/users", method = RequestMethod.GET)
public String viewUsers(Model model) {
List<User> list = userService.getAllUsers();
model.addAttribute("userList", list);
return "welcome";
}
List contains private Long id;
private String username;
private String password;
private String passwordConfirm;
I want to dynamically provide value of contactsJSON in my javascript file. How can I do this ?
You can either return a response as String or you can use ResponseEntity Object provided by Spring as below. By this way, you can also return Http status code which is more helpful in the web service call.
#RestController
#RequestMapping("/api")
public class MyRestController
{
#RequestMapping(value = "/users", method = RequestMethod.GET, produces=MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Object> viewUsers(Model model) {
{
//Get data from service layer into entityList.
List<User> list = userService.getAllUsers();
List<JSONObject> entities = new ArrayList<JSONObject>();
for (Entity n : list) { // You can iterate over the list and add in json format below is the example for same
//JSONObject entity = new JSONObject();
//entity.put("aa", "bb");
//entities.add(entity);
}
return new ResponseEntity<Object>(entities, HttpStatus.OK);
}
}
First, a Thymeleaf recommendation
I highly recommend Thymeleaf over JSP templates. For one thing, it makes inline object-to-JSON expressions very easy. For example...
<script th:inline="javascript">
const contactsJSON = [[${userList}]];
</script>
See https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#advanced-inlined-evaluation-and-javascript-serialization
If you don't want to switch, I would recommend adding an AJAX call to fetch your user data. On the Java side, it might look like this
#GetRequest(path = "/users", produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public List<User> getAllUsers() {
return userService.getAllUsers();
}
Spring will automatically serialize the response as JSON.
Then in your welcome.jsp
<script>
// load the current URL (ie /users) but requesting JSON
fetch('', {
headers: {
Accept: 'application/json'
}
}).then(res => res.json()).then(contactsJSON => {
// you can now use contactsJSON here
})
</script>
I have a Web Application with Angular as front end and Spring Boot webservices as the backend. i am able to perform "Web Parameter Tampering" attack on one of my post call which inserts data in to my database. Has anyone solved this before ?
Appreciate your help.
Angular :
$scope.submitRequest = function() {
ar.createRequest.save({
orderNum : orderNum
...
}, function() {
}, function (httpResponse){
});
};
SpringBoot :
#RequestMapping(value = "/v1/...", method = RequestMethod.POST)
#ResponseStatus(HttpStatus.CREATED)
#CrossOrigin
public ResponseEntity<?> createRequest(#RequestBody OrderDTO orderdto){
RestTemplate restTemplate = new RestTemplate();
.......
.....
return new ResponseEntity<>(HttpStatus.CREATED);
}
Let me give an example of my problem,
I have registered my routes as following(RouteConfig.cs):
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
if you look at my controller, it has the following function;
[Route("all")]
public HttpResponseMessage Get(HttpRequestMessage request)
{
return CreateHttpResponse(request, () =>
{
HttpResponseMessage response = null;
var HolidayCalendars = _holidayCalendarsRepository.GetAll().ToList();
IEnumerable<HolidayCalendarViewModel> holiVm = Mapper.Map<IEnumerable<HolidayCalendar>, IEnumerable<HolidayCalendarViewModel>>(HolidayCalendars);
response = request.CreateResponse<IEnumerable<HolidayCalendarViewModel>>(HttpStatusCode.OK, holiVm);
return response;
});
}
up to this point everything is going great. My page loads with the requested data. Now, when I go and add another function, for example;
[Route("allHolidays/{id:int}")]
public HttpResponseMessage GetHolidays(HttpRequestMessage request, int id)
{
return CreateHttpResponse(request, () =>
{
HttpResponseMessage response = null;
HolidayCalendar Calendar = _holidayCalendarsRepository.GetSingle(id);
var Holidays = Calendar.Holidays.OrderBy(s => s.HolidayDate).ToList();
IEnumerable<HolidayViewModel> holidayVm = Mapper.Map<IEnumerable<Holiday>, IEnumerable<HolidayViewModel>>(Holidays);
response = request.CreateResponse<IEnumerable<HolidayViewModel>>(HttpStatusCode.OK, holidayVm);
return response;
});
}
I will get the following error in my webpage;
Failed to load resource: the server responded with a status of 400 (Bad Request)
Strange thing is, my request did not change, there is only a new Controller in my api.
This should not be happening because my code is requesting different routes, for example;
function loadData() {
apiService.get('/api/HolidayCalendars/all', null,
HolidayCalendarLoadCompleted,
HolidayCalendarLoadFailed);
}
or
function loadData() {
apiService.get('/api/HolidayCalendars/allHolidays?id=' + $routeParams.id, null,
HolidaysLoadCompleted,
HolidaysLoadFailed);
}
Any ideas?
constructor class WebApiConfig:
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new {id = RouteParameter.Optional }
);
}
Your route for allHolidays implies this format
/api/HolidayCalendars/allHolidays/123
According to your route attribute
[Route("allHolidays/{id:int}")]
But you've passed the id as a querystring parameter
api/HolidayCalendars/allHolidays?id=123
Looks like you're using AttributeRouting (http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2) on the Controllers :
[Route("all")]
but you're using standard routing in the config:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Try activate the AttributeRouting with:
configuration.MapHttpAttributeRoutes();
where configuration is the actual instance of HttpConfiguration.
The issue is in your WebApiConfig. In the routeTemplate, you haven't specified an action.
routeTemplate: "api/{controller}/{id}",
If I remember correctly, this is the default config for WebAPI. It filters requests on a controller by verb. That's why when you call
apiService.get('/api/HolidayCalendars/all'.....)
It returns the Get() method on your HolidayCalendars controller.
To fix the issue, add the {action} parameter to your routeTemplate:
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new {id = RouteParameter.Optional }
);
}
I finally found the solution!
At the top of my code i had a reference to System.Web.Mvc. This way the routing and RESTful functions were not interpreted as it should in a web Api. This caused some strange functioning in my app.
Solution:
Change
using System.Web.Mvc;
To
using System.Web.Http;
This evaded me for three days,until I came along the following answer:
https://stackoverflow.com/a/21999235/6033193
I'm a very new comer to the asp.net web api world. I've got the basic understanding of get(), put(), post() and delete.
In my application, I require two more get() method. An explanation is given below-
public class StudentController : ApiController
{
public IEnumerable Get()
{
//returns all students.
}
//I would like to add this method=======================
[HttpGet]
public IEnumerable GetClassSpecificStudents(string classId)
{
//want to return all students from an specific class.
}
//I also would like to add this method=======================
[HttpGet]
public IEnumerable GetSectionSpecificStudents(string sectionId)
{
//want to return all students from an specific section.
}
public Student Get(string id)
{
//returns specific student.
}
}
There is already a $http.get(..) in angularjs controller.
My question is, how can I call the two additional get() methods from angular controller.
Well, I haven't used asp.net mvc in forever. But you be able to do something like:
public class StudentController : ApiController
{
[Route("students")]
public IEnumerable Get()
{
//returns all students.
}
//I would like to add this method=======================
[HttpGet]
[Route("students/class/{classId}")]
public IEnumerable GetClassSpecificStudents(string classId)
{
//want to return all students from an specific class.
}
//I also would like to add this method=======================
[HttpGet]
[Route("students/section/{sectionId}")]
public IEnumerable GetSectionSpecificStudents(string sectionId)
{
//want to return all students from an specific section.
}
[Route("students/{id}")]
public Student Get(string id)
{
//returns specific student.
}
}
You could also specify routes in the routeconfig like this:
routes.MapRoute(
name: "students",
url: "students/class/{classId}",
defaults: new { controller = "Student", action = "GetClassSpecificStudents", id = UrlParameter.Optional }
);
You have to try for your self. And you can read more about it here and here.
Not that you have your specified routes you can add angular $http.gets for each route.
var url = "whateverdoma.in/students/"
$http.get(url)
.success()
.error()
var url = "whateverdoma.in/students/class/" + classId;
$http.get(url)
.success()
.error()
var url = "whateverdoma.in/students/filter/" + filterId;
$http.get(url)
.success()
.error()
What you want to do is write costum angular resource method, to call your API.
Use angular $resource and not $http - > it is the more common usage (and more REST oriented: $resource wraps $http for use in RESTful web API scenarios).
Read about it
Find how to add a resource to the $resource service.
Here is an example:
.factory('Store', function ($resource, hostUrl) {
var url = hostUrl + '/api/v3/store/';
return $resource("", { storeId: '#storeId' }, {
getSpecific: {
method: 'GET',
url: hostUrl + '/api/v3/store-specific/:storeId'
}
});
})