Jaxb in Restful webservices - rest

I have worked on Web services using Jaxb earlier. I geneated Java from xsd, and then I used to post the xml request to the specified URL using HTTP post. Recently I heard about this Restful web services, on reading I felt that what I had been doing earlier is the restful web service only. But, I am not sure about it if its the same thing.
Can anyone explain please.

It sounds like you have been creating the same types of RESTful services. You may be referring to is JAX-RS with is a standard that defines an easier way of creating RESTful services where JAXB is the standard binding layer for the application/xml media type. Below is an example service:
package org.example;
import java.util.List;
import javax.ejb.*;
import javax.persistence.*;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
#Stateless
#LocalBean
#Path("/customers")
public class CustomerService {
#PersistenceContext(unitName="CustomerService",
type=PersistenceContextType.TRANSACTION)
EntityManager entityManager;
#POST
#Consumes(MediaType.APPLICATION_XML)
public void create(Customer customer) {
entityManager.persist(customer);
}
#GET
#Produces(MediaType.APPLICATION_XML)
#Path("{id}")
public Customer read(#PathParam("id") long id) {
return entityManager.find(Customer.class, id);
}
#PUT
#Consumes(MediaType.APPLICATION_XML)
public void update(Customer customer) {
entityManager.merge(customer);
}
#DELETE
#Path("{id}")
public void delete(#PathParam("id") long id) {
Customer customer = read(id);
if(null != customer) {
entityManager.remove(customer);
}
}
}
For More Information
http://blog.bdoughan.com/2010/08/creating-restful-web-service-part-45.html

When it comes to say 'RESTful', it's just an convention of HTTP methods and url patterns.
CRUD METHOD URL RESPONSE DESCRIPTION
----------------------------------------------------------------
CREATE POST http://www.doma.in/people 202 Creates a new person with given entity body
READ GET http://www.doma.in/people 200
READ GET http://www.doma.in/people/1 200 404 Reads a single person
UPDATE PUT http://www.doma.in/people/2 204 Updates a single person with given entity body
DELETE DELETE http://www.doma.in/people/1 204 Deletes a person mapped to given id(1)
You can even implement those kind of contracts with Sevlets. Actually I had done with Sevlets before the era of JAX-RS.
And your life will be much more easier when you use JAX-RS.
Here comes a slightly modified version of Mr. Blaise Doughan's.
Nothing's wrong with Mr. Blaise Doughan's code.
I just want to add more for above url patterns.
One of great things that JAX-RS can offer is that you can serve XMLs and JSONs as clients want if you have those fine JAXB classes. See #Producess and #Consumess for those two formats in same method.
When client want to receive as XML with Accept: application/xml, they just get the XML.
When client want to receive as JSON with Accept: application/json, they just get the JSON.
#Path("/customers");
public class CustomersResource {
/**
* Reads all person units.
*/
#POST
#Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response read() {
final List<Customer> listed = customerBean.list();
final Customers wrapped = Customers.newInstance(listed);
return Response.ok(wrapped).build();
}
#POST
#Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response createCustomer(final Customer customer) {
entityManager.persist(customer);
return Response.created("/" + customer.getId()).build();
}
#GET
#Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
#Path("/{id: \\d+}")
public Response read(#PathParam("id") final long id) {
final Customer customer = entityManager.find(Customer.class, id);
if (customer == null) {
return Response.status(Status.NOT_FOUND).build();
}
return Response.ok(customer).build();
}
#PUT
#Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public void updateCustomer(final Customer customer) {
entityManager.merge(customer);
}
#DELETE
#Path("/{id: \\d+}")
public void deleteCustomer(#PathParam("id") final long id) {
final Customer customer = entityManager.find(Customer.class, id);
if (customer != null) {
entityManager.remove(customer);
}
return Response.status(Status.NO_CONTENT).build();
}
}
Say you want to serve some images?
#GET
#Path("/{id: \\d+}")
#Produces({"image/png", "image/jpeg"})
public Response readImage(
#HeaderParam("Accept") String accept,
#PathParam("id") final long id,
#QueryParam("width") #DefaultValue("160") final int width,
#QueryParam("height") #DefaultValue("160") final int height) {
// get the image
// resize the image
// make a BufferedImage for accept(MIME type)
// rewrite it to an byte[]
return Response.ok(bytes).build();
// you can event send as a streaming outout
return Response.ok(new StreamingOutput(){...}).build();
}

Related

How to call third party REST API from ASP.NET Core Web API?

