Restlet JAX-RS deserializes into ArrayList<LinkedHashMap> - jax-rs

I started using the Restlet JAX-RS extension, but I'm finding that lists get deserialized into an ArrayList of LinkedHashMap instead of the class.
#Path("MyService")
public class MyService {
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
#Path("myTestMethod")
public String testIt(final List<MyTestClass> myList) {
return "hello";
}
}
public class MyTestClass {
#JsonProperty("name")
private String name;
}
Any ideas on how to have Jackson deserialize into List<MyTestClass>? It works fine when I don't use the JAX-RS extension, but I need this to port over some existing web services.
Thanks for any help.
==Update==
Sending data like this:
import requests
import json
url = "http://localhost:8182/MyService/myTestMethod"
headers = {
"Content-Type": "application/json",
"Accept": "application/json"
}
request_object = [{"name": "joe"}, {"name": "bill"}]
params = json.dumps(request_object)
response = requests.post(url, data=params, headers=headers)

Related

javax.ws.rs.ProcessingException: RESTEASY003215: could not find writer for content-type application/json type: com.test.Request

I am using JAX-RS to develop an endpoint, I have the following class:
public class Request implements Serializable {
private String key;
private Content content;
private List<String> user;
}
And this is the endpoint that is configured:
#Path("/api/v1")
public class MyAPI {
#POST
#Path("/alerts")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
String postAlerts(Request request) throws WebApplicationException{
//Implementation
}
}
When I invoke the end point from client, I get following exception:
javax.ws.rs.ProcessingException: RESTEASY003215: could not find writer for content-type application/json type: com.test.Request
I have included all the dependencies mentioned in RESTEasy: Could not find writer for content-type application/json type. Specifically [resteasy-jaxrs, jaxrs-api, resteasy-jaxb-provider, resteasy-jackson2-provider] and I am able to verfiy that they are present in the classpath. Do I need provide some annotations on the 'Request' class which would create a writer for that for content-type application/json?

quarkus(vertx) how to get request body in a blocking way

What I've tried:
inject CurrentVertxRequest context and then get body from there
#Path("/map_event")
public class MapEventApi {
#Inject
CurrentVertxRequest reqContext;
#POST
#Consumes({ "application/json" })
#Produces({ "application/json" })
Response create(#Valid MapEvent mapEvent, #Context SecurityContext securityContext) throws Exception {
String body = reqContext.getCurrent().getBodyAsString();
...
}
}
but this will give a warning:
2022-01-25 18:22:08,854 WARN [null:-1] (executor-thread-0) BodyHandler in not enabled on this route: RoutingContext.getBodyAsString(...) in always be NULL
another try:
inject standard JaxRS HttpServletRequest context
#Context HttpServletRequest
will get this error:
org.jboss.resteasy.spi.LoggableFailure: RESTEASY003880: Unable to find contextual data of type: javax.servlet.http.HttpServletRequest
at org.jboss.resteasy.core.ContextParameterInjector$GenericDelegatingProxy.invoke(ContextParameterInjector.java:155)
at com.sun.proxy.$Proxy97.getInputStream(Unknown Source)
I guess it's because quarkus use vertx under the hood so injecting regular jaxrs context won't work since it's not the same thread.
According to #geoand proposiiton following solution worked for me:
#POST
#Consumes(MediaType.TEXT_PLAIN)
#Produces(MediaType.APPLICATION_JSON)
Response create(String body) throws Exception {
...
}
consuming the request as String was the trick → #Consumes(MediaType.TEXT_PLAIN)

"The JSON value could not be converted to Enum in Refit

I have one .Net Core Razor pages app which is trying to call a .Net Core API with a class library created using Refit.
I have created one Refit API interface which uses a model with enum as one of the property type.
Here is the interface snippet on the API side: IPaymentAPI interface
[Post("/recharge")]
Task<string> Recharge([Body] RechargeRequest request);
Here is the request model: The model contains one simple enum ELicenseType.
public class RechargeRequest
{
public ELicenseType LicenseType{ get; set; }
}
The ELicenseType:
public enum ELicenseType
{
NotSpecified = 0,
Standard = 1,
Commercial = 2
}
The API implementation in controller:
[HttpPost("recharge")]
public async Task<IActionResult> Recharge(
[FromBody] RechargeRequest request)
{
Recharge result = await _mediator.Send(_mapper.Map<RechargeCommand>(request));
return Ok();
}
When calling this Recharge method the Refit is throwing the ValidationApiException:
ValidationApiException: Response status code does not indicate success: 400 (Bad Request).
And the content is:
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"traceId": "00-f9178d81421bca438241dd2def43d065-edbd7f210919b24e-00",
"errors": {
"$.licenseType": [
"The JSON value could not be converted to ELicenseType. Path: $.licenseType | LineNumber: 0 | BytePositionInLine: 98."
]
}
}
It seems that the Refit library does not support Enums in the request or my JSON serializer is misconfigured.
Do you need to set this config in your startup.cs?
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers().AddJsonOptions(opt =>
{
opt.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
});
}
When creating a Refit implementation for your API's interface, you can provide an instance of RefitSettings.
var refitSettings = new RefitSettings
{
ContentSerializer = new SystemTextJsonContentSerializer(new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
NumberHandling = JsonNumberHandling.AllowReadingFromString,
Converters =
{
new JsonStringEnumConverter(JsonNamingPolicy.CamelCase)
}
})
};
var api = RestService.For<IMyApi>(client, refitSettings);
I think that will solve your problem.

