struts2-rest-plugin - Pass Json object in POST method [duplicate] - rest

I'm using this as a reference to create a REST only configuration on Struts2:
https://cwiki.apache.org/confluence/display/WW/REST+Plugin
I have one model, Receipt with a few test fields: title, body.
Currently to create a receipt, I send a request in this way:
POST /receipt/?body=new_body&title=new_title
and it creates me a receipt with the new body and title passed in.
This doesn't work:
POST /receipt/
{
"body": "new_body",
"title": "new title"
}
Here's some code:
struts.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<bean type="org.apache.struts2.rest.handler.ContentTypeHandler" name="jackson" class="org.apache.struts2.rest.handler.JacksonLibHandler"/>
<constant name="struts.rest.handlerOverride.json" value="jackson"/>
<constant name="struts.enable.DynamicMethodInvocation" value="false"/>
<constant name="struts.devMode" value="true"/>
<constant name="struts.rest.content.restrictToGET" value="false"/>
<constant name="struts.rest.defaultExtension" value="json"/>
<constant name="struts.rest.handlerOverride.EXTENSION" value="json"/>
<constant name="struts.i18n.encoding" value="UTF-8"/>
<constant name="struts.action.extension" value="xhtml,,xml,json,action"/>
<constant name="struts.mapper.class" value="org.apache.struts2.dispatcher.mapper.PrefixBasedActionMapper" />
<constant name="struts.mapper.prefixMapping" value="/receipt:rest,:struts"/>
<constant name="struts.convention.action.suffix" value="Controller"/>
<constant name="struts.convention.action.mapAllMatches" value="true"/>
<constant name="struts.convention.default.parent.package" value="receipto"/>
<constant name="struts.convention.package.locators" value="controllers,actions"/>
</struts>
ReceiptController.java:
public class ReceiptController implements ModelDriven<Object> {
private ReceiptManager receiptManager = new ReceiptManager();
private String id;
private Receipt model = new Receipt();
private Collection list;
public Object getModel()
{
return (list==null ? model : list);
}
public HttpHeaders create()
{
receiptManager.save(model);
return new DefaultHttpHeaders("create");
}
public HttpHeaders show()
{
model = receiptManager.find(id);
return new DefaultHttpHeaders("show");
}
public HttpHeaders update()
{
receiptManager.save(model);
return new DefaultHttpHeaders("update");
}
public HttpHeaders destroy()
{
model = receiptManager.destroy(id);
return new DefaultHttpHeaders("destroy");
}
public HttpHeaders index()
{
list = receiptManager.list();
return new DefaultHttpHeaders("index").disableCaching();
}
public String getId()
{
return id;
}
public void setId(String id)
{
this.id = id;
}
}
Is it supposed to work as I want it to, or is it just how the plugin works?

I guess that postman is sending JSON in the body of the request and sets the content type application/json. Struts can parse the request if you add json interceptor to the stack.
<interceptor-stack name="myStack">
<interceptor-ref name="json"/>
<interceptor-ref name="myInterceptor"/>
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
The description for "json" interceptor in the JSON Plugin:
If the interceptor is used, the action will be populated from the JSON content in the request, these are the rules of the interceptor:
The "content-type" must be "application/json"
The JSON content must be well formed, see json.org for grammar.
Action must have a public "setter" method for fields that must be populated.
Supported types for population are: Primitives (int,long...String), Date, List, Map, Primitive Arrays, Other class (more on this later), and Array of Other class.
Any object in JSON, that is to be populated inside a list, or a map, will be of type Map (mapping from properties to values), any whole number will be of type Long, any decimal number will be of type Double, and any array of type List.
Resources:
Kickstart FAQ
Getting Started
FAQs
Other Resources

Related

Spring MVC, Serving RESTFul request (json) and JSP (content negotiation)

