What is the correct REST Urlpattern for POST operation - asp.net-mvc

I have rest url support POST request. its looks like
api/country/{countryId}/state
using to create state resource in a country with given id
but the mapping function of this url is
public HttpResponseMessage Post(int countryId,StateDto state)
{
var country = _countryAppService.AddNewState(state, countryId);
var message = Request.CreateResponse(HttpStatusCode.Created, country);
return message;
}
and expected sample of the given urls is like
api/country/1/state (create a new state in country with id=1)
but here i am not using the url value (1) in the above function instead of here the caller need to pass the corresponding countryId via request body, ie there is no any guarantee to both contryId in url and post request are same. so my doubt is what is the right url pattern to save a state in particular country vai a post request?

If you have the same information in the resource path and the request body, it's duplication of information; the caller should never need to pass you the same information twice in the same request.
You should pick one as the authoritative source and ignore the other. Since you must have the correct resource address to perform the operation, I would suggest you need to take the value from there:
public HttpResponseMessage Post(int countryId,StateDto state)
{
// Compose the DTO from the route parameter.
state.CountryId = countryId;
var country = _countryAppService.AddNewState(state);
var message = Request.CreateResponse(HttpStatusCode.Created, country);
return message;
}

You can pass StateDto object in body also,it will go in body ,id can go in url
public HttpResponseMessage Post([FromUri]int countryId,[FromBody]StateDto state)
{
var country = _countryAppService.AddNewState(state, countryId);
var message = Request.CreateResponse(HttpStatusCode.Created, country);
return message;
}
Only one param can come from body , other have to come from uri, you can read more here:
http://www.asp.net/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api
Passing stateDto in uri is also an option but then you will have to pass all its members in querystring.

Related

Asp.net FromBody fails when payload is null

I have a controller that expects to get a json payload ie
public async Task<IActionResult> InitUser([FromBody] Tenant tenant)
This is fine when a valid json payload is sent, but if no payload is sent I get the error
No input formatter was found to support the content type 'null' for use with the [FromBody] attribute
And HTTP status code 415 is returned to the client.
Is it possible to catch this case and set the json payload to some default value so that the input formatter wont throw this error?
You can remove the [FromBody] attribute and get the body directly from the HTTP request. Make sure you have the [HttpPost] Attribute decoration.
In the example below you can see how to do that in a very simple way. You can also create your own CustomAttribute and middleware if you want to make it a system wide and elegant solution.
You will also need to parse the body. For that you can use JsonConverter if you like.
[HttpPost]
public async Task<IActionResult> Index()
{
Tenant tenant;
string result;
using (var reader = new StreamReader(Request.Body))
{
var body = await reader.ReadToEndAsync();
result = body;
}
//Define the naming strategy here if you need
DefaultContractResolver contractResolver = new DefaultContractResolver
{
//NamingStrategy = new SnakeCaseNamingStrategy()
NamingStrategy = new CamelCaseNamingStrategy()
};
//Optional configuration to add in DeserializeObject constructor as second param.
var jsonSerializerSettings = new JsonSerializerSettings
{
ContractResolver = contractResolver,
};
tenant = JsonConvert.DeserializeObject<Tenant>(result);
Console.WriteLine(tenant);
return View();
}

How to put parameters into post request with Jersey?

We want to sent a post request to an external API provider.
we know how to send a GET, and how to parse the JSON response.
We know how to send a JSON payload into the POST.
What we cant find an example of a good way to get request parameters into the body of a POST with jersey.
e.g. to send a really simple get request, we can do this:
private final Client theHttpClient;
ClientConfig clientConfig = new ClientConfig();
JacksonJaxbJsonProvider jacksonProvider = new JacksonJaxbJsonProvider();
jacksonProvider.setMapper(theObjectMapper);
clientConfig.register(jacksonProvider);
clientConfig.register(EncodingFilter.class);
clientConfig.register(GZipEncoder.class);
theHttpClient = ClientBuilder.newClient(clientConfig);
int param1 = 123134
String param2 = "this+is+a+test";
String url = "https://api.some.com?param1=" + param1 + "&param2=" + param2;
uri = new URI(url);
WebTarget webTarget = theHttpClient.target(uri);
Invocation.Builder invocationBuilder = webTarget.request(MediaType.APPLICATION_JSON);
RESPONSE response = invocationBuilder.get(responseClass);
This code will send the params via get.
How would we get the params into a post request body?
we see there is an invocationBuild.post which looks like this:
#Override
public <T> T post(final Entity<?> entity, final Class<T> responseType)
throws ProcessingException, WebApplicationException {
return method("POST", entity, responseType);
}
What is entity in this case? We assume we could manually create a text body with the params packed in it, but there must be a nicer way to do this, which will ensure no typos etc? E.g. something which takes a map, or even an addParam function?

How to return view along with model in Async task in Asp.Net MVC