Post value always null between Python request and ASP.Net Core API

I have some simple Python
import requests
response = requests.post(url+'users/', data = 'hallo')
And a simple ASP.Net Core API
public void Post(string value)
{
var newUser = JsonConvert.DeserializeObject<User>(value);
// [...]
}
When I breakpoint the C# code the incoming parameter 'value' is always null. I have tried sending JSON instead of a simple string, setting the headers to 'text/plain', 'application/json', or 'application/x-www-form-urlencoded' in Python but the result is the same. I have tried decorating the parameter [FromBody] in the ASP.Net Core API, but then I get a 400 error ("The input was invalid").
What am I missing?
(EDIT. Here is a hacky fix, definitely not an answer, but it may help people see what's wrong.
public void Post(string value)
{
Request.EnableRewind();
var body = "";
using (var reader = new StreamReader(Request.Body))
{
Request.Body.Seek(0, SeekOrigin.Begin);
body = reader.ReadToEnd();
}
value = body;
var newUser = JsonConvert.DeserializeObject<User>(value);
// [...]
}
Then value is set correctly. But if the value is sitting there in the body of the post, it is frustrating that using [FromBody] results in a 400 error.)
Hi dumbledad I had this same issue, I guess you would have solved it but I wanted to post the solution here.
I had to send the data to params instead of data.
So on the Python side:
payload = {"type": "2"}
myheaders={'Content-type':'application/json', 'Accept':'application/json'}
r = requests.post(url=myurl,headers=myheaders, params=payload)
On the c# side:
[Route("MyFunction")]
[HttpPost]
public ActionResult GetOpenFootball_(string type){}
This mapped the value "2" to the string parameter called "type".
EDIT
I managed to get objects through the [From Body] tag in this way:
I changed the header to:
_headers = {'Content-type': 'application/json', 'Accept': 'text/plain'}
I put my parameters into a json such as:
jsondata = {"username":"selzero"}
Then I plugged that into the json property in request like:
r = requests.post(url=_url, headers=_headers,json=jsondata)
This works, MVC controller [FromBody] likes it.
If you make your request like this:
response = requests.post(url, data={'value' : 'hallo'})
Then this will associate 'hallo' with the method-parameter value:
[HttpPost]
public void Post(string value)
{
Console.WriteLine(value);
}
If you need to send more data, you could also wrap your request in an object and fetch it by model-binding. See more here: Model Validation in ASP.NET Web API. It looks like that's what you're attempting to do anyways here:
var newUser = JsonConvert.DeserializeObject<User>(value);
You can achieve this automatically like this:
response = requests.post(url, json={ 'FirstName' : 'john', 'LastName' : 'doe' })
Notice the use of json=. And in your controller:
public class User {
public string FirstName { get; set; }
public string LastName { get; set; }
}
[HttpPost]
public void Post([FromBody] User user)
{
Console.WriteLine(user.FirstName);
Console.WriteLine(user.LastName);
}
// Outputs:
// john
// doe

Calling wcf rest service from jquery doesn't work

I have written a really simple wcf rest service which seems to work fine when I make requests to it through fiddler but I cannot get it to work when calling it from JQuery.
Service:
[ServiceContract]
public interface IService1
{
[OperationContract]
[WebInvoke(Method = "POST",
UriTemplate = "customers/{regionId}",
ResponseFormat = WebMessageFormat.Json
)]
Customer[] GetCustomers(String regionId);
}
[DataContract]
public class Customer
{
[DataMember]
public Guid Id { get; private set; }
[DataMember]
public String Name { get; private set; }
public Customer(Guid id, String name)
{
Id = id;
Name = name;
}
}
public class Service1 : IService1
{
public Customer[] GetCustomers(String regionId)
{
return new[]
{
new Customer(Guid.NewGuid(), "john"),
new Customer(Guid.NewGuid(), "pete"),
new Customer(Guid.NewGuid(), "ralph")
};
}
}
I can make requests to this service via fiddler and it returns the expected json. However, when I try and call it with JQuery ajax via the firebug console it always fails. Here is the call:
$.ajax({
type: "POST",
data: "{}",
url: "http://127.0.0.1:8081/json/customers/1",
contentType: "application/json; charset=utf-8",
dataType: "json",
success:function(res)
{
alert('success');
},
error: function(xhr)
{
alert('failed: '+xhr.responseText);
}
});
I always get the failed alert and the responseText is always blank. Any ideas would be greatly appreciated.
When you say "via Fiddler" do you mean "using Fiddler's request builder" or do you mean "with Fiddler running?"
Question: What URL is your website running on? You cannot generally make XHR requests to different servers (or ports, in FF) using XHR.