optional POST parameter in spring MVC? - spring-mvc

I have the following code:
#RequestMapping(method = RequestMethod.POST)
public ModelAndView editItem(String name, String description)
However, sometime description is not passed in (this is a simplified example than the real one), and i would like to make description optional, perhaps by filling in a default value if none is passed in.
Anyone have any idea how to do that?
thanks a lot!
Jason

If you are using Spring MVC 3.0 or higher then just set defaultValue parameter of #RequestParam:
public ModelAndView editItem(#RequestParam(value = "description", defaultValue = "new value") String description)
In Spring MVC 2.5, I suggest to mark value as required = false and check their value against null manually:
public ModelAndView editItem(#RequestParam(value = "description", required = false) String description) {
if (description == null) {
description = "new value";
}
...
}
See also corresponding documentation about #RequestParam annotation.
UPDATE for JDK 8 & Spring 4.1+: now you could use java.util.Optional like this:
public ModelAndView editItem(#RequestParam("description") Optional<String> description) {
item.setDescription(description.getOrElse("default value"));
// or only if it's present:
description.ifPresent(value -> item.setDescription(description));
...
}

Instead of using #RequestParam for the optional parameters, take a parameter of type org.springframework.web.context.request.WebRequest. For example,
#RequestMapping(method = RequestMethod.POST)
public ModelAndView editItem(
#RequestParam("name")String name,
org.springframework.web.context.request.WebRequest webRequest)
{
String description = webRequest.getParameter("description");
if (description != null)
{
// optional parameter is present
}
else
{
// optional parameter is not there.
}
}
Note: See below (defaultValue and required) for a way to solve this without using a WebRequest parameter.

Related

Spring MVC url parameters format

In my web application, I am trying to make an URL accesible with this format:
http://host/admin/users/edit/{id}
http://host/admin/users/edit/20 -> 20 represents the id param
I am trying with #RequestMapping like this
#Override
#RequestMapping(path = { "/admin/users/edit/{id}" }, method = RequestMethod.GET, params = { "id" })
public ModelAndView editUserInfo(#RequestParam(value = "id", required = true) Long id) {
...
}
I don't want use the url?param=value format if it's possible without config friendly urls.
Thanks in advance.
In order to do what you want yo have to write the following:
#Override
#RequestMapping(path = { "/admin/users/edit/{id}" }, method = RequestMethod.GET)
public ModelAndView editUserInfo(#PathVariable("id") Long id) {
...
}
PathVariable is for parameter variables, RequestVariable is for request params
Angelo

Query string parameter vs regular parameter ASP.Net MVC 5

I have been working on desktop applications mostly and thought to learn web development using ASP.Net MVC5 and thus going through the book by Jon Galloway. So I was reading about how you can pass the parameters to action methods using query string like
/Store/Browse?genre=Disco
or directly embed them in the url like
/Store/Details/5
Now the controller code that I wrote (taken from book) is below :
namespace MvcMusicStore.Controllers
{
public class StoreController : Controller
{
// GET: Store
public string Index()
{
return "Hello from Store.Index()";
}
public string Browse(string genre)
{
string message = HttpUtility.HtmlEncode("Store.Browser, Genre = " + genre);
return message;
}
public string Details(int id)
{
string message = "Store.Details, ID = " + id;
return message;
}
}
}
The url opens fine and the actions return the message as expected. But just to try I tried to pass the genre value by embedding it in the url like
/Store/Browse/Rap
but that doesn't work like it did for the Details() action. I thought it may have to do something with the datatype of genre, so I tried changing the data type of id in Details() to string as below :
public string Details(string id)
{
string message = "Store.Details, ID = " + id;
return message;
}
}
and opened the url
/Store/Details/5
and the Details() action returns message with id value 5, but when i do the same for Browse() action
/Store/Browse/Rap
the action doesn't return the message with genre value "Rap". I tried to pass the genre value and removed the html encoding to see if that had anything to do with it, but it didn't.
I looked at the post here but that didn't help either. Any comments appreciated.
Your using the Default route which is defined as
url: "{controller}/{action}/{id}",
and expects a value for id. When you use /Store/Browse/Rap, then the value of the 3rd segment ("Rap") will be bound to a paramater named id, but your Browse() method does not contain one (its named genre).
Either change the name of the parameter so its
public string Browse(string id)
and the value of id will be "Rap",
Or create a specific route definition and place it before the Default route (and keep the Browse() method as is)
routes.MapRoute(
name: "Browse",
url: "Store/Browse/{genre}",
defaults: new { controller = "Store", action = "Browse", genre = UrlParameter.Optional }
);
... // default route here
Side note: You do not need to change the type of the parameter in the Details method if your always passing a value that is a valid int