I am testing asynch task in MVC and creating asynchronous task following code. When I return model value along with view name return View("Index", EmpResponse), I am getting error. but if I simply return view return view(). it is working well.
public class AsynchController : Controller
{
string Baseurl = "http://dummy.restapiexample.com/api/v1/";
// GET: Asynch
public async Task<ActionResult> Index()
{
using (var client = new HttpClient())
{
//Passing service base url
client.BaseAddress = new Uri(Baseurl);
client.DefaultRequestHeaders.Clear();
//Define request data format
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
//Sending request to find web api REST service resource GetAllEmployees using HttpClient
HttpResponseMessage Res = await client.GetAsync("employees");
var EmpResponse = "";
//Checking the response is successful or not which is sent using HttpClient
if (Res.IsSuccessStatusCode)
{
//Storing the response details recieved from web api
EmpResponse = Res.Content.ReadAsStringAsync().Result;
//Deserializing the response recieved from web api and storing into the Employee list
}
//returning the employee list to view
return View("Index", EmpResponse);
}
}
In simply ActionResult, I know we can return view name and model both together. is it issue with Async task?
Your intention is to pass EmpResponse as the view-model for your Index view, but since you have already deserialized EmpResponse as a string, it matches the wrong overload of the View() helper method (the one which accepts both viewName and masterName).
Try to pass it as an object to match the correct overload:
return View("Index", EmpResponse as object);
A better approach would be to store the received data as a strongly-typed collection of objects:
var empResponse = await Res.Content.ReadAsAsync<IEnumerable<Employee>>();
Then pass it as a view-model:
return View("Index", empResponse);
This isn't really an async issue, but a model type issue. (Though there is an async issue waiting to become a problem... Don't call .Result directly, but instead use await to get the result.)
Your model is a string. But the overload for View() which takes a second string uses it to find a named view. Which is why it's looking for a view called your long JSON string. (Well, a "master view" in this case since you're sending it two strings.)
Don't use a string as a model. Use a model. Instead of sending one big JSON string to your view, deserialize it into a model of some sort. The type is up to you, but the deserialization might look something like:
var response = await client.GetAsync("employees");
YourModelTypeHere model = null;
if (response.IsSuccessStatusCode)
{
var responseString = await result.Content.ReadAsStringAsync();
model = JsonConvert.DeserializeObject<YourModelTypeHere>(responseString);
}
return View(model);
There may even be an option in result to read/deserialize as your model directly, saving you a line of code above. But the overall principle is the same. Use strongly typed models instead of complex serialized strings.
*In this case, YourModelTypeHere looks like it would in fact be an IEnumerable<YourModel> or perhaps an IList<YourModel>, based on the serialized JSON we're seeing.
*Note also that this uses your current logic of sending an empty model to the same view if nothing was successfully retrieved. For an empty string that may be okay, for null it may become problematic depending on what your view is doing. (Either way your view is going to have to change if it currently expects a string as a model.) Perhaps redirect or return an error in the case of no available model? The logic of how your system should behave is up to you.

How Data is posted (POST) to Service in Servicestack , is it through URL?

I have complex requstDto which composed of other list of DTO's (Entity framework Entities) like
[Route("/demoservice/{Userdemo}/{EmployerDemoid}/{ReportDemo}/{DemoselectedDataList}/", "POST")]
public class ReportDemo : IReturn<String>
{
public List<selectedidList> selectedDataList{ get; set; }
}
where UserReport is follows
public class UserReport
{
public string UserName { get; set; }
public Datetime CreatedON{ get; set; }
}
when i try to post to request it gives me following error
A potentially dangerous Request.Path value was detected from the client (:)
i think it gives error due to : in CreatedON field ( for time part).
is the post values are also sent through URL to ServiceStack URL ? if yes
1) what if we have very large and complex requestDTO resulting into large number of characters (greater than allowed )in URL?
2) how to make above scenario work as ":" is reserved and cant be sent through URL?
3) How to see request URL Generated from client ?
My Client code in MVC.net is
var client = new JsonServiceClient(ConfigurationManager.AppSettings["applicationUrl"])
{
//for windows authentication
Credentials = CredentialCache.DefaultCredentials
};
var result = client.Post (new ReportDemo
{
UserName = model.UserName,
EmployerID = model.EmployerID,
Report = model.Report,
selectedDataList =userReportViewModel.selectedDataList
});
Thanks in advance,
Amol
Only the /path/info of the Url should be specified in the [Route]. Ideally routes should use a human-readable logically-structured Url that refers to a "Resource" (noun). See the SeviceStack REST Events Example for different examples.
Routes should also never include complex types and any variable that isn't on the [Route] is automatically sent in the HTTP Request Body for POST requests or the QueryString from GET Requests.
For a User Report like this I would choose a URL that identifies the report, if the report has a name like "Demo Report" I would use a path info like:
[Route("/reports/demo")]
public class ReportDemo : IReturn<String> { ... }
Otherwise if this is a Report for Users you may instead want to use something like:
[Route("/users/{UserName}/reports/demo")]
public class ReportDemo : IReturn<String> { ... }
You can check what url is used by using the Reverse Routing Extension methods, e.g:
var request = ReportDemo { UserName = "Foo", ... };
request.ToPostUrl().Print(); //= /users/Foo/reports/demo
Now you can send your Request with any property not in the Route getting POST'ed to the above url, e.g:
string result = client.Post (new ReportDemo {
UserName = userReportViewModel.UserName,
EmployerID = userReportViewModel.EmployerID,
Report = userReportViewModel.Report,
selectedDataList =userReportViewModel.selectedDataList
});
If your Report does return a string you can use IReturn<string> however if it returns a Response DTO you'll want to use that instead, e.g IReturn<ReportDemoResponse>.