I have been working with .NET Core for only a few weeks. Now I have a question about a Web API that I created for a project.
I need to call an external API, however all the documentation I see is for applications with MVC. This means they have a main page, which I
don't have and don't want, because I just want to call this API on an new endpoint and to be consume on client side.
If I test the external API in postman it returns something like this:
postman response
I tried to follow along with the official documentation of Microsoft Call a Web API From a .NET Client (C#) however I get to a point where I'm not sure how to proceed.
I've create a model with a few of the result properties that I want to capture from the call to this endpoint shown in the first image and a simple controller that I want to be the final endpoint that return the information from this external API.
These are the things that I have already:
Model class Product:
namespace MyProyect
{
public class Product
{
public string Id { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
public string Type { get; set; } = string.Empty;
}
}
Controller to consume the external API:
namespace MyProyect.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ThirdPartyAPIController : ControllerBase
{
static HttpClient client = new HttpClient();
[HttpGet]
static async Task<Product> GetProductsAsync(string path)
{
Product product = null;
HttpResponseMessage response = await client.GetAsync(path);
if (response.IsSuccessStatusCode)
{
product = await response.Content.ReadAsAsync<Product>();
}
return product!;
}
}
}
I don't really know how to proceed from here, this is all I can get from the documentation. I want this controller to be an endpoint that the user can consume. But this is not even appearing on the Swagger UI screen.
Is there an easy way to do this? Am I using an incorrect way?
First register a HttpClient for your Controller/Service
// In Startup.cs (5.0)
services.AddHttpClient<ThirdPartyAPIController>();
// ... or in Program.cs (6.0)
builder.Services.AddHttpClient();
Then inject the HttpClient and use it in the Controller method.
namespace MyProyect.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ThirdPartyAPIController : ControllerBase
{
// Let the `IHttpClientFactory` do the `HttpClient` handling
private HttpClient _client;
public ThirdPartyAPIController(HttpClient client)
{
_client = client;
}
[HttpGet]
// use `public` instead of `static`
// only this is then visible in Swagger UI
public async Task<Product> GetProductsAsync(string path)
{
Product product = null;
HttpResponseMessage response = await _client .GetAsync(path);
if (response.IsSuccessStatusCode)
{
product = await response.Content.ReadAsAsync<Product>();
}
return product!;
}
}
}
Like written in the code comment. You have to use public (not static) for the controller method to be registered and visible in Swagger UI.

How to get user info directly at JPA level in rest api

I am using REST api with JPA, and getting the user information in the header section .
For audit purpose need to save the user detail with each request.
How to directly get the user info at JPA level (#Prepersist and #PreUpdate hooks) from rest header.
I don't want to pass the details though service layer
Is there any generic way to do it ?
Note-I am not using spring.
Thanks in advance.
I had the similar problem with spring framework. Following idea may help you.
Create AppContext using ThreadLocal
public class AppContext {
private static final ThreadLocal<User> currentUser = new ThreadLocal<>();
public static void setCurrentUser(String tenant) {
currentUser.set(tenant);
}
public static String getCurrentUser() {
return currentUser.get();
}
public static void clear() {
currentUser.remove();
}
}
Use filter or similar to get user from http header and set to the AppContext
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
// Your code to extract user info from header
// Build user object and set to the AppContext
AppContext.setCurrentUser(user);
//doFilter
chain.doFilter(httpRequest, response);
}
Use AppContext on the repository. It should available on request scope.
#PrePersist
public void onPrePersist() {
if(AppContext.getCurrentUser() != null){
this.user = AppContext.getCurrentUser();
}
}
}

Combining verbs in Apache CXF JAX-RS

We would usually define POST and PUT verbs as different service APIs.
#POST
#Path("/getbook")
#Produces({"application/xml","application/json"})
#Consumes({"application/xml","application/json","application/x-www-form-urlencoded"})
public Response getBucket() {
... }
#PUT
#Path("/getbook/{name}")
#Produces({"application/xml","application/json"})
#Consumes({"application/xml","application/json","application/x-www-form-urlencoded"})
public Response getBucket(#PathParam("name") String name) {
... }
Would there be a way to combine these verbs into a single method - and then drive different logic based on the type of the verb ?
Hypothetically
#POST
#PUT
#Path("/getbook/{name}")
#Produces({"application/xml","application/json"})
#Consumes({"application/xml","application/json","application/x-www-form-urlencoded"})
public Response getBucket(#PathParam("name") String name) {
if(verb=POST){
... }
else{
}
}
You may try like this using MessageContext. You need the context injected into the service method like below for updateCustomer method and then you can check for the method type as you like (here I am checking for PUT):
#Path("/customer")
public class CustomerService {
#Context
private org.apache.cxf.jaxrs.ext.MessageContext mc;
#PUT
public Response updateCustomer(#Context MessageContext context, Customer c) {
HttpServletRequest request = context.getHttpServletRequest();
boolean isPut = "PUT".equals(request.getMethod());
}
}

