Url changes if i don't pass api parameters - spring

I have a common api for search opertaions as well as get operations if the i don't pass the search parameters the URL changes which i don't want.
for eg:
if search parameter is passed:
localhost:8080/api/search?name=harmeet
if no search parameter is passed:
localhost:8080/api/search
what i want is the url should not change whether i pass the parameter or not
for example if i don't pass parameter the url should be:
localhost:8080/api/search?name=''
The code has been written using spring boot

What URL gets called is depended on the client-side and not on the server-side. If you always want a parameter value to be something or "" when it is not passed by the client-side, you can define your controller method as:
#GetMapping("/api/search")
public Object getName(#Requestparam(value="name",required=false,defaultValue="")String name){
// your logic here
// value of 'name' variable will "" if nothing is passed
}
Just set the required field as false and use the defaultValue field to set whatever default value you want to set if the parameter is not provided in the URL call.

As per i understand you want to create API which handle two API calls.
Such as,
http://localhost:8080/search
http://localhost:8080/search?name=hermeet
I supposed you already create API in backend... Right!!!
So, There is not any issue on your logic but parameter which you defined in your method is by default required.
Which don't allow you to pass null or empty value.
You just need to allow your parameter for work with both the cases.
As following ways,
Optional<'Parameter_Type'> parameter_name
#RequestParam(value = "parameter_name", required = false) String variable
Example :
#GetMapping("/search")
public List<Entity> getSearchResult(#RequestParam(value = "parameter_name", required = false) String variable) {
if(variable!=null) {
// Get Data based on condition.
} else {
// Get All Data
}
}
#GetMapping("/search")
public List<Entity> getSearchResult(#RequestParam("parameter_name") Optinal<String> variable) {
if(variable.isPresent()) {
// Get Data based on condition.
} else {
// Get All Data
}
}

Related

Passing exact parameters to Web API in ASP.NET Core

I have written a Web API in ASP.NET Core, for which I need to pass 2 parameters; of them one is a string with grade, the other is of type list of studentInfo as shown here:
[HttpPost]
[Route("UpdateActiveStudents")]
public Response UpdateActiveStudents(string grade, [FromBody] List<StudentsInfo> lststudents)
{
try
{
// My Logic
}
catch(Exception ex)
{
resp.flag = false;
resp.message = ex.Message;
}
return resp;
}
To test this API, I used ARC (Advanced Rest Client). I passed the data as like this in a POST request:
{
"grade": "B",
"lststudents": [
{ "StudentName": "abcdef", "RollNo": "user1"},
{ "StudentName": "abcdef", "RollNo": "user1"}
]
}
It throws a HTTP 400 status error with the following message :
Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[SchoolHub.Model.StudentList]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly. To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object. Path 'lststudents', line 2, position 13.
I'm unaware of this exception.
You have this issue because you are not sending the data in the format at which ASP.Net Web API expects. ASP.net Web API needs some special format when dealing with a value like string and value type (int, bool, etc) parameter are marked with FromBody attribute.
Just remove FromBody it will work. For better understanding go with this link.
Why do we have to specify FromBody and FromUri?
In Web API, general parameter binding rules for POST method are as follows -
Query string -> Primitive type
Request body -> Complex type
Now if you want to use POST method with Mixed parameters i.e in your case you are passing primitive (string) and complex (List), Web API will get the grade parameter from query string and student parameter from the request body.
Possible solutions to try -
In the ARC request it seems you are passing grade in request body instead of as a query string parameter. Try passing grade as a query string parameter.
Also add a class viz. StudentInfoRequest to wrap List<StudentsInfo> lststudents and then use StudentInfoRequest object to pass as parameter to UpdateActiveStudents method.
You dont need to mention [FromBody] in UpdateActiveStudents method as by default complex parameters are read from the request body by Web API.
Hope this helps!
You must add [ApiController] in the controller level. Code is as follows
[ApiController]
[Route("[controller]")]
public class studentController : ControllerBase
{
[HttpPost]
[Route("UpdateActiveStudents")]
public Response UpdateActiveStudents(string grade, List<StudentsInfo>
lststudents)
{
//code
}
}

Passing several query parameters to a GET endpoint in .netcore web api

I have an application where there will be several parameters passed to my endpoint for searching, these parameters are not defined because they are dynamically generated so i cannot map it to a specific model. What would be the best way to map any query parameters into my GET endpoint?
[HttpGet]
public CustomResponse GetResults({something here that will map the parameters?})
{
//perhaps a dictionary? a collection of some sort?
}
Then I need to get all those keys and values and search the database for anything containing that and as i said it could be anything.
So I could pass something like?
/api/Merchandise/GetResults?sku=30021&cupsize=medium&color=red&location=south& {and all the dynamic fields which could be anything}
HttpRequest object has Query property that is an IQueryCollection and holds all passed query parameters.
In other words, in your action method you may do:
[HttpGet]
public CustomResponse GetResults()
{
var queryParams = HttpContext.Request.Query;
// directly get by name
var value1 = queryParams["parameter_name"];
// or queryParams.TryGetValue()
foreach (var parameter in queryParams)
{
string name = parameter.Key;
object value = parameter.Value;
}
}
You could map it to JObject, which is like a Dictionary.
Don't forget:
using Newtonsoft.Json;