How to Store A Json Array or Json Object in mysql using Spring And Mybatis

Controller.java
#RequestMapping(value = { ControllerUriConstant.add }, method = RequestMethod.POST)
#ActivityMapping(activity = ActivityEnum.ADD)
public String addActivities(#ModelAttribute("activityForm") ActivityForm activityForm, BindingResult bindingResult, Model model,
HttpServletRequest request) throws Exception {
new ActivityFormValidator().validate(activityForm, bindingResult, request.getLocale(), getMessageSource(), null, activityService);
if (bindingResult.hasErrors()) {
model.addAttribute(Constants.ACTIVITY_FORM,activityForm );
model.addAttribute(Constants.HAS_ERROR, Boolean.TRUE);
model.addAttribute("add", true);
model.addAttribute(Constants.ERROR_MESSAGE, getMessageSource().getMessage("ulearn.messages.add.failed", activityForm.getName()));
return "activityform";
} else {
model.addAttribute(Constants.SUCCESS_MESSAGE, getMessageSource().getMessage("ulearn.messages.add.success", activityForm.getName()));
JSONArray address =new JSONArray();
JSONObject jo = new JSONObject();
jo.put("id", "1");
jo.put("name","Test");
JSONObject jo1= new JSONObject();
jo1.put("id", "1");
jo1.put("name", "Test2");
JSONObject jo2= new JSONObject();
jo2.put("id", "1");
jo2.put("name", "Test3");
address.add(jo);
address.add(jo1);
address.add(jo2);
activityForm.setInsertJsonMysql(address);
activityService.add(activityForm);
}
return getActivities( model, request);
}
Service.java
public void add(ActivityForm activityForm) throws TechnoShineException {
try {
Activity activity = new Activity();
activity.setName(activityForm.getName());
activity.setActive(activityForm.getActive());
activity.setInsertJsonMysql(activityForm.getInsertJsonMysql());
activityDAO.getMapper().insert(activity);
logAudit(getAuditableList(activity, null), getMessageSource().getMessage("ulearn.messages.add.success", activity.getName()), 0, ActivityEnum.ADD);
} catch (Exception e) {
throw new TechnoShineException(e, ActivityService.class);
}
}
Mapper.xml
<insert id="insert" parameterType="com.technoshinelabs.ulearn.persistance.bean.controlpanel.Activity">
INSERT INTO activity (activity_name,is_active,json_array)
VALUES (#{name},#{active},#{insertJsonMysql})
</insert>
// Finally got this error error Caused by: java.lang.IllegalStateException: Type handler was null on parameter mapping for property 'insertJsonMysql'. It was either not specified and/or could not be found for the javaType / jdbcType combination specified.
[1]: https://i.stack.imgur.com/OeZeR.png
I suppose what you want to store is the actual JSON and the MySQL table column json_array is of type VARCHAR or TEXT.
JSONArray is actually but an utility class used to build the JSON.
Then you shall change the type of property insertJsonMysql of class Activity to String and set it to the String representation of the JSONArray.
activity.setInsertJsonMysql(activityForm.getInsertJsonMysql().toString());
EDIT: about inserting into enum column.
According to MySQL 5.7 Reference Manual, I would say you insert into enum typed column the same way you insert any String, the DB will check provided value against list of values declared in CREATE TABLE.
But according to the code sample you provide, the value for insertJsonMysql will be:
[{"id":"1","name":"Test1"},{"id":"1","name":"Test2"},{"id":"1","name":"Test3"}]
that means one of the enum values would have to be this exact string.
However, I feel like that's not what you expect. Maybe using a SET typed column is a closer answer.

what i need to write in else part in the following code?

#RequestMapping("/insert")
public String insertEmpDetails(#ModelAttribute("employee") Employee emp) {
if (emp != null)
empService.insertEmpDetails(emp);
return "redirect:/getList";
}
Please tell me what to write in else part.I was trying for that but i am not getting
Make employeeId as a required param to serve the request by this RequestMapping then you don't need to write else part because Employee model will never be null in that case. It must have at least employeeId.
Note: Make it POST request.
Example:
#RequestMapping(value = "/insert", params = "employeeId", method = RequestMethod.POST)
public String insertEmpDetails(#ModelAttribute("employee") Employee emp) {
empService.insertEmpDetails(emp);
return "redirect:/getList";
}
In other way you can validate the model and redirect to form page back with validation error messages.
Sample:
#RequestMapping(value = "/insert", params = "employeeId", method = RequestMethod.POST)
public String insertEmpDetails(#Valid #ModelAttribute("employee") Employee emp, BindingResult result, ModelMap model) {
if (result.hasErrors()){
model.addAttribute("error", "Your custom error messages");
return "<<back to form page without redirection>>";
}else {
empService.insertEmpDetails(emp);
return "redirect:/getList";
}
}
Read more about Spring - Validation, Data Binding, and Type Conversion
Read a post on Spring MVC : How to perform validation ?

How to get POST data in WebAPI?

I'm sending a request to server in the following form:
http://localhost:12345/api/controller/par1/par2
The request is correctly resolved to a method like:
[HttpPost]
public void object Post(string par1, string par2)
However, I pass additional data through the request content. How can I retrieve these data?
For the sake of example, let's say, that the request is sent from the form:
<form action="http://localhost:12345/api/controller/par1/par2" method="post">
<input type="hidden" name="data" value="value" />
<input type="submit" name="submit" value="Submit" />
</form>
From answer in this question:
How to get Json Post Values with asp.net webapi
Autoparse using parameter binding; note that the dynamic is made up of JToken, hence the .Value accessor.
public void Post([FromBody]dynamic value) {
var x = value.var1.Value; // JToken
}
Read just like Request.RequestUri.ParseQueryString()[key]
public async Task Post() {
dynamic obj = await Request.Content.ReadAsAsync<JObject>();
var y = obj.var1;
}
Same as #2, just not asynchronously (?) so you can use it in a helper method
private T GetPostParam<T>(string key) {
var p = Request.Content.ReadAsAsync<JObject>();
return (T)Convert.ChangeType(p.Result[key], typeof(T)); // example conversion, could be null...
}
Caveat -- expects media-type application/json in order to trigger JsonMediaTypeFormatter handling.
After spending a good bit of time today trying to wrap my brain around the (significant but powerful) paradigm shift between old ways of processing web form data and how it is done with WebAPI, I thought I'd add my 2 cents to this discussion.
What I wanted to do (which is pretty common for web form processing of a POST) is to be able to grab any of the form values I want, in any order. Say like you can do if you have your data in a System.Collections.Specialized.NameValueCollection. But turns out, in WebAPI, the data from a POST comes back at you as a stream. So you can't directly do that.
But there is a cool little class named FormDataCollection (in System.Net.Http.Formatting) and what it will let you do is iterate through your collection once.
So I wrote a simple utility method that will run through the FormDataCollection once and stick all the values into a NameValueCollection. Once this is done, you can jump all around the data to your hearts content.
So in my ApiController derived class, I have a post method like this:
public void Post(FormDataCollection formData)
{
NameValueCollection valueMap = WebAPIUtils.Convert(formData);
... my code that uses the data in the NameValueCollection
}
The Convert method in my static WebAPIUtils class looks like this:
/// <summary>
/// Copy the values contained in the given FormDataCollection into
/// a NameValueCollection instance.
/// </summary>
/// <param name="formDataCollection">The FormDataCollection instance. (required, but can be empty)</param>
/// <returns>The NameValueCollection. Never returned null, but may be empty.</returns>
public static NameValueCollection Convert(FormDataCollection formDataCollection)
{
Validate.IsNotNull("formDataCollection", formDataCollection);
IEnumerator<KeyValuePair<string, string>> pairs = formDataCollection.GetEnumerator();
NameValueCollection collection = new NameValueCollection();
while (pairs.MoveNext())
{
KeyValuePair<string, string> pair = pairs.Current;
collection.Add(pair.Key, pair.Value);
}
return collection;
}
Hope this helps!
I had a problem with sending a request with multiple parameters.
I've solved it by sending a class, with the old parameters as properties.
<form action="http://localhost:12345/api/controller/method" method="post">
<input type="hidden" name="name1" value="value1" />
<input type="hidden" name="name2" value="value2" />
<input type="submit" name="submit" value="Submit" />
</form>
Model class:
public class Model {
public string Name1 { get; set; }
public string Name2 { get; set; }
}
Controller:
public void method(Model m) {
string name = m.Name1;
}
It is hard to handle multiple parameters on the action directly. The better way to do it is to create a view model class. Then you have a single parameter but the parameter contains multiple data properties.
public class MyParameters
{
public string a { get; set; }
public string b { get; set; }
}
public MyController : ApiController
{
public HttpResponseMessage Get([FromUri] MyParameters parameters) { ... }
}
Then you go to:
http://localhost:12345/api/MyController?a=par1&b=par2
Reference: http://www.asp.net/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api
If you want to use "/par1/par2", you can register an asp routing rule. eg routeTemplate: "API/{controller}/{action}/{a}/{b}".
See http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api
Try this.
public string Post(FormDataCollection form) {
string par1 = form.Get("par1");
// ...
}
It works for me with webapi 2
None of the answers here worked for me. Using FormDataCollection in the post method seems like the right answer but something about my post request was causing webapi to choke. eventually I made it work by including no parameters in the method call and just manually parsing out the form parameters like this.
public HttpResponseMessage FileUpload() {
System.Web.HttpRequest httpRequest = System.Web.HttpContext.Current.Request;
System.Collections.Specialized.NameValueCollection formData = httpRequest.Form;
int ID = Convert.ToInt32(formData["ID"]);
etc
I found for my use case this was much more useful, hopefully it helps someone else that spent time on this answer applying it
public IDictionary<string, object> GetBodyPropsList()
{
var contentType = Request.Content.Headers.ContentType.MediaType;
var requestParams = Request.Content.ReadAsStringAsync().Result;
if (contentType == "application/json")
{
return Newtonsoft.Json.JsonConvert.DeserializeObject<IDictionary<string, object>>(requestParams);
}
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
Is there a way to handle form post data in a Web Api controller?
The normal approach in ASP.NET Web API is to represent the form as a model so the media type formatter deserializes it. Alternative is to define the actions's parameter as NameValueCollection:
public void Post(NameValueCollection formData)
{
var value = formData["key"];
}
ON WEB API.
[HttpGet]
[Route("api/Get_EXCUTA_PROCEDURE_IBESVNDACMVDD")]
public IHttpActionResult Get(int CodigoPuxada...)
{
string retornoErro = string.Empty;
try
{
//int codigoPuxada = entrada.CodigoPuxada;
SetKeyAtual(CodigoPuxada);
var repo = new ItemBroker_Dim_Canal_BookRepositorio(ConnectionString);
try
{
var dadosRetorno = repo.ExcuteProcedure_Busca_vbc_(CodigoPuxada,...); // method return object (dataset)
return Ok(dadosRetorno);
}
catch
{
throw;
}
}
catch (Exception ex)
{
retornoErro = ex.Message;
if (ex.InnerException != null)
retornoErro = ex.InnerException.ToString();
}
return Ok(retornoErro);
}
Other projet invoke web api...
(USING RESTSHARP DLL)
RestClient clientHttpPost1 = null;
string dadosVbc123 = string.empty;
clientHttpPost1 = new RestSharp.RestClient($"{urlWebApiAvante}Get_EXCUTA_PROCEDURE_IBESVNDACMVDD?CodigoPuxada=...");
RestSharp.RestRequest request2 = new RestSharp.RestRequest(RestSharp.Method.GET);
request2.RequestFormat = RestSharp.DataFormat.Json;
request2.AddHeader("Content-Type", "application/json;charset=utf-8");
string strAux1 = string.Empty;
request2.Timeout = 180000;
RestSharp.IRestResponse response = clientHttpPost1.Execute(request2);
if ((response != null) && response.StatusCode == System.Net.HttpStatusCode.OK)
{
try
{
var dataObjects = response.Content.ToString().Trim();
dadosVbc123 = dataObjects.ToString().Replace("\t", "");
if (dadosVbc123.Trim() == "{\"IBESVNDACMVDD\":[]}")
dadosVbc123 = string.Empty;
}
...
}
// converting JSON to dataset
string val1 = dadosVbc123.Replace("{\"IBESVNDACMVDD\":", "").Replace("}]}", "}]");
DataTable dtVBC123 = (DataTable)Newtonsoft.Json.JsonConvert.DeserializeObject(val1, (typeof(DataTable)));

Resources