Marshalling List<String> with JAX-RS

I'm used to working with jax-ws where a wsdl file is generated, and a client can then be generated based on this wsdl file and its xsd(s) using a maven plugin. Using this client is no hassle at at, and you don't have to really think about what happens in the background, like marshalling, http transfer and such.
I'm currently working on a jax-rs project using jaxb to unmarshal objects. One of the methods there returns a list of strings, but it seems that jaxb does not know how to marshal this, which is kinda surprising as it does know how to marshal a list of entities (ex, customers).
Also, I have written a client for the jax-rs service on my own, handling both http responses and unmarshalling of the payload using jaxb. Marshalling and unmarshalling with jaxb is a real hassle since it cannot automatically marshall or unmarshall list of entities that is added to its context, even less lists of strings.
I would like to know if there is some neat way to get all of this for free using restful webservices? This would have to be quite lightweight, and the clients must be easy to distribute.
Thanks!
Runar
The service method that is not working using jaxrs and jaxb:
#GET
#Path("/{customerId}")
#Produces(MediaType.APPLICATION_XML)
public List<String> isCustomerLocked(#PathParam("customerId") Long customerId) {
}
Client code that attempts to marshall/unmarshall text payload. Classes added to the jaxbcontext not shown:
javax.xml.bind.Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.marshal(obj, stringwriter)
javax.xml.bind.Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
unmarshaller.unmarshal(inputstream)
I'd use JAXB to wrap the data. For a simple List<String> this may look as overkill. But in most cases you want to un-/marshall Resource Representations, not simple objects.
Remember: REST ist not RPC!
If you really want tom un-/marshall List<String> write a JAX-RS Provider. But I'd prefer using JAXB.
S.java
#XmlRootElement
public class S {
private String s;
public S() {
}
public S(String s) {
this.s = s;
}
public String getS() {
return s;
}
public void setS(String s) {
this.s = s;
}
}
Ss.java
#XmlRootElement(name="ss-wrapper")
public class Ss {
private List<S> ss;
public List<S> getSs() {
return ss;
}
public void setSs(List<S> ss) {
this.ss = ss;
}
public Ss(List<S> ss) {
this.ss = ss;
}
public Ss() {
}
}
JAX-RS class
#Path("/strings")
#GET
#Produces(MediaType.APPLICATION_XML)
public Response getListOfStrings() {
S s1 = new S("foo");
S s2 = new S("bar");
List<S> strings = new ArrayList<S>();
strings.add(s1);
strings.add(s2);
Ss ss = new Ss(strings);
return Response.ok(ss).build();
}
XML
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ss-wrapper>
<ss>
<s>foo</s>
</ss>
<ss>
<s>bar</s>
</ss>
</ss-wrapper>

How to access multiple resources in a single request : Jersey Rest

I am trying to a find a good design for the following scenario.
I have a POST rest service which will be given an array of services as data. And which should in turn be calling them one by one to aggregate results on the server and send them back to the client.
#Path("/resource1")
#Path("/resource2")
#Path("/collection")
Post data to /collection
{["serviceName": "resource1", "data":"test1"], ["serviceName":"resource2","data":"test2"]}
The reason i need the resource1 and resource2 are, because those services can be called standalone also. I want to reuse the same setup if possible.
Is there any way to do this.
I am using jersey with spring.
Not sure what these resources have in common. If the post method has the same signature for all of them, you could have an abstract class or interface they implement defining the post method and can try using ResourceContext.matchResource to do this. E.g. something like this:
public abstract class AbstractResource {
public abstract String post(Object data);
}
#Path("/resource1")
public class Resource1 extends AbstractResource {
#POST
public String post(String data) {
// do something
}
}
#Path("/collection")
public class CollectionResource {
#Context
private ResourceContext rc;
#POST
#Consumes("application/json")
public String post(List<PostRequest> postRequests) {
StringBuilder result = new StringBuilder();
for (PostRequest pr : postRequests) {
// should wrap this in try-catch
AbstractResource ar = rc.matchResource(pr.resource,
AbstractResource.class);
sb.append(ar.post(pr.data));
}
return result.toString();
}
}
#XmlRootElement
public class PostRequest {
public String resource;
public String data;
}
Hopefully you got the idea and will be able to play with it and tweak it to fit your needs.