ASP.NET WebAPI routing confusing me with null paramters

I can't figure out why I need to create an empty method signature to allow a Get rest call with 3 null parameters to work. I have the following Code:
public class SessionPresenterController : ApiController
{
public HttpResponseMessage Get()
{
return Get(null, null, null);
}
public HttpResponseMessage Get(int? codeCampYearId, int? sessionId, int? attendeesId)
{
and in my WebApiConfig I have
config.Routes.MapHttpRoute
("API Default Rest", "rest/{controller}/{id}",
new { id = RouteParameter.Optional });
config.Routes.MapHttpRoute
("API Default RPC", "rpc/{controller}/{action}/{id}",
new { id = RouteParameter.Optional });
If I call /rest/SessionPresenter without any parameters and I Don't have the Get() defined, the Get with the three null parameters is not found.
Why not?
Try specifying defaults for your parameters:
public HttpResponseMessage Get(
int? codeCampYearId = null,
int? sessionId = null,
int? attendeesId = null)
{
//...
}
Jacobs' snippet will solve your issue. Let's answert the question why not? why get is not found..
There is a complete documentation of the Routing and Action Selection. Let's use some extratc and reveal what happens:
Action Selection
Create a list of all actions on the controller that match the HTTP request method.
If the route dictionary has an "action" entry, remove actions whose name does not match this value.
Try to match action parameters to the URI, as follows:
For each action, get a list of the parameters that are a simple type, where the binding gets the parameter from the URI. Exclude optional parameters.
From this list, try to find a match for each parameter name, either in the route dictionary or in the URI query string. Matches are case insensitive and do not depend on the parameter order.
Select an action where every parameter in the list has a match in the URI.
If more that one action meets these criteria, pick the one with the most parameter matches.
Other words, if there will be only Get with three parameters (omited Get()), to decided which action should be selected:
Selected was Get(int? codeCampYearId, int? sessionId, int? attendeesId)
still the Get(int? codeCampYearId, int? sessionId, int? attendeesId) is selected
URL is /rest/SessionPresenter
no optional parameters excluded. All have to be found
URL does not have a match for all three parameters
So to solve it, we have to either pass all params (empty, null):
/rest/SessionPresenter?codeCampYearId&attendeesId&sessionid
Or change the signature to have parameters optinal (Jacobs' answer), and skipped during the action selection

Filter select drop down using Grails security ACL

I am implementing ACL security using the spring-security-acl plugin. I have the following domain classes:
package test
class Subitem {
String name
static belongsTo = [employer: Employer]
static constraints = {
name blank: false
}
}
package test
class Employer {
String name
static hasMany = [users: User, items: Subitem]
static belongsTo = User
static constraints = {
name blank: false, unique: true
}
String toString() {
name
}
}
In the create.gsp file which is used to create a Subitem, there is the following statement:
<g:select id="employer" name="employer.id" from="${test.Employer.list()}" optionKey="id" required="" value="${subitemInstance?.employer?.id}" class="many-to-one"/>
From the EmployerController:
def list = {
params.max = Math.min(params.max ? params.int('max') : 10, 100)
[employerInstanceList: employerService.list(params),
employerInstanceTotal: employerService.count()]
}
Following the tutorial given here, I have moved some of the functionality with dealing with Employer to a service called EmployerService:
#PreAuthorize("hasRole('ROLE_USER')")
#PostFilter("hasPermission(filterObject, read)")
List<Employer> list(Map params) {
Employer.list params
}
int count() {
Employer.count()
}
Access to information in any given Employer class instance is restricted using ACL. At present, I can see ALL instances of Employer in the database in the drop down, and I assume that is because I am using the controller list(), not the service list() - however, I only want to see the filtered list of Employer domain classes. However, if I replace the g:select with:
<g:select id="employer" name="employer.id" from="${test.EmployerService.list()}" optionKey="id" required="" value="${subitemInstance?.employer?.id}" class="many-to-one"/>
then I get an internal server error because I haven't passed a Map parameter to the service list() function (and I don't know how to do this within the tag):
URI /security/subitem/create
Class groovy.lang.MissingMethodException
Message No signature of method: static test.EmployerService.list() is applicable for argument types: () values: [] Possible solutions: list(java.util.Map), is(java.lang.Object), wait(), find(), wait(long), get(long)
I only want to see the information that comes from the EmployerService list() function - how do I do this please? How do I reference the correct function from within the gap?
Edit 16 Mar 0835: Thanks #OverZealous, that's really helpful, I hadn't realised that. However, I've tried that and still get the same problem. I've put a println() statement in both the Employer and EmployerService list() functions, and can see that neither actually seems to get called when the g:select tag is parsed (even if I leave the g:select to refer to Employer). Is there another version of the list() function that is being called perhaps? Or how else to get the g:select to take account of the ACL?
Just change your method signature in the Service to look like this:
List<Employer> list(Map params = [:]) {
Employer.list params
}
The change is adding this: = [:]. This provides a default value for params, in this case, an empty map.
(This is a Groovy feature, BTW. You can use it on any method or closure where the arguments are optional, and you want to provide a default.)
OK, I worked it out, and here is the solution to anyone else who comes up against the same problem.
The create Subitem page is rendered by means of the Subitem's create.gsp file and the SubitemController. The trick is to amend the SubitemController create() closure:
class SubitemController {
def employerService
def create() {
// this line was the default supplied method:
// [subitemInstance: new Subitem(params)]
// so replace with the following:
params.max = Math.min(params.max ? params.int('max') : 10, 100)
[subitemInstance: new Subitem(params), employerInstanceList: employerService.list(params),
employerInstanceTotal: employerService.count()]
}
}
So now when the SubitemController is asked by the g:select within the Subitem view for the list of Employers, it calls the EmployerService, which supplies the correct answer. We have simply added 2 further variables that are returned to the view, and which can be referenced anywhere within the view (such as by the g:select tag).
The lesson for me is that the View interacts with the Controller, which can refer to a Service: the Service doesn't play nicely with a View, it seems.

codeigniter alternative to Get variables

I was looking for a way to pass "GET" variables in codeigniter and ended up coming across this :
link text
I am wondering how to implement it.
For example :
www.website.com/query would give me every entry in the DB .
Typically I would have
www.website.com/query/?id=5 to get the equivalent entry.
when i try to do that the CI way :
www.website.com/query/id/5
I get a 404 error since it is looking for a class named id and it can't find it.
is there any way to get a step by step way to do this?
thank you.
Two good ways to achieve this using methods intended by the Codeigniter developers.
OPTION ONE:
If you always expect an "id" parameter to be present you could take advantage of a feature where you pass the value in the URI immediately after the method (function) you want to call.
Example passing /[controller]/[method]/[value]:
http://www.website.com/query/index/5
You would then access the value of "id" as an expected parameter of the function.
Class Query extends Controller {
...
// From your URL I assume you have an index method in the Query controller.
function index($id = NULL)
{
// Show current ID value.
echo "ID is $id";
...
}
...
}
OPTION TWO:
If you would like to allow many parameters to be passed in addition to ID, you could add all parameters as key=>value pairs to the URI segments in any order.
Example passing /[controller]/[method]/[key1]/[val1]/[key2]/[val2]/[key3]/[val3]:
http://www.website.com/query/index/id/5/sort/date/highlight/term
You would then parse all the URI segments from the 3rd segment ("id") forward into an array of key=>value pairs with the uri_to_assoc($segment) function from the URI Class.
Class Query extends Controller {
...
// From your code I assume you are calling an index method in the Query controller.
function index()
{
// Get parameters from URI.
// URI Class is initialized by the system automatically.
$data->params = $this->uri->uri_to_assoc(3);
...
}
...
}
This would give you easy access to all the parameters and they could be in any order in the URI, just like a traditional query string.
$data->params would now contain an array of your URI segments:
Array
(
[id] => 5
[sort] => date
[highlight] => term
)
HYBRID OF ONE AND TWO:
You could also do a hybrid of these where ID is passed as an expected parameter and the other options are passed as key=>value pairs. This is a good option when ID is required and the other parameters are all optional.
Example passing /[controller]/[method]/[id]/[key1]/[val1]/[key2]/[val2]:
http://www.website.com/query/index/5/sort/date/highlight/term
You would then parse all the URI segments from the 4th segment ("sort") forward into an array of key=>value pairs with the uri_to_assoc($segment) function from the URI Class.
Class Query extends Controller {
...
// From your code I assume you are calling an index method in the Query controller.
function index($id = NULL)
{
// Show current ID value.
echo "ID is $id";
// Get parameters from URI.
// URI Class is initialized by the system automatically.
$data->params = $this->uri->uri_to_assoc(4);
...
}
...
}
$id would contain your ID value and $data->params would contain an array of your URI segments:
You can still use GET parameters, they're just mapped to controller member function parameters:
test.com/query/id/4
Would map to the controller:
$query->id($id);
This assumes you have added a query controller and member function properly in the controllers folder in your CI application.
You can also pass your parameter values as POST parameters using a form and the CI input class.
Use $this->uri->uri_to_assoc(2) 2 is an offset, as you starting your associative array of segments in the 2nd segment. You will also need a route to make /query map to a controller and method (unless you do this in the index() method).
So, this URL:
/query/id/foo/key/bar
could be read using:
$get = $this->uri->uri_to_assoc(2);
echo $get['id']; // 'foo'
echo $get['key']; // 'bar'
It's not great, but it works.

Resources