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
I am implementing SessionAware in my action class. But, the sessionMap is always null. The setSession method doesn't seem to be called.
Here is the code. In the execute method, the sessionMap is always null.
What am I doing wrong?
Action class:
public class HelloWorldAction extends ActionSupport implements SessionAware
{
private static final long serialVersionUID = 544659976107736338L;
private Map<String, Object> sessionMap;
private String name;
public String execute() throws Exception {
if(sessionMap != null) {
sessionMap.put("TestID", "Test");
}
return "success";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public void setSession(Map<String, Object> arg0) {
this.sessionMap = arg0;
}
}
struts xml:
<?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>
<include file="struts-default.xml"/>
<package name="my-projects" namespace="/" extends="struts-default">
<action name="hello"
class="com.my.projects.actions.HelloWorldAction"
method="execute">
<result name="success">/jsp/HelloWorld.jsp</result>
</action>
You can try this to get Session Map
Map session = ActionContext.getContext().getSession();
and remove below line
<include file="struts-default.xml"/>
The struts-default.xml is the name of the default configuration file used by the Struts in it's core library. You should not use this name to your custom configurations.
A base configuration file named struts-default.xml is included in the struts2-core.jar file. This file is automatically included into struts.xml file to provide the standard configuration settings without having to copy them.
To exclude the struts-default.xml or to provide your own version, see the struts.configuration.files setting in struts.properties.
Remove this line from the configuration:
<include file="struts-default.xml"/>
This file can override the default settings such as the interceptor stack and your application won't work in the default mode.
By default it's configured to populate the session to the actions that implement SessionAware.
I'm trying to achieve authorization for my RestAPI, by letting Spring's Interceptor's (HandlerInterceptorAdapter) PreHandle method check wether the user is in the required role, before the scope hits the requested action in the controller. This requires, however, that I provide each action (URL Path) with the ID of the role it requires. This is my current setup:
public class AuthorizationInterceptor extends HandlerInterceptorAdapter{
#Autowired
IUserService us;
//before the actual handler will be executed
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler, Integer roleId)
throws Exception {
String userId = request.getHeader("UserId");
if(!us.isUserInRole(Long.parseLong(userId), roleId))
return false;
return true;
}
}
And (a part of) the servlet-context.xml:
<interceptors>
<interceptor>
<mapping path="/" />
<mapping path="/users/**" />
<beans:bean class="com.lumi.api.interceptors.AuthorizationInterceptor"></beans:bean>
</interceptor>
</interceptors>
My question is, wether I can pass in the parameter roleId with the bean in the servlet-context config. I can't seem to find anything in the docs. I think I once saw something like:
<mapping path="/" />
<parameter name="something" value="some value">
But i'm not sure.
You can simply set a property using a standard spring sintax, an example
<beans:bean class="com.lumi.api.interceptors.AuthorizationInterceptor">
<beans:property name="roleId" value="REGISTERED_USER"/>
</beans:bean>
your interceptor should of course include the property, so simply
public class AuthorizationInterceptor extends HandlerInterceptorAdapter{
private String roleId;
public String getRoleId() {
return roleId;
}
public void setRoleId(String roleId) {
this.roleId = roleId;
}
// The rest of your code
}
All my attempts to get a working example of both, pure restful configuration together with at least one non-restful URL failed.
I have this page as a guide:
https://cwiki.apache.org/confluence/display/WW/REST+Plugin
I have one model, Receipt with a few test fields.
The pure REST solution works as expected, so doing this:
<constant name="struts.enable.DynamicMethodInvocation" value="false"/>
<constant name="struts.devMode" value="true"/>
<constant name="struts.mapper.class" value="rest" />
<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"/>
works well with these results:
receipt.json => display the list of all the receipts
receipt/1.json => display the receipt with id = 1
But if I try to mix restful with non-restful URLs, (the same way I'm told to in the wiki article) like this:
<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"/>
it blows up with this:
Struts has detected an unhandled exception:
Messages:
com.immaculate.receipto.controllers.ReceiptController.execute()
File: java/lang/Class.java
Line number: 1,778
I am confused. Why is it searching for execute()?
execute() should be a method present on actions. I'm not giving it an action, I'm giving it a controller like before.
How do I fix this?
Here's the ReceiptController:
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(){
if(list==null){
return model;
} else {
return list;
}
}
public HttpHeaders create() {
receiptManager.save(model);
return new DefaultHttpHeaders("show");
}
public HttpHeaders show() {
model = receiptManager.find(id);
return new DefaultHttpHeaders("show");
}
public HttpHeaders destroy() {
model = receiptManager.destroy(id);
return new DefaultHttpHeaders("show");
}
public HttpHeaders index() {
list = receiptManager.list();
return new DefaultHttpHeaders("show");
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
Obviously I'm doing something wrong here. Do I need to know something else besides what's explained here?
Also, how do I read this? :
<constant name="struts.mapper.prefixMapping" value="/rest:rest,:struts"/>
Does this mean that everything starting with /rest will map to my pure restful controller that will, in turn map to my receipt and anything without the /rest prefix will map to the default struts mappings? or am I going entirely in the wrong direction here?
Prefix based action mapper delegates finding mapping to the corresponding mapper defined by the
<constant name="struts.mapper.prefixMapping" value="/rest:rest,:struts"/>
This means all URLs that have /rest in the URL before the last slash / are mapped to the rest mapper, others to the default mapper. If you have a receipt controller, then you should use the value "/receipt:rest,:struts" .
References:
REST Plugin
RestfulActionMapper
PrefixBasedActionMapper
I am also had the same error, i fix it by adding
<package name="api" namespace="/api" extends="rest-default">
<action name="users/*" class="com.aurum.struts.rest.UserController">
<param name="id">{1}</param>
</action>
</package>
complete example for REST and Non-REST action together is here https://github.com/sivailango/struts2-rest-nonrest
I am trying to pass a key value in query string from one page to another in a project using the Struts 2 framework.
URL Mapping:
<struts>
<package name="default" namespace="/" extends="struts-default">
<!-- Line -->
<action name="line" class="com.example.a.b.action.LineAction">
<result name="success">/line.jsp</result>
</action>
</package>
</struts>
URL For HTML Anchor Link:
http://localhost/line?key=value
When it goes to LineAction I want to get the query string key value part but could find anything online for it.
If I understand you correctly. In your action class create Getters and Setters for key. You will get a value in your action class.
May be You try this way..
try{
URL url = new URL("http://www.google.com?gender=male&name=jagga");
System.out.println(url.getQuery());
}catch(MalformedURLException e)
{
e.printStackTrace();
}
You build your URL of that Action
You can get parameters from the action context like
Map<String, Object> parameters = ActionContext.getContext().getParameters();
But a better way is to implement ParameterAware
private Map<String, String[]> parameters;
public void setParameters(Map<String, String[]> parameters){
this.parameters = parameters;
}
I have created a simple form in Extjs4.1.
I am sending the request to Struts2 framework, and using the Strtus2JSON plugin, i have to recieve the response. But unfortunately, the response is null.
Action class
public String execute()throws Exception{
Employee emp = new Employee();
emp.setAddress(getAddress());
emp.setDepartment(getDepartment());
emp.setName(getName());
emp.setSalary(getSalary());
AddEmployeeService empService = new AddEmployeeService();
boolean flag = empService.addEmployee(emp);
resultJSONobj = new JSONObject();
if(flag == true)
resultJSONobj.put("success","Inserted Successfully");
else
resultJSONobj.put("failure","An error occured");
System.out.println(resultJSONobj);
return SUCCESS;
}
struts.xml
<struts>
<package name="default" extends="struts-default, json-default">
<action name="AddEmp" class = "actions.AddEmpAction">
<result name = "success" type="json">
<param name ="root">resultJSONobj</param>
</result>
</action>
</package>
</struts>
Can anybody tell why iam getting the response as null ?
Do you have a Getter for resultJSONobj like this?:
public JSONObject getResultJSONobj(){
return this.resultJSONobj;
}
Add a namespace to your package, like this:
<package name="default" namespace="/" extends="struts-default, json-default">
And see if something has changed...
EDIT
As described in the guide,
'transient' fields are not serialized
fields without getter method are not serialized
Try like this:
Use a Map instead of your JSONObject. Not every class is accepted, and I'm not sure your JSONObject is serializable (do you implement serializable? do you declare a serialVersionUID? etc)
private Map resultJSONobj = new HashMap(); //instantiated at class level
// accessors
public Map getResultJSONobj() {
return resultJSONobj;
}
public void setResultJSONobj(Map resultJSONobj) {
this.resultJSONobj = resultJSONobj;
}
//...
public String execute()throws Exception{
Employee emp = new Employee();
emp.setAddress(getAddress());
emp.setDepartment(getDepartment());
emp.setName(getName());
emp.setSalary(getSalary());
AddEmployeeService empService = new AddEmployeeService();
boolean flag = empService.addEmployee(emp);
if(flag == true)
resultJSONobj.put("success","Inserted Successfully");
else
resultJSONobj.put("failure","An error occured");
System.out.println(resultJSONobj);
return SUCCESS;
}
And change struts config like this:
<struts>
<package name="default" extends="struts-default, json-default">
<action name="AddEmp" class = "actions.AddEmpAction">
<result name="success" type="json" />
</action>
</package>
</struts>
Success is not necessary, but leave it for a clearer code, you can always remove it later.
Now, you should see this as result (if you doesn't have any other getter):
{
"resultJSONobj": {
"success":"Inserted Successfully"
}
}
or
{
"resultJSONobj": {
"failure":"An error occured"
}
}
in your configuration file struts.xml, please use
<param name="includeProperties">resultJSONobj.*</param>
Instead of
<param name="includeProperties">resultJSONobj</param>
I was facing the same issue before in my project, after I made the above change it works.
There are few things missing in your struts.xml file. After result is success, where are you going?
Create json object instance outside your execute method and inside action class.
Struts2JSON plugin serializes all action variables inside an action class. It might be that since you have only declared JSONobject in action class and actually creating it inside execute, JSON might be getting it {}.
Put
private JSONObject resultJSONobj = new JSONObject();
inside action class and outside execute and see, it might work.
For more information you can go here
http://struts.apache.org/2.2.3/docs/json-plugin.html