Add an extra get method in asp.net web api - javascript

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'
}
});
})

Related

MVC controller is not being called, but template controller works?

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!

How to dynamically populate javascript file directly from spring boot java controller in json format?

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>

How can I use c# asp.net to get data from url?

I'm trying to get some info I sent by form to angularJS in my c# asp.net backend, and I'm having trouble doing it.
Visual Studio won't let me compile because it says:
Error CS0120: an object reference is required for the nonstatic field, method, or property 'member'
That's is my controller
public class SearchController : ApiController
{
public string Get()
{
string test = HttpContext.Request.QueryString["txt_search"];
return test;
}
}
Here's what I got in my angularjs:
$scope.sendForm = function () {
console.log($scope.model.search);
$http({
method: 'GET',
url: '/api/search?txt_search=' + $scope.model.search
})
.then(function () {
console.log('sucesso a executar o post');
}, function () {
console.log('Erro ao executar o post');
});
};
As suggested in the comments, you should just be able to change your method definition and skip this altogether:
public string Get(string txt_search)
{
return txt_search;
}
Alternatively, to reference the current request, I believe you need to use the following (note the addition of .Current):
string test = HttpContext.Current.Request.QueryString["txt_search"];
The reason is that HttpContext defines Request as an instance property. The only public static property is Current, which returns an instance of HttpContext through which you can reach Request.
Welcome to Stack Overflow,
Your Angular code is correct
You need to pass a parameter on server side to collect txt_search value
Here it is:
[HttpGet]
[Route("/api/search")]
public string mySearchMethod(string txt_search)
{
//something here with txt_search
return "OK";
}
Both of the above solution will work, but for another approach as you are using asp.net web api and router you can make it as below as well
In your Angular code, simple pass the search as below
```
$scope.sendForm = function () {
console.log($scope.model.search);
$http({
method: 'GET',
url: '/api/search/'+ $scope.model.search
})
.then(function () {
console.log('sucesso a executar o post');
}, function () {
console.log('Erro ao executar o post');
});
};
```
Notice
url: '/api/search/'+ $scope.model.search
and change the Action method as below
```
[HttpGet]
[Route("/api/search/{txt_search}")]
public string mySearchMethod(string txt_search)
{
//something here with txt_search
return "OK";
}
```
by doing this you don't have to worry about the name of the parameter txt_search
whatever you mention in route [Route("/api/search/{txt_search}")], you will get the value in same parameter.

multiple routes in one controller causes 400:bad request

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

Posting to ASP.NET WebApi server from AngularJS client

I'm trying to post strings from an AngularJS application (using $http) to a server built on ASP.NET WebApi, but I get 404 as soon as I add a parameter.
The client code is this
$scope.add = function () {
// ...cut...
$http({ method: "POST", url: url, data: { fileString: "test string" }}).then(
function successCallback(response) {
$log.info(response.data);
}
);
}
The server code is
[HttpPost]
public IHttpActionResult UploadExcel(string fileString) {
// cut
}
I get a 404, but if I remove the parameter on server side it works, so i can use a server side code like this
[HttpPost]
public IHttpActionResult UploadExcel() {
// cut
}
What is wrong? Should I pass the data in a different way? I tried different combination but I can't get it work.
What you want to do is send a string, not a JSON object as you are doing right now with { fileString: "test string" }. When I want to send a string, what I normally do is that I send data from Angular like this:
$http.post("/Search/QuickSearch?searchQuery="+ searchString);
And my controller I make ready to receive a string like this:
[HttpPost]
public IHttpActionResult QuickSearch(string searchQuery)
{
// cut
}
If I want to send a JSON object, I tell my controller what it should expect, like this:
[HttpPost]
public IHttpActionResult SaveActivity(ActivityEditForm form);
{
// cut
}
public class ActivityEditForm
{
public int? Id { get; set; }
[Required]
public string Title { get; set; }
public string Description { get; set; }
}
And then send my JSON from Angular like this:
$http.post("/Activity/SaveActivity", { form: activity });
I suggest you should capture the request send by Angular. By default, Angular send parameters in a json string in request body.
I'm not sure wether Asp.net can parse them from json string in body.
So, you can try to add the below codes (also need jQuery)
angular.module('yourApp').config(function ($httpProvider) {
$httpProvider.defaults.transformRequest = function(data){
if (data === undefined) {
return data;
}
return $.param(data);
}
});
The first error is in the controller, [FromBody] should be used with the input parameter.
public IHttpActionResult UploadExcel([FromBody]string fileString)
Then the data variable on the client should be a single string, so
$http({ method: "POST", url: url, data: "test string" }).then(
Anyway I found some issue with this solution later, it seems the simplest but I suggest to avoid it.
Best solution
Thank to #Squazz answer and this SO answer I strongly suggest a change in the webapi controller, client was correct. Just introduce a class to handle a single string and adapt the input parameter
// new class with a single string
public class InputData {
public string fileString { get; set; }
}
// new controller
[HttpPost]
public IHttpActionResult UploadExcel([FromBody] InputData myInput) {
string fileString = myInput.fileString;
// cut
}
This way JSON code from the client is automatically parsed and it's easy to change the data input.
Extra tip
$scope.add angular function was correct as in the question, but here is a more complete example
$scope.testDelete = function () {
var url = "http://localhost/yourAppLink/yourControllerName/UploadExcel";
var data = ({ fileString: "yourStringHere" });
$http({ method: "POST", url: url, data: data }).then(
function successCallback(response) {
console.log("done, here is the answer: ", response.data);
}, function errorCallback(response) {
console.log("an error occurred");
}
);
}

Categories