Web API allows me to capture the body of a POST request in a JObject:
$.post('/api/Query/DoSomething', { Foo: "one", Bar: 4 });
public string Post(JObject data)
{
// data is populated
}
However the same technique does not work with a get request and URI parameters.
$.get('/api/Controller', { Foo : "one", Bar : 4 });
public string Get([FromUri]JObject data)
{
// data is empty
}
Any workaround here?
It doesn't work because a GET request does not have a body, and hence no content type. Therefore, Web API does not know that you have JSON in your URL. You have a few choices:
Pass your data as query string parameters, as is traditionally done in GET requests, and change your method to accept those parameters individually, or in a regular class (POCO).
Change your GET method to accept a string instead of a JObject, then use JSON.Net to deserialize it manually, e.g. JObject obj = JObject.Parse(data);
If you're feeling ambitious, you might be able to implement a custom binder to do this.
My recommendation is option 1. Traditionally, a GET method is just intended to look something up, so you really should only be passing IDs and simple query options anyway. It is unusual to be passing JSON data in a URL. Also the length of URLs can be limited by some browsers. If you find you are needing to pass JSON data, use POST (or PUT) instead.
You can create an object and bind to it using the FromUri.
Check out this solution which I am using https://stackoverflow.com/a/49632564/2463156.
Related
I have a very simple spring boot web application which consumes requests with json body.
For each json which the application will receive (from any client) I would like to manipulate it as a first step.
For example if the client sends the following body:
{
"hello": "world!!!"
}
I would like to replace each ! with a ?. In this case the result is:
{
"hello": "world???"
}
This json transformation should be valid for each controller and for any json entering the system.
Is this kind of operation possible?
Thanks.
You may use string.replace to do the same.
Or also you can add custom annotation to manipulate the values of any keys.
You can use any replacement methods or regex in your classes.
#GetMapping
public String replace(RequestItem item){
// item = item.regex/replacement method
// call your service or whatever
return item;
}
When you got the data, you can do whatever you want to do.
Our front end application always expects a certain json format for the result, something like:
{
success: true,
errors: [],
responseObject: ...
}
Say I want to return a list of users, that list would be a in the responseObject. Now I would like to be able to just return this list from the restcontroller and have something like a controller advice wrap the list in this response object. However as I understand I cannot use the controller advice in this case. Is there a way to do this?
You can use for this purpose Filter. You can get your list of users from the response object, wrap in your desired model structure and change in response.
Example:
https://medium.com/#sportans300/fiddling-with-httpresponses-in-java-2a269cd5a474
#JsonView
how can use like parameter from request:
#JsonView(header="range")
when response value,read request header range to exclude/include some field
JsonView provides "static" view mapping. so for your dynamic behaviour you can do like this:
// actual request handling is happened here
private ResponseEntity<SomeObject> processRequest();
// request handling with view limit in result
#JsonView(YourDefinedView.class)
#RequestMapping(value = "/request", headers={"range=include"})
public ResponseEntity<SomeObject> processWithView() {
return processRequest();
}
// request handling without view (no headers specified)
#RequestMapping(value = "/request")
public ResponseEntity<SomeObject> processWithoutView() {
return processRequest();
}
this will map your client to same request url, but depending on header it will provide view or not. Than you can create a set of methods, that will be using different #JsonView depending on headers information.
But with this you will limit only the data transfered to client, and the whole data load will happen on server. For example with database and JPA, if you would like not to fetch from database all that data you will end with javax.persistence.NamedEntityGraphs, which will change the general logic of your application - and will at the end of the day produce 2 different methods.
And if you would like to expose custom header with list of fields, to be serialized - custom DTO object, or Map<String, Object> (ugly-ugly-ugly) or custom HandlerMethodReturnValueHandler comes to your help.
I'am starting with Asp.Net Web API and here's my problem :
I implement a custom authorization filter to inspect my message header looking for an API Key. Based on this API Key, I retrieve my user and then I would like to see if he can have access to some resources. The resources ID I want to check is on the parameters of the HTTP request. But when I'am on the AuthorizationFilter method, the actions parameters list is empty.
How can I do that ?
If I used an ActionFilter in replacement of an authorization filter, how can I be sure that this will be the first filter executed ? And globally, how can I specify the executing order of filters ?
Last question, is it possible to add some data "on the pipe" that I could retrieve on any filter ? Something like a session store but limited to the request ?
Thanks for any response
The authorization attributes run before parameter binding has run therefore you cannot (as you have seen) use the ActionArguments collection. Instead you will need to use the request uri for query parameters and route data for uri parameters as demonstrated below.
//request at http://localhost/api/foo/id?MyValue=1
public class MyAuthorizationAttribute : AuthorizeAttribute
{
protected override bool IsAuthorized(HttpActionContext actionContext)
{
//will not work as parameter binding has not yet run
object value;
actionContext.ActionArguments.TryGetValue("id", out value);
//Will get you the resource id assuming a default route like /api/foo/{id}
var routeData = actionContext.Request.GetRouteData();
var myId = routeData.Values["id"] as string;
//uri is still accessible so use this to get query params
var queryString = HttpUtility.ParseQueryString(actionContext.Request.RequestUri.Query);
var myQueryParam = queryString["MyValue"];
//and so on
}
}
About the execution order:
There are 3 different ways of specifying the execution order of filters using the FilterScope Enumeration... scope being Global, Controller and Action. The AuthoriseAttribute is "Global" and therefore it
Specifies an action before Controller.
If you needed to specify the execution order within these 3 scopes then you should read this blog article here where you will need to implement a FilterProvider
To add some data to the pipe:
Use the properties collection on the request this collection is available for the duration of the request.
protected override bool IsAuthorized(HttpActionContext actionContext)
{
actionContext.Request.Properties.Add("__MYKEY__","MyValue");
//access this later in the controller or other action filters using
var value = actionContext.Request.Properties["__MYKEY__"];
}
Another alternative to get to the parameters is to Execute the binding for the parameters.
try
{
var binding = actionContext.ActionDescriptor.ActionBinding;
var parameters = binding.ParameterBindings.OfType<ModelBinderParameterBinding>();
var newBinding = new HttpActionBinding(actionContext.ActionDescriptor, parameters.ToArray());
newBinding.ExecuteBindingAsync(actionContext, new CancellationToken());
var id = actionContext.ActionArguments["id"] as string;
}
catch
{
base.HandleUnauthorizedRequest(actionContext);
}
Note: You need to make sure you only filter on the parameters that will come from the Request URI, as I have noticed that executing the binding for any parameters that are expected to come from the Request body will no longer be passed on to the actual action. i.e. those parameters will be null.
This is just to note that you can do this, I'd recommend using GetRouteData()/RouteData as it is not likely to disrupt the further flow of ASP.NET MVC modelbinding.
var routeData = actionContext.ControllerContext.RouteData;
var id = routeData.Values["id"] as string;
I'm trying to build a restful JSON api for my Symfony2 Application.
I'm using the http://jmsyst.com/libs/serializer JMS\Serializer Bundle to serialize my Entities to JSON.
I have this example Controller-Action:
public function getFarmerByNameAction(Request $request) {
$this->setLocale($request);
$name = $request->get("name");
$farmer = $this->getDoctrine()->getRepository("FarmerguideBackendBundle:Farmer")->findByName($name);
// Return json response
return new Response($this->jsonify($farmer));
}
Since I'm using this serializer very often (I know I should do something like a singleton or whatever, but currently I don't have the time for that, I was just playing with the framework) I've put the code inside a function which does the serializing.
private function jsonify($object) {
// Serialize to json
$serializer = new Serializer(array(new GetSetMethodNormalizer()), array('json' => new
JsonEncoder()));
$json = $serializer->serialize($object, 'json');
return $json;
}
My problem is the following:
This code is inside a BackendController, which does NOT contain any gui-specific information. So just a RESTful API.
In another Controller, let's say WebappController I have the code to access these backendfunctions and do some stuff with twig-files and render()-methods.
I want to access all these information via mobile over ajax (therefore I need this json return value)
What's the best-practice here? Is it better to say: Well if it's a ajax-call (check with if($request->isXmlHttpRequest())) , do jsonify right before returning the repsonse and if it's not return the entities (I need entities for twig-templates..) Or is there another approach?
Or is it even better to work with $request->getFormatType() and making the ajax call with contentType="application/json; charset=utf-8"
Here is how KnpBundles handles it https://github.com/KnpLabs/KnpBundles/blob/master/src/Knp/Bundle/KnpBundlesBundle/Controller/DeveloperController.php#L35
I guess you need to clearify what your intentions are. Because right now it seems as if your WebappController would just be a client to your Backendcontroller. Something like:
$result = file_get_contents('/path/to/backend/method/1/3');
You then simply go ahead and decode the json.
That is some additional overhead of course. If you want to get entities, I would suggest to create a Service for all your backend methods and return the entities there. You then simply call those methods from your BackendController and your WebappController. You then would only jsonify the entities in your BackendController and render the appropriate templates in your WebappController.