I'm trying to create a Spring MVC 4 web project (Maven project) in order to handle REST requests.
The system should answer with a web page in case the HTTP accept is text/html
and in json in case it is application/json (and possibly other formats like XML in case it is application/xml).
I've set up a controller and the JSP (I've also used the #RestController). The problem is that I cannot make it working togheter. If the system is correctly answer with a JSP, then the json service is not working and vice versa.
Do I have to set an handler for each representation, and how?
Thank you in advance.
To determining what format the user has requested relies on a ContentNegotationStrategy,there are default implementations available out of the box,but you can also implement your own if you wish
To configure and use content negotiation with Spring using HTTP message converters :
You have to enable Content Negotiation in Spring MVC :
<bean id="cnManager"
class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="favorPathExtension" value="true"/>
<property name="ignoreAcceptHeader" value="true" />
<property name="defaultContentType" value="application/json" />
<property name="useJaf" value="false"/>
<property name="mediaTypes">
<map>
<entry key="html" value="text/html" />
<entry key="json" value="application/json" />
<entry key="xml" value="application/xml" />
</map>
</property>
</bean>
Or using java configuration :
#Configuration
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void configureContentNegotiation(
ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(true).
ignoreAcceptHeader(true).
useJaf(false).
defaultContentType(MediaType.TEXT_HTML).
mediaType("html", MediaType.TEXT_HTML).
mediaType("xml", MediaType.APPLICATION_XML).
mediaType("json", MediaType.APPLICATION_JSON);
}
}
This is an exemple :
the first method will return client.jsp page for each get for url end .../client and the second will return a client json object for each get end .../clients/id
#RequestMapping(value = "/client", method = RequestMethod.GET)
public ModelAndView getClientPage(final HttpServletRequest request,
final Model model,final HttpSession session) {
// traitement
return new ModelAndView("client");
}
#RequestMapping(value = "/clients/{id}", method = RequestMethod.GET)
public #ResponseBody Client getClient(#pathVariable("id") long id) {
Client client = YourService.getClient(id);
return client;
}
Contentnegotiatingviewresolver java config :
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "com.yourbasepackage")
public class AppConfig extends WebMvcConfigurerAdapter {
#Bean
public ViewResolver jspViewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}

Struts2 rest request asking for execute method