How do I include a model with a RedirectToAction?

In the RedirectToAction below, I'd like to pass a viewmodel. How do I pass the model to the redirect?
I set a breakpoint to check the values of model to verify the model is created correctly. It is correct but the resulting view does not contain the values found in the model properties.
//
// model created up here...
//
return RedirectToAction("actionName", "controllerName", model);
ASP.NET MVC 4 RC
RedirectToAction returns a 302 response to the client browser and thus the browser will make a new GET request to the url in the location header value of the response came to the browser.
If you are trying to pass a simple lean-flat view model to the second action method, you can use this overload of the RedirectToAction method.
protected internal RedirectToRouteResult RedirectToAction(
string actionName,
string controllerName,
object routeValues
)
The RedirectToAction will convert the object passed(routeValues) to a query string and append that to the url(generated from the first 2 parameters we passed) and will embed the resulting url in the location header of the response.
Let's assume your view model is like this
public class StoreVm
{
public int StoreId { get; set; }
public string Name { get; set; }
public string Code { set; get; }
}
And you in your first action method, you can pass an object of this to the RedirectToAction method like this
var m = new Store { StoreId =101, Name = "Kroger", Code = "KRO"};
return RedirectToAction("Details","Store", m);
This code will send a 302 response to the browser with location header value as
Store/Details?StoreId=101&Name=Kroger&Code=KRO
Assuming your Details action method's parameter is of type StoreVm, the querystring param values will be properly mapped to the properties of the parameter.
public ActionResult Details(StoreVm model)
{
// model.Name & model.Id will have values mapped from the request querystring
// to do : Return something.
}
The above will work for passing small flat-lean view model. But if you want to pass a complex object, you should try to follow the PRG pattern.
PRG Pattern
PRG stands for POST - REDIRECT - GET. With this approach, you will issue a redirect response with a unique id in the querystring, using which the second GET action method can query the resource again and return something to the view.
int newStoreId=101;
return RedirectToAction("Details", "Store", new { storeId=newStoreId} );
This will create the url Store/Details?storeId=101
and in your Details GET action, using the storeId passed in, you will get/build the StoreVm object from somewhere (from a service or querying the database etc)
public ActionResult Details(string storeId)
{
// from the storeId value, get the entity/object/resource
var store = yourRepo.GetStore(storeId);
if(store!=null)
{
// Map the the view model
var storeVm = new StoreVm { Id=storeId, Name=store.Name,Code=store.Code};
return View(storeVm);
}
return View("StoreNotFound"); // view to render when we get invalid store id
}
TempData
Following the PRG pattern is a better solution to handle this use case. But if you don't want to do that and really want to pass some complex data across Stateless HTTP requests, you may use some temporary storage mechanism like TempData
TempData["NewCustomer"] = model;
return RedirectToAction("Index", "Users");
And read it in your GET Action method again.
public ActionResult Index()
{
var model=TempData["NewCustomer"] as Customer
return View(model);
}
TempData uses Session object behind the scene to store the data. But once the data is read the data is terminated.
Rachel has written a nice blog post explaining when to use TempData /ViewData. Worth to read.
Using TempData to pass model data to a redirect request in Asp.Net Core
In Asp.Net core, you cannot pass complex types in TempData. You can pass simple types like string, int, Guid etc.
If you absolutely want to pass a complex type object via TempData, you have 2 options.
1) Serialize your object to a string and pass that.
Here is a sample using Json.NET to serialize the object to a string
var s = Newtonsoft.Json.JsonConvert.SerializeObject(createUserVm);
TempData["newuser"] = s;
return RedirectToAction("Index", "Users");
Now in your Index action method, read this value from the TempData and deserialize it to your CreateUserViewModel class object.
public IActionResult Index()
{
if (TempData["newuser"] is string s)
{
var newUser = JsonConvert.DeserializeObject<CreateUserViewModel>(s);
// use newUser object now as needed
}
// to do : return something
}
2) Set a dictionary of simple types to TempData
var d = new Dictionary<string, string>
{
["FullName"] = rvm.FullName,
["Email"] = rvm.Email;
};
TempData["MyModelDict"] = d;
return RedirectToAction("Index", "Users");
and read it later
public IActionResult Index()
{
if (TempData["MyModelDict"] is Dictionary<string,string> dict)
{
var name = dict["Name"];
var email = dict["Email"];
}
// to do : return something
}
Another way to do it is to store it in the session.
var s = JsonConvert.SerializeObject(myView);
HttpContext.Session.SetString("myView", s);
and to get it back
string s = HttpContext.Session.GetString("myView");
myView = JsonConvert.DeserializeObject<MyView>(s);

Resources