I am new to struts. I am writing a web application using struts2 rest. I have struts2 configured to accept both rest and non rest url. ( following http://struts.apache.org/release/2.3.x/docs/rest-plugin.html)
I expect "http://mylocalhost.com/myApp/detail" url to call DetailController class's index() method. However, I got this error :
java.lang.NoSuchMethodException: com.project.struts.rest.controllers.DetailController.execute()
java.lang.Class.getMethod(Class.java:1665)
It seems struts worked out it need to call detailController, but it tries to call the execute() method instead of the index() method.
Have I missed something in my struts config?
struts.xml configure file:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.devMode" value="true" />
<constant name="struts.action.extension" value="xhtml,,xml,json,action"/>
<constant name="struts.mapper.class" value="org.apache.struts2.dispatcher.mapper.PrefixBasedActionMapper" />
<constant name="struts.mapper.prefixMapping" value="/rest:rest,:struts"/>
<constant name="struts.convention.action.suffix" value="Controller"/>
<constant name="struts.convention.action.mapAllMatches" value="true"/>
<constant name="struts.convention.default.parent.package" value="rest-default"/>
<constant name="struts.convention.package.locators" value="controllers"/>
</struts>
I have a controller called DetailController in com.project.struts.rest.controllers
DetailController class:
package com.project.struts.rest.controllers;
import org.apache.struts2.rest.DefaultHttpHeaders;
import org.apache.struts2.rest.HttpHeaders;
import com.opensymphony.xwork2.ModelDriven;
public class DetailController implements ModelDriven<Object> {
private String propertyId;
#Override
public Object getModel() {
// TODO Auto-generated method stub
return null;
}
// GET /test/1
public HttpHeaders show() {
return new DefaultHttpHeaders("show");
}
// GET /test
public HttpHeaders index() {
return new DefaultHttpHeaders("index");
}
}

Camel best practices for RESTfull web service exposing

I want to expose a RESTfull webservice using camel. I have used URI templates to define my service contracts. I want to know how should I route the requests to the relevant method of my ServiceProcessor based on the URI template.
As an example, take the following two operations:
#GET
#Path("/customers/{customerId}/")
public Customer loadCustomer(#PathParam("customerId") final String customerId){
return null;
}
#GET
#Path("/customers/{customerId}/accounts/")
List<Account> loadAccountsForCustomer(#PathParam("customerId") final String customerId){
return null;
}
Following is the route I have used:
<osgi:camelContext xmlns="http://camel.apache.org/schema/spring">
<route trace="true" id="PaymentService">
<from uri="`enter code here`cxfrs://bean://customerCareServer" />
<process ref="customerCareProcessor" />
</route>
</osgi:camelContext>
Is there any way that I can match the uri header(Exchange.HTTP_PATH) to an existing template uri in my service definition?
Following is the Service Contract for my service:
#Path("/customercare/")
public class CustomerCareService {
#POST
#Path("/customers/")
public void registerCustomer(final Customer customer){
}
#POST
#Path("/customers/{customerId}/accounts/")
public void registerAccount(#PathParam("customerId") final String customerId, final Account account){
}
#PUT
#Path("/customers/")
public void updateCustomer(final Customer customer){
}
#PUT
#Path("/accounts/")
public void updateAccount(final Account account){
}
#GET
#Path("/customers/{customerId}/")
public Customer loadCustomer(#PathParam("customerId") final String customerId){
return null;
}
#GET
#Path("/customers/{customerId}/accounts/")
List<Account> loadAccountsForCustomer(#PathParam("customerId") final String customerId){
return null;
}
#GET
#Path("/accounts/{accountNumber}")
Account loadAccount(#PathParam("accountNumber") final String accountNumber){
return null;
}
}
The cxfrs endpoint consumer provides a special exchange header operationName that contains a service method name (registerCustomer, registerAccount, etc).
You could decide what to do with a request using this header like following:
<from uri="cxfrs://bean://customerCareServer" />
<choice>
<when>
<simple>${header.operationName} == 'registerCustomer'</simple>
<!-- registerCustomer request processing -->
</when>
<when>
<simple>${header.operationName} == 'registerAccount'</simple>
<!-- registerAccount request processing -->
</when>
....
</choice>

Use Dispatcher servlet for interceptor call only for selective URL

I am having a controller class with two types of methods. first type must execute its body as soon as URL is hit suppose "http://localhost:8080/SpringAOP1/welcome/two/aa". Here SpringAOP1 is my project,welcome is the controller class name(Which is set with annotation) and two is the function name with parameter "aa".
The Second type of function needs to be pre-handled by a interceptor.So when URL is hit suppose "http://localhost:8080/SpringAOP1/welcome/intercep/six/aa" the interceptor must be called. Here i am passing the "intercep" parameter with all those functions of second type which needs interceptor call.
Controller class:-
#Controlle
#RequestMapping("/welcome")
public class HelloController {
#RequestMapping(method = RequestMethod.GET)
public String printWelcome1(ModelMap model) {
model.addAttribute("message", "Spring 3 MVC Hello World 1");
return "hello";
}
#RequestMapping( value="two/{name}", method = RequestMethod.GET)
public String printWelcome2(#PathVariable String name,ModelMap model) {
model.addAttribute("message", "Spring 3 MVC Hello World 2");
model.addAttribute("value", name);
return "hello";
}
#RequestMapping( value="intercep/six/{name}", method = RequestMethod.GET)
public String printWelcome6(#PathVariable String name,ModelMap model) {
model.addAttribute("message", "Spring 3 MVC Hello World 6");
model.addAttribute("value", name);
return "hello";
}
`
Dispatcher servlet
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="com.mkyong.common.controller" />
<bean name="HijackBeforeMethod" class="com.mkyong.common.controller.HijackBeforeMethod"/>
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/intercep/*"/>
<bean id="HijackBeforeMethod" class="com.mkyong.common.controller.HijackBeforeMethod" />
</mvc:interceptor>
</mvc:interceptors>
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="order" value="0" />
<property name="prefix">
<value>/WEB-INF/pages/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
HijackBeforeMethod
(This is the interceptor Class which is called)
public class HijackBeforeMethod extends HandlerInterceptorAdapter {
public Boolean comp(String u,String p)
{
Map mMap = new HashMap();
mMap.put("aa", "bb");
mMap.put("mm", "nn");
mMap.put("xx", "yy");
Iterator iter = mMap.entrySet().iterator();
String k,v;
Boolean flg=false;
while (iter.hasNext())
{
Map.Entry mEntry = (Map.Entry) iter.next();
if(mEntry.getKey().equals(u) && mEntry.getValue().equals(p))
flg=true;
}
return flg;
}
public boolean preHandle(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse, Object o) throws Exception {
String u="aa";
String p="bb";
//System.out.println(" The username is--> "+u+" and pass is --> "+p);
Boolean ch=comp(u,p);
if(ch)
{
if(httpServletRequest.getMethod().equalsIgnoreCase("get")){
String uri = httpServletRequest.getRequestURI();
System.out.println("The method is GET :: "+ uri);
}
else
{
return false;
}
}
}
Please help me finding this. I am in greate trouble..
You can use the WebRequestInterceptor interface instead of HandlerInterceptorAdapter.
The following code segment is working fine for me.
public class FlashInterceptor implements WebRequestInterceptor {
private Flash flash;
#Autowired
public void setFlash(Flash flash) {
this.flash = flash;
}
#Override
public void preHandle(WebRequest request) throws Exception {
final Map<String, Object> messages = flash.getMessages();
if (messages.containsKey("flash_info")) {
request.setAttribute("flash_info", messages.get("flash_info"),
RequestAttributes.SCOPE_REQUEST);
request.setAttribute("flash_info_params",
messages.get("flash_info_params"),
RequestAttributes.SCOPE_REQUEST);
}
if (messages.containsKey("flash_error")) {
request.setAttribute("flash_error", messages.get("flash_error"),
RequestAttributes.SCOPE_REQUEST);
request.setAttribute("flash_error_params",
messages.get("flash_error_params"),
RequestAttributes.SCOPE_REQUEST);
}
if (messages.containsKey("flash_success")) {
request.setAttribute("flash_success",
messages.get("flash_success"),
RequestAttributes.SCOPE_REQUEST);
request.setAttribute("flash_success_params",
messages.get("flash_success_params"),
RequestAttributes.SCOPE_REQUEST);
}
flash.reset();
}
#Override
public void postHandle(WebRequest request, ModelMap model) throws Exception {
}
#Override
public void afterCompletion(WebRequest request, Exception ex)
throws Exception {
}
}

REST API with Struts

I'm trying to add a REST API to an existing struts 2 application.
The idea is to have part of the application using standard struts mapping, and another part using REST.
So I used the struts2-rest-plugin plugin, and added the following configuration:
struts.xml:
<constant name="rest" value="org.apache.struts2.rest.RestActionMapper"/>
<constant name="struts.mapper.class"
value="org.apache.struts2.dispatcher.mapper.PrefixBasedActionMapper"/>
<constant name="struts.mapper.prefixMapping" value="/rest:rest,/:struts"/>
struts.properties:
struts.action.extension=,htm,action,xml,json
TasksController.java:
package xxx.common.webservice.rest;
public class TasksController implements ModelDriven<Task> {
public String update() {
return "UPDATE";
}
// Handles /tasks/{id} GET requests
public String show() {
return "YES";
}
#Override
public Task getModel() {
// TODO Auto-generated method stub
return null;
}
}
With this configuration, the basic struts action work, but I can't get the REST actions to work.
I also tried different struts.xml configurations (including the convention plugin options), but without any success, the mappings are never shown with the config-brower plugin.
Any idea of what I have missed or done wrong?
It finally worked, but it was a while ago and I don't remember exactly what I did, here is my configuration, hope this helps.
struts.xml
<constant name="struts.convention.action.mapAllMatches" value="true"/>
<constant name="struts.convention.package.locators" value="webservice"/>
<constant name="struts.convention.action.suffix" value="Controller"/>
<constant name="struts.convention.default.parent.package" value="rest-default"/>
<constant name="struts.mapper.class" value="org.apache.struts2.dispatcher.mapper.PrefixBasedActionMapper" />
<constant name="struts.mapper.prefixMapping" value="/rest:rest,:struts" />
<package name="home" namespace="/" extends="struts-default">
...
</package>
TaskController.java
package com.test.webservice.rest;
public class TaskController extends RestActionSupport implements
ModelDriven<TaskDTO> {
public final HttpHeaderResult show() {
...
}
...
}